basctl/inc/pch/precompiled_basctl.hxx | 1 basegfx/Library_basegfx.mk | 1 basegfx/source/tools/bgradient.cxx | 774 ++++++++++++++ basegfx/source/tools/gradienttools.cxx | 602 ---------- chart2/inc/pch/precompiled_chartcontroller.hxx | 1 chart2/qa/extras/chart2import.cxx | 8 chart2/source/controller/main/ChartController_Tools.cxx | 11 cui/source/inc/cuitabarea.hxx | 8 cui/source/tabpages/tpgradnt.cxx | 41 cui/source/tabpages/tptrans.cxx | 18 drawinglayer/inc/texture/texture.hxx | 21 drawinglayer/qa/unit/vclpixelprocessor2d.cxx | 61 - drawinglayer/source/attribute/fillgradientattribute.cxx | 26 drawinglayer/source/primitive3d/textureprimitive3d.cxx | 6 drawinglayer/source/processor3d/defaultprocessor3d.cxx | 6 drawinglayer/source/texture/texture.cxx | 146 +- drawinglayer/source/tools/wmfemfhelper.cxx | 20 filter/source/msfilter/msdffimp.cxx | 8 filter/source/msfilter/svdfppt.cxx | 22 include/basegfx/utils/bgradient.hxx | 329 +++++ include/basegfx/utils/gradienttools.hxx | 249 ---- include/drawinglayer/attribute/fillgradientattribute.hxx | 15 include/oox/export/drawingml.hxx | 9 include/svx/sidebar/AreaPropertyPanelBase.hxx | 22 include/svx/sidebar/AreaTransparencyGradientPopup.hxx | 2 include/svx/xflftrit.hxx | 4 include/svx/xflgrit.hxx | 13 include/svx/xgrad.hxx | 85 - include/svx/xtable.hxx | 8 oox/qa/unit/drawingml.cxx | 3 oox/source/drawingml/fillproperties.cxx | 22 oox/source/export/chartexport.cxx | 48 oox/source/export/drawingml.cxx | 169 +-- reportdesign/Library_rptui.mk | 1 reportdesign/inc/pch/precompiled_rptui.hxx | 1 reportdesign/source/ui/misc/UITools.cxx | 4 reportdesign/source/ui/report/ReportController.cxx | 4 sc/inc/pch/precompiled_sc.hxx | 1 sc/qa/unit/bugfix-test.cxx | 4 sc/source/ui/drawfunc/drawsh.cxx | 2 sd/inc/pch/precompiled_sdui.hxx | 1 sd/qa/unit/export-tests-ooxml1.cxx | 6 sd/qa/unit/export-tests-ooxml2.cxx | 3 sd/qa/unit/export-tests-ooxml3.cxx | 6 sd/qa/unit/misc-tests.cxx | 3 sd/qa/unit/uiimpress.cxx | 4 sd/source/core/drawdoc4.cxx | 18 sd/source/ui/sidebar/SlideBackground.cxx | 21 sd/source/ui/sidebar/SlideBackground.hxx | 7 sd/source/ui/view/drviews2.cxx | 2 sd/source/ui/view/drviews7.cxx | 2 sd/source/ui/view/drviews9.cxx | 21 solenv/clang-format/excludelist | 1 svx/inc/pch/precompiled_svx.hxx | 1 svx/source/customshapes/EnhancedCustomShape2d.cxx | 11 svx/source/sdr/primitive2d/sdrattributecreator.cxx | 317 ----- svx/source/sidebar/area/AreaPropertyPanelBase.cxx | 24 svx/source/sidebar/area/AreaTransparencyGradientPopup.cxx | 13 svx/source/svdraw/gradtrns.cxx | 8 svx/source/svdraw/gradtrns.hxx | 5 svx/source/svdraw/svdetc.cxx | 3 svx/source/svdraw/svdfmtf.cxx | 31 svx/source/svdraw/svdmrkv.cxx | 3 svx/source/svdraw/svdoashp.cxx | 4 svx/source/tbxctrls/fillctrl.cxx | 4 svx/source/unodraw/XPropertyTable.cxx | 54 svx/source/unodraw/unobrushitemhelper.cxx | 8 svx/source/xoutdev/xattr.cxx | 313 ----- svx/source/xoutdev/xpool.cxx | 8 svx/source/xoutdev/xtabgrdt.cxx | 21 svx/source/xoutdev/xtable.cxx | 2 sw/inc/pch/precompiled_msword.hxx | 1 sw/inc/pch/precompiled_sw.hxx | 1 sw/inc/pch/precompiled_swui.hxx | 1 sw/qa/extras/odfexport/odfexport.cxx | 5 sw/qa/extras/ooxmlexport/ooxmlexport2.cxx | 5 sw/qa/extras/rtfexport/rtfexport.cxx | 7 sw/source/core/unocore/unoframe.cxx | 8 sw/source/filter/ww8/docxattributeoutput.cxx | 2 sw/source/filter/ww8/rtfattributeoutput.cxx | 4 sw/source/uibase/docvw/HeaderFooterWin.cxx | 2 sw/source/uibase/docvw/ShadowOverlayObject.cxx | 6 sw/source/uibase/shells/drawdlg.cxx | 4 sw/source/uibase/sidebar/PageStylesPanel.cxx | 14 sw/source/uibase/sidebar/PageStylesPanel.hxx | 2 sw/source/uibase/uiview/viewtab.cxx | 4 86 files changed, 1774 insertions(+), 1993 deletions(-)
New commits: commit 4249e57b9fff2a66cb1fb1757154f4a98379be68 Author: Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de> AuthorDate: Tue May 16 15:59:42 2023 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Sun May 21 15:00:00 2023 +0200 MCGR: Adaptions to WriteGradientFill and BGradient Added code to make WriteGradientFill directly use the available BGradient implementation. The goal is to never directly work on awt::Gradient2, but use BGradient & it's tooling methods. Added constructors and tooling to BGradient and BColorStops to make that easier (single line conversions between uno::Any, basesgfx classes and awt:: incarnations). Directly handle uno::Any and awt:: classes, changed stuff to make use of this. Change-Id: I083a323b9efee8ca4f3becb2966aac0a294b9a60 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151842 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/basegfx/source/tools/bgradient.cxx b/basegfx/source/tools/bgradient.cxx index 7cb1ed85e859..1499b0c682fe 100644 --- a/basegfx/source/tools/bgradient.cxx +++ b/basegfx/source/tools/bgradient.cxx @@ -94,10 +94,12 @@ BColorStops::BColorStops(const css::awt::ColorStopSequence& rColorStops) BColorStops::BColorStops(const css::uno::Any& rVal) { - css::awt::Gradient2 aGradient2; - if (rVal >>= aGradient2) + if (rVal.has<css::awt::ColorStopSequence>()) { - setColorStopSequence(aGradient2.ColorStops); + // we can use awt::ColorStopSequence + css::awt::ColorStopSequence aColorStopSequence; + rVal >>= aColorStopSequence; + setColorStopSequence(aColorStopSequence); } } @@ -627,7 +629,7 @@ BGradient::BGradient(const basegfx::BColorStops& rColorStops, css::awt::Gradient SetColorStops(aColorStops); } -BGradient::BGradient(const css::awt::Gradient2& rGradient2) +void BGradient::setGradient2(const css::awt::Gradient2& rGradient2) { // set values SetGradientStyle(rGradient2.Style); @@ -640,10 +642,24 @@ BGradient::BGradient(const css::awt::Gradient2& rGradient2) SetSteps(rGradient2.StepCount); // set ColorStops - aColorStops = BColorStops(rGradient2.ColorStops); - aColorStops.sortAndCorrect(); + if (rGradient2.ColorStops.hasElements()) + { + // if we have a awt::ColorStopSequence, use it + aColorStops = BColorStops(rGradient2.ColorStops); + aColorStops.sortAndCorrect(); + } + else + { + // if not, for compatibility, use StartColor/EndColor + aColorStops = BColorStops{ + BColorStop(0.0, ColorToBColorConverter(rGradient2.StartColor).getBColor()), + BColorStop(1.0, ColorToBColorConverter(rGradient2.EndColor).getBColor()) + }; + } } +BGradient::BGradient(const css::awt::Gradient2& rGradient2) { setGradient2(rGradient2); } + BGradient::BGradient(const css::uno::Any& rVal) : BGradient() { @@ -653,19 +669,7 @@ BGradient::BGradient(const css::uno::Any& rVal) css::awt::Gradient2 aGradient2; rVal >>= aGradient2; - // set values - SetGradientStyle(aGradient2.Style); - SetAngle(Degree10(aGradient2.Angle)); - SetBorder(aGradient2.Border); - SetXOffset(aGradient2.XOffset); - SetYOffset(aGradient2.YOffset); - SetStartIntens(aGradient2.StartIntensity); - SetEndIntens(aGradient2.EndIntensity); - SetSteps(aGradient2.StepCount); - - // set ColorStops - aColorStops = BColorStops(aGradient2.ColorStops); - aColorStops.sortAndCorrect(); + setGradient2(aGradient2); } else if (rVal.has<css::awt::Gradient>()) { diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx index c909108932cc..2cc455aa0342 100644 --- a/basegfx/source/tools/gradienttools.cxx +++ b/basegfx/source/tools/gradienttools.cxx @@ -265,47 +265,6 @@ namespace basegfx namespace utils { - /// 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 - const BColorStops aTempColorStops { - BColorStop(0.0, ColorToBColorConverter(aTmp.StartColor).getBColor()), - BColorStop(1.0, ColorToBColorConverter(aTmp.EndColor).getBColor()) }; - rGradient.ColorStops = aTempColorStops.getAsColorStopSequence(); - bRetval = true; - } - } - - return bRetval; - } - /* Tooling method to extract data from given awt::Gradient2 to ColorStops, doing some corrections, partitally based on given SingleColor. @@ -321,11 +280,11 @@ namespace basegfx directly */ void prepareColorStops( - const com::sun::star::awt::Gradient2& rGradient, + const basegfx::BGradient& rGradient, BColorStops& rColorStops, BColor& rSingleColor) { - rColorStops = BColorStops(rGradient.ColorStops); + rColorStops = rGradient.GetColorStops(); if (rColorStops.isSingleColor(rSingleColor)) { @@ -335,12 +294,12 @@ namespace basegfx return; } - if (rGradient.StartIntensity != 100 || rGradient.EndIntensity != 100) + if (100 != rGradient.GetStartIntens() || 100 != rGradient.GetEndIntens()) { // apply 'old' blend stuff, blend against black rColorStops.blendToIntensity( - rGradient.StartIntensity * 0.01, - rGradient.EndIntensity * 0.01, + rGradient.GetStartIntens() * 0.01, + rGradient.GetEndIntens() * 0.01, basegfx::BColor()); // COL_BLACK // can lead to single color (e.g. both zero, so all black), @@ -352,18 +311,18 @@ namespace basegfx } } - if (rGradient.Border != 0) + if (0 != rGradient.GetBorder()) { // apply Border if set // NOTE: no new start node is added. The new ColorStop // mechanism does not need entries at 0.0 and 1.0. // In case this is needed, do that in the caller - const double fFactor(rGradient.Border * 0.01); + const double fFactor(rGradient.GetBorder() * 0.01); BColorStops aNewStops; for (const auto& candidate : rColorStops) { - if (css::awt::GradientStyle_AXIAL == rGradient.Style) + if (css::awt::GradientStyle_AXIAL == rGradient.GetGradientStyle()) { // for axial add the 'gap' at the start due to reverse used gradient aNewStops.emplace_back((1.0 - fFactor) * candidate.getStopOffset(), candidate.getStopColor()); diff --git a/include/basegfx/utils/bgradient.hxx b/include/basegfx/utils/bgradient.hxx index 37de614faba7..4f85ed85a407 100644 --- a/include/basegfx/utils/bgradient.hxx +++ b/include/basegfx/utils/bgradient.hxx @@ -135,6 +135,8 @@ public: { } BColorStops(const css::awt::ColorStopSequence& rColorStops); + + // needs true == rVal.has<css::awt::ColorStopSequence>() BColorStops(const css::uno::Any& rVal); // constuctor with two colors to explicitly create a @@ -265,6 +267,7 @@ public: class BASEGFX_DLLPUBLIC BGradient final { +private: css::awt::GradientStyle eStyle; // MCGS: ColorStops in the range [0.0 .. 1.0], including StartColor/EndColor @@ -279,6 +282,7 @@ class BASEGFX_DLLPUBLIC BGradient final sal_uInt16 nStepCount; static std::string GradientStyleToString(css::awt::GradientStyle eStyle); + void setGradient2(const css::awt::Gradient2& rGradient2); public: BGradient(); @@ -288,6 +292,8 @@ public: sal_uInt16 nBorder = 0, sal_uInt16 nStartIntens = 100, sal_uInt16 nEndIntens = 100, sal_uInt16 nSteps = 0); BGradient(const css::awt::Gradient2& rGradient2); + + // needs true == (rVal.has<css::awt::Gradient>() || rVal.has<css::awt::Gradient2>()) BGradient(const css::uno::Any& rVal); bool operator==(const BGradient& rGradient) const; diff --git a/include/basegfx/utils/gradienttools.hxx b/include/basegfx/utils/gradienttools.hxx index 02d9b4ca68c8..50d58e8a39c0 100644 --- a/include/basegfx/utils/gradienttools.hxx +++ b/include/basegfx/utils/gradienttools.hxx @@ -186,11 +186,6 @@ namespace basegfx namespace utils { - /// Tooling method to fill awt::Gradient2 from data contained in the given Any - BASEGFX_DLLPUBLIC bool fillGradient2FromAny( - com::sun::star::awt::Gradient2& rGradient, - const com::sun::star::uno::Any& rVal); - /* Tooling method to extract data from given awt::Gradient2 to ColorStops, doing some corrections, partitally based on given SingleColor. @@ -206,7 +201,7 @@ namespace basegfx directly */ BASEGFX_DLLPUBLIC void prepareColorStops( - const com::sun::star::awt::Gradient2& rGradient, + const basegfx::BGradient& rGradient, BColorStops& rColorStops, BColor& rSingleColor); diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index f7bd91669a1d..cdfb31af9c7c 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -49,6 +49,7 @@ #include <tools/color.hxx> #include <vcl/mapmod.hxx> #include <svx/EnhancedCustomShape2d.hxx> +#include <basegfx/utils/bgradient.hxx> class Graphic; class SdrObjCustomShape; @@ -368,16 +369,16 @@ public: void WriteGradientFill( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); /* New API for WriteGradientFill: - If a awt::Gradient2 is given, it will be used. Else, the 'Fix' entry will be used for + If a BGradient 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, double fFixTransparence = 0.0); + const basegfx::BGradient* pColorGradient, sal_Int32 nFixColor, + const basegfx::BGradient* pTransparenceGradient, double fFixTransparence = 0.0); - void WriteGrabBagGradientFill( const css::uno::Sequence< css::beans::PropertyValue >& aGradientStops, const css::awt::Gradient2& rGradient); + void WriteGrabBagGradientFill( const css::uno::Sequence< css::beans::PropertyValue >& aGradientStops, const basegfx::BGradient& rGradient); void WriteBlipOrNormalFill( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet, const OUString& rURLPropName ); diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx index 359edfcb081b..e3704845407a 100644 --- a/oox/source/export/chartexport.cxx +++ b/oox/source/export/chartexport.cxx @@ -1903,37 +1903,26 @@ 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::Gradient2 aTransparenceGradient; + basegfx::BGradient aTransparenceGradient; bool bNeedGradientFill(false); OUString sFillTransparenceGradientName; + if (GetProperty(xPropSet, "FillTransparenceGradientName") && (mAny >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty()) { 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); + const uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName); - if (basegfx::utils::fillGradient2FromAny(aTransparenceGradient, rTransparenceValue)) - { - const basegfx::BColorStops aColorStops(rTransparenceValue); - basegfx::BColor aSingleColor; - bNeedGradientFill = !aColorStops.isSingleColor(aSingleColor); - } + aTransparenceGradient = basegfx::BGradient(rTransparenceValue); + basegfx::BColor aSingleColor; + bNeedGradientFill = !aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor); - if (!bNeedGradientFill && 0 != aTransparenceGradient.StartColor) + if (!bNeedGradientFill) { - // Our alpha is a gray color value. - sal_uInt8 nRed(0); - - if (aTransparenceGradient.ColorStops.getLength() > 0) - { - nRed = static_cast<sal_uInt8>(aTransparenceGradient.ColorStops[0].StopColor.Red * 255.0); - } - else - { - nRed = ::Color(ColorTransparency, aTransparenceGradient.StartColor).GetRed(); - } + // Our alpha is a single gray color value. + const sal_uInt8 nRed(aSingleColor.getRed() * 255.0); // drawingML alpha is a percentage on a 0..100000 scale. nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255; @@ -2012,19 +2001,23 @@ void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet try { uno::Reference< container::XNameAccess > xGradient( xFact->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY ); - uno::Any rGradientValue = xGradient->getByName( sFillGradientName ); - awt::Gradient2 aGradient; + const uno::Any rGradientValue(xGradient->getByName( sFillGradientName )); + const basegfx::BGradient aGradient(rGradientValue); + basegfx::BColor aSingleColor; - if (basegfx::utils::fillGradient2FromAny(aGradient, rGradientValue)) + if (!aGradient.GetColorStops().isSingleColor(aSingleColor)) { - awt::Gradient2 aTransparenceGradient; + basegfx::BGradient 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); - basegfx::utils::fillGradient2FromAny(aTransparenceGradient, rTransparenceValue); + const uno::Any rTransparenceValue(xTransparenceGradient->getByName(sFillTransparenceGradientName)); + + aTransparenceGradient = basegfx::BGradient(rTransparenceValue); + WriteGradientFill(&aGradient, 0, &aTransparenceGradient); } else if (GetProperty(xPropSet, "FillTransparence") ) @@ -2038,7 +2031,7 @@ void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet } else { - WriteGradientFill(&aGradient, 0, &aTransparenceGradient); + WriteGradientFill(&aGradient, 0, nullptr); } mpFS->endElementNS(XML_a, XML_gradFill); diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 2f2aa2558f21..8646b056ff66 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -211,7 +211,7 @@ static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& namespace { -void WriteGradientPath(const awt::Gradient2& rGradient, const FSHelperPtr& pFS, const bool bCircle) +void WriteGradientPath(const basegfx::BGradient& rBGradient, const FSHelperPtr& pFS, const bool bCircle) { pFS->startElementNS(XML_a, XML_path, XML_path, bCircle ? "circle" : "rect"); @@ -221,13 +221,13 @@ void WriteGradientPath(const awt::Gradient2& rGradient, const FSHelperPtr& pFS, // edge of the tile rectangle and 100% means the center of it. rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList( sax_fastparser::FastSerializerHelper::createAttrList()); - sal_Int32 nLeftPercent = rGradient.XOffset; + sal_Int32 nLeftPercent = rBGradient.GetXOffset(); pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT)); - sal_Int32 nTopPercent = rGradient.YOffset; + sal_Int32 nTopPercent = rBGradient.GetYOffset(); pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT)); - sal_Int32 nRightPercent = 100 - rGradient.XOffset; + sal_Int32 nRightPercent = 100 - rBGradient.GetXOffset(); pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT)); - sal_Int32 nBottomPercent = 100 - rGradient.YOffset; + sal_Int32 nBottomPercent = 100 - rBGradient.GetYOffset(); pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT)); pFS->singleElementNS(XML_a, XML_fillToRect, pAttributeList); @@ -469,31 +469,19 @@ 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::Gradient2 aTransparenceGradient; + basegfx::BGradient aTransparenceGradient; bool bNeedGradientFill(false); if (GetProperty(rXPropSet, "FillTransparenceGradient")) { - if (basegfx::utils::fillGradient2FromAny(aTransparenceGradient, mAny)) - { - const basegfx::BColorStops aColorStops(mAny); - basegfx::BColor aSingleColor; - bNeedGradientFill = !aColorStops.isSingleColor(aSingleColor); - } + aTransparenceGradient = basegfx::BGradient(mAny); + basegfx::BColor aSingleColor; + bNeedGradientFill = !aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor); - if (!bNeedGradientFill && 0 != aTransparenceGradient.StartColor) + if (!bNeedGradientFill && aSingleColor != basegfx::BColor()) { // Our alpha is a gray color value. - sal_uInt8 nRed(0); - - if (aTransparenceGradient.ColorStops.getLength() > 0) - { - nRed = static_cast<sal_uInt8>(aTransparenceGradient.ColorStops[0].StopColor.Red * 255.0); - } - else - { - nRed = ::Color(ColorTransparency, aTransparenceGradient.StartColor).GetRed(); - } + const sal_uInt8 nRed(aSingleColor.getRed() * 255.0); // drawingML alpha is a percentage on a 0..100000 scale. nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255; @@ -591,57 +579,16 @@ void DrawingML::WriteGradientStop(double fOffset, const basegfx::BColor& rColor, | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 )); } -bool DrawingML::EqualGradients( const awt::Gradient2& rGradient1, const awt::Gradient2& rGradient2 ) -{ - 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::Gradient2 aGradient; - if (!GetProperty(rXPropSet, "FillGradient")) return; - // use fillGradient2FromAny to evtl. take care of Gradient/Gradient2 - basegfx::utils::fillGradient2FromAny(aGradient, mAny); + // use BGradient constructor directly, it will take care of Gradient/Gradient2 + basegfx::BGradient aGradient(mAny); // get InteropGrabBag and search the relevant attributes - awt::Gradient2 aOriginalGradient; + basegfx::BGradient aOriginalGradient; Sequence< PropertyValue > aGradientStops; if ( GetProperty( rXPropSet, "InteropGrabBag" ) ) { @@ -651,14 +598,14 @@ void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) if( rProp.Name == "GradFillDefinition" ) rProp.Value >>= aGradientStops; else if( rProp.Name == "OriginalGradFill" ) - // use fillGradient2FromAny to evtl. take care of Gradient/Gradient2 - basegfx::utils::fillGradient2FromAny(aOriginalGradient, rProp.Value); + // use BGradient constructor direcly, it will take care of Gradient/Gradient2 + aOriginalGradient = basegfx::BGradient(rProp.Value); } // check if an ooxml gradient had been imported and if the user has modified it // Gradient grab-bag depends on theme grab-bag, which is implemented // only for DOCX. - if( EqualGradients( aOriginalGradient, aGradient ) && GetDocumentType() == DOCUMENT_DOCX) + if (aOriginalGradient == aGradient && GetDocumentType() == DOCUMENT_DOCX) { // If we have no gradient stops that means original gradient were defined by a theme. if( aGradientStops.hasElements() ) @@ -672,8 +619,8 @@ void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) { mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); - awt::Gradient2 aTransparenceGradient; - awt::Gradient2* pTransparenceGradient(nullptr); + basegfx::BGradient aTransparenceGradient; + basegfx::BGradient* pTransparenceGradient(nullptr); double fTransparency(0.0); OUString sFillTransparenceGradientName; @@ -682,7 +629,9 @@ void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) && !sFillTransparenceGradientName.isEmpty()) { if (GetProperty(rXPropSet, "FillTransparenceGradient")) - aTransparenceGradient = *o3tl::doAccess<awt::Gradient2>(mAny); + { + aTransparenceGradient = basegfx::BGradient(mAny); + } pTransparenceGradient = &aTransparenceGradient; } @@ -702,7 +651,7 @@ void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) } } -void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, const awt::Gradient2& rGradient ) +void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, const basegfx::BGradient& rBGradient ) { // write back the original gradient mpFS->startElementNS(XML_a, XML_gsLst); @@ -748,44 +697,46 @@ void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGrad } mpFS->endElementNS( XML_a, XML_gsLst ); - switch (rGradient.Style) + switch (rBGradient.GetGradientStyle()) { default: + { + const sal_Int16 nAngle(rBGradient.GetAngle()); mpFS->singleElementNS( XML_a, XML_lin, XML_ang, - OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000)); + OString::number(((3600 - static_cast<sal_Int32>(nAngle) + 900) * 6000) % 21600000)); break; + } case awt::GradientStyle_RADIAL: - WriteGradientPath(rGradient, mpFS, true); + { + WriteGradientPath(rBGradient, mpFS, true); break; + } } } void DrawingML::WriteGradientFill( - const awt::Gradient2* pColorGradient, sal_Int32 nFixColor, - const awt::Gradient2* pTransparenceGradient, double fFixTransparence) + const basegfx::BGradient* pColorGradient, sal_Int32 nFixColor, + const basegfx::BGradient* pTransparenceGradient, double fFixTransparence) { basegfx::BColorStops aColorStops; basegfx::BColorStops aAlphaStops; basegfx::BColor aSingleColor(::Color(ColorTransparency, nFixColor).getBColor()); basegfx::BColor aSingleAlpha(fFixTransparence); - awt::Gradient2 aGradient; + const basegfx::BGradient* pGradient(pColorGradient); if (nullptr != pColorGradient) { - // remember basic Gradient definition to use - aGradient = *pColorGradient; - - // extract and corrrect/process ColorStops + // extract and correct/process ColorStops basegfx::utils::prepareColorStops(*pColorGradient, aColorStops, aSingleColor); } if (nullptr != pTransparenceGradient) { // remember basic Gradient definition to use - if (nullptr == pColorGradient) + if (nullptr == pGradient) { - aGradient = *pTransparenceGradient; + pGradient = pTransparenceGradient; } // extract and corrrect/process AlphaStops @@ -797,10 +748,12 @@ void DrawingML::WriteGradientFill( // method (at import time) will be exported again basegfx::utils::synchronizeColorStops(aColorStops, aAlphaStops, aSingleColor, aSingleAlpha); - if (aColorStops.size() != aAlphaStops.size()) + if (aColorStops.size() != aAlphaStops.size() || nullptr == pGradient) { // this is an error - synchronizeColorStops above *has* to create that - // state, see desciption there (!) + // state, see description there (!) + // also an error - see comment in header - is to give neither pColorGradient + // nor pTransparenceGradient assert(false && "oox::WriteGradientFill: non-synchronized gradients (!)"); return; } @@ -809,7 +762,7 @@ void DrawingML::WriteGradientFill( bool bLinear(false); bool bAxial(false); - switch (aGradient.Style) + switch (pGradient->GetGradientStyle()) { case awt::GradientStyle_LINEAR: { @@ -902,18 +855,19 @@ void DrawingML::WriteGradientFill( if (bLinear || bAxial) { // cases where gradient rotation has to be exported + const sal_Int16 nAngle(pGradient->GetAngle()); mpFS->singleElementNS( XML_a, XML_lin, XML_ang, - OString::number(((3600 - aGradient.Angle + 900) * 6000) % 21600000)); + OString::number(((3600 - static_cast<sal_Int32>(nAngle) + 900) * 6000) % 21600000)); } if (bRadialOrEllipticalOrRectOrSquare) { // cases where gradient path has to be exported - const bool bCircle(aGradient.Style == awt::GradientStyle_RADIAL || - aGradient.Style == awt::GradientStyle_ELLIPTICAL); + const bool bCircle(pGradient->GetGradientStyle() == awt::GradientStyle_RADIAL || + pGradient->GetGradientStyle() == awt::GradientStyle_ELLIPTICAL); - WriteGradientPath(aGradient, mpFS, bCircle); + WriteGradientPath(*pGradient, mpFS, bCircle); } } diff --git a/svx/source/unodraw/XPropertyTable.cxx b/svx/source/unodraw/XPropertyTable.cxx index 8eb7401e0c36..3defe221d1f5 100644 --- a/svx/source/unodraw/XPropertyTable.cxx +++ b/svx/source/unodraw/XPropertyTable.cxx @@ -558,29 +558,10 @@ uno::Any SvxUnoXGradientTable::getAny( const XPropertyEntry* pEntry ) const noex std::unique_ptr<XPropertyEntry> SvxUnoXGradientTable::createEntry(const OUString& rName, const uno::Any& rAny) const { - awt::Gradient aGradient; - if(!(rAny >>= aGradient)) + if (!rAny.has<css::awt::Gradient>() || !rAny.has<css::awt::Gradient2>()) return std::unique_ptr<XPropertyEntry>(); - basegfx::BGradient aBGradient( - basegfx::BColorStops( - Color(ColorTransparency, aGradient.StartColor).getBColor(), - Color(ColorTransparency, aGradient.EndColor).getBColor())); - - aBGradient.SetGradientStyle( aGradient.Style ); - aBGradient.SetAngle( Degree10(aGradient.Angle) ); - aBGradient.SetBorder( aGradient.Border ); - aBGradient.SetXOffset( aGradient.XOffset ); - aBGradient.SetYOffset( aGradient.YOffset ); - aBGradient.SetStartIntens( aGradient.StartIntensity ); - aBGradient.SetEndIntens( aGradient.EndIntensity ); - aBGradient.SetSteps( aGradient.StepCount ); - - // check if we have a awt::Gradient2 with a ColorStopSequence - const basegfx::BColorStops aColorStops(rAny); - if (!aColorStops.empty()) - aBGradient.SetColorStops(aColorStops); - + const basegfx::BGradient aBGradient(rAny); return std::make_unique<XGradientEntry>(aBGradient, rName); } diff --git a/svx/source/xoutdev/xattr.cxx b/svx/source/xoutdev/xattr.cxx index 9e9c1d74f59b..24807388535f 100644 --- a/svx/source/xoutdev/xattr.cxx +++ b/svx/source/xoutdev/xattr.cxx @@ -2151,37 +2151,6 @@ bool XFillGradientItem::GetPresentation return true; } -namespace -{ - void fillXGradientFromAny(basegfx::BGradient& rBGradient, const css::uno::Any& rVal) - { - css::awt::Gradient aGradient; - if (!(rVal >>= aGradient)) - return; - - // for compatibility, read and set StartColor/EndColor - rBGradient.SetColorStops( - basegfx::BColorStops( - Color(ColorTransparency, aGradient.StartColor).getBColor(), - Color(ColorTransparency, aGradient.EndColor).getBColor())); - - // set values - rBGradient.SetGradientStyle( aGradient.Style ); - rBGradient.SetAngle( Degree10(aGradient.Angle) ); - rBGradient.SetBorder( aGradient.Border ); - rBGradient.SetXOffset( aGradient.XOffset ); - rBGradient.SetYOffset( aGradient.YOffset ); - rBGradient.SetStartIntens( aGradient.StartIntensity ); - rBGradient.SetEndIntens( aGradient.EndIntensity ); - rBGradient.SetSteps( aGradient.StepCount ); - - // check if we have a awt::Gradient2 with a ColorStopSequence - const basegfx::BColorStops aColorStops(rVal); - if (!aColorStops.empty()) - rBGradient.SetColorStops(aColorStops); - } -} - bool XFillGradientItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const { nMemberId &= ~CONVERT_TWIPS; @@ -2270,10 +2239,9 @@ bool XFillGradientItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId SetName( aName ); - if ( aGradientAny.hasValue() ) + if (aGradientAny.hasValue() && (aGradientAny.has<css::awt::Gradient>() || aGradientAny.has<css::awt::Gradient2>())) { - basegfx::BGradient aBGradient; - fillXGradientFromAny(aBGradient, aGradientAny); + const basegfx::BGradient aBGradient(aGradientAny); SetGradientValue(aBGradient); } @@ -2294,22 +2262,28 @@ bool XFillGradientItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId case MID_FILLGRADIENT: { - basegfx::BGradient aBGradient; - fillXGradientFromAny(aBGradient, rVal); - SetGradientValue(aBGradient); + if (rVal.hasValue() && (rVal.has<css::awt::Gradient>() || rVal.has<css::awt::Gradient2>())) + { + const basegfx::BGradient aBGradient(rVal); + SetGradientValue(aBGradient); + } + break; } case MID_GRADIENT_COLORSTOPSEQUENCE: { - // check if we have a awt::Gradient2 with a ColorStopSequence - const basegfx::BColorStops aColorStops(rVal); - - if (!aColorStops.empty()) + // check if we have a awt::ColorStopSequence + if (rVal.hasValue() && rVal.has<css::awt::ColorStopSequence>()) { - basegfx::BGradient aBGradient(GetGradientValue()); - aBGradient.SetColorStops(aColorStops); - SetGradientValue(aBGradient); + const basegfx::BColorStops aColorStops(rVal); + + if (!aColorStops.empty()) + { + basegfx::BGradient aBGradient(GetGradientValue()); + aBGradient.SetColorStops(aColorStops); + SetGradientValue(aBGradient); + } } break; } commit b1e18601c6a8ff2a0b516b309739af1b233d013b Author: Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de> AuthorDate: Fri May 12 15:32:51 2023 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Sun May 21 15:00:00 2023 +0200 MCGR: consolidations/cleanups for changes so far Change-Id: I85cf40e4803b0485bb40349d8e81adc8123666c4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151706 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/basctl/inc/pch/precompiled_basctl.hxx b/basctl/inc/pch/precompiled_basctl.hxx index 4f3566f3f6ea..257159149a3b 100644 --- a/basctl/inc/pch/precompiled_basctl.hxx +++ b/basctl/inc/pch/precompiled_basctl.hxx @@ -499,7 +499,6 @@ #include <svx/svxdllapi.h> #include <svx/xdash.hxx> #include <svx/xdef.hxx> -#include <svx/xgrad.hxx> #include <svx/xhatch.hxx> #include <svx/xpoly.hxx> #include <svx/xtable.hxx> diff --git a/basegfx/Library_basegfx.mk b/basegfx/Library_basegfx.mk index 2f9830d0dcfd..da257e2c9797 100644 --- a/basegfx/Library_basegfx.mk +++ b/basegfx/Library_basegfx.mk @@ -67,6 +67,7 @@ $(eval $(call gb_Library_add_exception_objects,basegfx,\ basegfx/source/range/b3drange \ basegfx/source/raster/rasterconvert3d \ basegfx/source/tools/b2dclipstate \ + basegfx/source/tools/bgradient \ basegfx/source/tools/canvastools \ basegfx/source/tools/gradienttools \ basegfx/source/tools/keystoplerp \ diff --git a/basegfx/source/tools/bgradient.cxx b/basegfx/source/tools/bgradient.cxx new file mode 100644 index 000000000000..7cb1ed85e859 --- /dev/null +++ b/basegfx/source/tools/bgradient.cxx @@ -0,0 +1,770 @@ +/* -*- 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/. + */ + +#include <basegfx/utils/bgradient.hxx> +#include <basegfx/utils/gradienttools.hxx> +#include <com/sun/star/awt/Gradient2.hpp> +#include <boost/property_tree/json_parser.hpp> +#include <map> + +typedef std::map<OUString, OUString> StringMap; + +namespace +{ +css::awt::GradientStyle lcl_getStyleFromString(std::u16string_view rStyle) +{ + if (rStyle == u"LINEAR") + return css::awt::GradientStyle_LINEAR; + else if (rStyle == u"AXIAL") + return css::awt::GradientStyle_AXIAL; + else if (rStyle == u"RADIAL") + return css::awt::GradientStyle_RADIAL; + else if (rStyle == u"ELLIPTICAL") + return css::awt::GradientStyle_ELLIPTICAL; + else if (rStyle == u"SQUARE") + return css::awt::GradientStyle_SQUARE; + else if (rStyle == u"RECT") + return css::awt::GradientStyle_RECT; + + return css::awt::GradientStyle_LINEAR; +} + +StringMap lcl_jsonToStringMap(std::u16string_view rJSON) +{ + StringMap aArgs; + if (rJSON.size() && rJSON[0] != '\0') + { + std::stringstream aStream(std::string(OUStringToOString(rJSON, RTL_TEXTENCODING_ASCII_US))); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + + for (const auto& rPair : aTree) + { + aArgs[OUString::fromUtf8(rPair.first)] + = OUString::fromUtf8(rPair.second.get_value<std::string>(".")); + } + } + return aArgs; +} + +basegfx::BGradient lcl_buildGradientFromStringMap(StringMap& rMap) +{ + basegfx::BGradient aGradient( + basegfx::BColorStops(ColorToBColorConverter(rMap["startcolor"].toInt32(16)).getBColor(), + ColorToBColorConverter(rMap["endcolor"].toInt32(16)).getBColor())); + + aGradient.SetGradientStyle(lcl_getStyleFromString(rMap["style"])); + aGradient.SetAngle(Degree10(rMap["angle"].toInt32())); + + return aGradient; +} +} + +namespace basegfx +{ +void BColorStops::setColorStopSequence(const css::awt::ColorStopSequence& rColorStops) +{ + const sal_Int32 nLen(rColorStops.getLength()); + + if (0 != nLen) + { + // we have ColorStops + reserve(nLen); + const css::awt::ColorStop* pSourceColorStop(rColorStops.getConstArray()); + + for (sal_Int32 a(0); a < nLen; a++, pSourceColorStop++) + { + emplace_back(pSourceColorStop->StopOffset, + BColor(pSourceColorStop->StopColor.Red, pSourceColorStop->StopColor.Green, + pSourceColorStop->StopColor.Blue)); + } + } +} + +BColorStops::BColorStops(const css::awt::ColorStopSequence& rColorStops) +{ + setColorStopSequence(rColorStops); +} + +BColorStops::BColorStops(const css::uno::Any& rVal) +{ + css::awt::Gradient2 aGradient2; + if (rVal >>= aGradient2) + { + setColorStopSequence(aGradient2.ColorStops); + } +} + +// constuctor with two colors to explicitly create a +// BColorStops for a single StartColor @0.0 & EndColor @1.0 +BColorStops::BColorStops(const BColor& rStart, const BColor& rEnd) +{ + emplace_back(0.0, rStart); + emplace_back(1.0, rEnd); +} + +/* Helper to grep the correct ColorStop out of + ColorStops and interpolate as needed for given + relative value in fPosition in the range of [0.0 .. 1.0]. + It also takes care of evtl. given RequestedSteps. + */ +BColor BColorStops::getInterpolatedBColor(double fPosition, sal_uInt32 nRequestedSteps, + BColorStopRange& rLastColorStopRange) const +{ + // no color at all, done + if (empty()) + return BColor(); + + // outside range -> at start + const double fMin(front().getStopOffset()); + if (fPosition < fMin) + return front().getStopColor(); + + // outside range -> at end + const double fMax(back().getStopOffset()); + if (fPosition > fMax) + return back().getStopColor(); + + // special case for the 'classic' case with just two colors: + // we can optimize that and keep the speed/resources low + // by avoiding some calculations and an O(log(N)) array access + if (2 == size()) + { + // if same StopOffset use front color + if (fTools::equal(fMin, fMax)) + return front().getStopColor(); + + const basegfx::BColor aCStart(front().getStopColor()); + const basegfx::BColor aCEnd(back().getStopColor()); + + // if colors are equal just return one + if (aCStart == aCEnd) + return aCStart; + + // calculate Steps + const sal_uInt32 nSteps( + basegfx::utils::calculateNumberOfSteps(nRequestedSteps, aCStart, aCEnd)); + + // we need to extend the interpolation to the local + // range of ColorStops. Despite having two ColorStops + // these are not necessarily at 0.0 and 1.0, so may be + // not the classical Start/EndColor (what is allowed) + fPosition = (fPosition - fMin) / (fMax - fMin); + return basegfx::interpolate(aCStart, aCEnd, + nSteps > 1 ? floor(fPosition * nSteps) / double(nSteps - 1) + : fPosition); + } + + // check if we need to newly populate the needed interpolation data + // or if we can re-use from last time. + // If this scope is not entered, we do not need the binary search. It's + // only a single buffered entry, and only used when more than three + // ColorStops exist, but makes a huge difference compared with accessing + // the sorted ColorStop vector each time. + // NOTE: with this simple change I get very high hit rates, e.g. rotating + // a donut with gradient test '1' hit rate is at 0.99909440357755486 + if (rLastColorStopRange.mfOffsetStart == rLastColorStopRange.mfOffsetEnd + || fPosition < rLastColorStopRange.mfOffsetStart + || fPosition > rLastColorStopRange.mfOffsetEnd) + { + // access needed spot in sorted array using binary search + // NOTE: This *seems* slow(er) when developing compared to just + // looping/accessing, but that's just due to the extensive + // debug test code created by the stl. In a pro version, + // all is good/fast as expected + const auto upperBound(std::upper_bound(begin(), end(), BColorStop(fPosition), + [](const BColorStop& x, const BColorStop& y) { + return x.getStopOffset() < y.getStopOffset(); + })); + + // no upper bound, done + if (end() == upperBound) + return back().getStopColor(); + + // lower bound is one entry back, access that + const auto lowerBound(upperBound - 1); + + // no lower bound, done + if (end() == lowerBound) + return back().getStopColor(); + + // we have lower and upper bound, get colors and offsets + rLastColorStopRange.maColorStart = lowerBound->getStopColor(); + rLastColorStopRange.maColorEnd = upperBound->getStopColor(); + rLastColorStopRange.mfOffsetStart = lowerBound->getStopOffset(); + rLastColorStopRange.mfOffsetEnd = upperBound->getStopOffset(); + } + + // when there are just two color steps this cannot happen, but when using + // a range of colors this *may* be used inside the range to represent + // single-colored regions inside a ColorRange. Use that color & done + if (rLastColorStopRange.maColorStart == rLastColorStopRange.maColorEnd) + return rLastColorStopRange.maColorStart; + + // calculate number of steps and adapted proportional + // range for scaler in [0.0 .. 1.0] + const double fAdaptedScaler( + (fPosition - rLastColorStopRange.mfOffsetStart) + / (rLastColorStopRange.mfOffsetEnd - rLastColorStopRange.mfOffsetStart)); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + nRequestedSteps, rLastColorStopRange.maColorStart, rLastColorStopRange.maColorEnd)); + + // interpolate & evtl. apply steps + return interpolate(rLastColorStopRange.maColorStart, rLastColorStopRange.maColorEnd, + nSteps > 1 ? floor(fAdaptedScaler * nSteps) / double(nSteps - 1) + : fAdaptedScaler); +} + +/* Tooling method that allows to replace the StartColor in a + vector of ColorStops. A vector in 'ordered state' is expected, + so you may use/have used sortAndCorrect. + This method is for convenience & backwards compatibility, please + think about handling multi-colored gradients directly. + */ +void BColorStops::replaceStartColor(const BColor& rStart) +{ + BColorStops::iterator a1stNonStartColor(begin()); + + // search for highest existing non-StartColor - CAUTION, + // there might be none, one or multiple with StopOffset 0.0 + while (a1stNonStartColor != end() + && basegfx::fTools::lessOrEqual(a1stNonStartColor->getStopOffset(), 0.0)) + a1stNonStartColor++; + + // create new ColorStops by 1st adding new one and then all + // non-StartColor entries + BColorStops aNewColorStops; + + aNewColorStops.reserve(size() + 1); + aNewColorStops.emplace_back(0.0, rStart); + aNewColorStops.insert(aNewColorStops.end(), a1stNonStartColor, end()); + + // assign & done + *this = aNewColorStops; +} + +/* Tooling method that allows to replace the EndColor in a + vector of ColorStops. A vector in 'ordered state' is expected, + so you may use/have used sortAndCorrectColorStops. + This method is for convenience & backwards compatibility, please + think about handling multi-colored gradients directly. + */ +void BColorStops::replaceEndColor(const BColor& rEnd) +{ + // erase all evtl. existing EndColor(s) + while (!empty() && basegfx::fTools::moreOrEqual(back().getStopOffset(), 1.0)) + pop_back(); + + // add at the end of existing ColorStops + emplace_back(1.0, rEnd); +} + +/* Tooling method to linearly blend the Colors contained in + a given ColorStop vector against a given Color using the + given intensity values. + The intensity values fStartIntensity, fEndIntensity are + in the range of [0.0 .. 1.0] and describe how much the + blend is supposed to be done at the start color position + and the end color position respectively, where 0.0 means + to fully use the given BlendColor, 1.0 means to not change + the existing color in the ColorStop. + Every color entry in the given ColorStop is blended + relative to it's StopPosition, interpolating the + given intensities with the range [0.0 .. 1.0] to do so. + */ +void BColorStops::blendToIntensity(double fStartIntensity, double fEndIntensity, + const BColor& rBlendColor) +{ + // no entries, done + if (empty()) + return; + + // correct intensities (maybe assert when input was wrong) + fStartIntensity = std::max(std::min(1.0, fStartIntensity), 0.0); + fEndIntensity = std::max(std::min(1.0, fEndIntensity), 0.0); + + // all 100%, no real blend, done + if (basegfx::fTools::equal(fStartIntensity, 1.0) && basegfx::fTools::equal(fEndIntensity, 1.0)) + return; + + // blend relative to StopOffset position + for (auto& candidate : *this) + { + const double fOffset(candidate.getStopOffset()); + const double fIntensity((fStartIntensity * (1.0 - fOffset)) + (fEndIntensity * fOffset)); + candidate = basegfx::BColorStop( + fOffset, basegfx::interpolate(rBlendColor, candidate.getStopColor(), fIntensity)); + } +} + +/* Tooling method to guarantee sort and correctness for + the given ColorStops vector. + A vector fulfilling these conditions is called to be + in 'ordered state'. + + At return, the following conditions are guaranteed: + - contains no ColorStops with offset < 0.0 (will + be removed) + - contains no ColorStops with offset > 1.0 (will + be removed) + - ColorStops with identical offsets are now allowed + - will be sorted from lowest offset to highest + + Some more notes: + - It can happen that the result is empty + - It is allowed to have consecutive entries with + the same color, this represents single-color + regions inside the gradient + - A entry with 0.0 is not required or forced, so + no 'StartColor' is technically required + - A entry with 1.0 is not required or forced, so + no 'EndColor' is technically required + + All this is done in one run (sort + O(N)) without + creating a copy of the data in any form + */ +void BColorStops::sortAndCorrect() +{ + // no content, we are done + if (empty()) + return; + + if (1 == size()) + { + // no gradient at all, but preserve given color + // evtl. correct offset to be in valid range [0.0 .. 1.0] + // NOTE: This does not move it to 0.0 or 1.0, it *can* still + // be somewhere in-between what is allowed + const BColorStop aEntry(front()); + clear(); + emplace_back(std::max(0.0, std::min(1.0, aEntry.getStopOffset())), aEntry.getStopColor()); + + // done + return; + } + + // start with sorting the input data. Remember that + // this preserves the order of equal entries, where + // equal is defined here by offset (see use operator==) + std::sort(begin(), end()); + + // prepare status values + size_t write(0); + + // use the paradigm of a band machine with two heads, read + // and write with write <= read all the time. Step over the + // data using read and check for valid entry. If valid, decide + // how to keep it + for (size_t read(0); read < size(); read++) + { + // get offset of entry at read position + double fOff((*this)[read].getStopOffset()); + + if (basegfx::fTools::less(fOff, 0.0) && read + 1 < size()) + { + // value < 0.0 and we have a next entry. check for gradient snippet + // containing 0.0 resp. StartColor + const double fOff2((*this)[read + 1].getStopOffset()); + + if (basegfx::fTools::more(fOff2, 0.0)) + { + // read is the start of a gradient snippet containing 0.0. Correct + // entry to StartColor, interpolate to correct StartColor + (*this)[read] + = BColorStop(0.0, basegfx::interpolate((*this)[read].getStopColor(), + (*this)[read + 1].getStopColor(), + (0.0 - fOff) / (fOff2 - fOff))); + + // adapt fOff + fOff = 0.0; + } + } + + // step over < 0 values, these are outside and will be removed + if (basegfx::fTools::less(fOff, 0.0)) + { + continue; + } + + if (basegfx::fTools::less(fOff, 1.0) && read + 1 < size()) + { + // value < 1.0 and we have a next entry. check for gradient snippet + // containing 1.0 resp. EndColor + const double fOff2((*this)[read + 1].getStopOffset()); + + if (basegfx::fTools::more(fOff2, 1.0)) + { + // read is the start of a gradient snippet containing 1.0. Correct + // next entry to EndColor, interpolate to correct EndColor + (*this)[read + 1] + = BColorStop(1.0, basegfx::interpolate((*this)[read].getStopColor(), + (*this)[read + 1].getStopColor(), + (1.0 - fOff) / (fOff2 - fOff))); + + // adapt fOff + fOff = 1.0; + } + } + + // step over > 1 values; even break, since all following + // entries will also be bigger due to being sorted, so done + if (basegfx::fTools::more(fOff, 1.0)) + { + break; + } + + // entry is valid value at read position + // copy if write target is empty (write at start) or when + // write target is different to read in color or offset + if (0 == write || !((*this)[read] == (*this)[write - 1])) + { + if (write != read) + { + // copy read to write backwards to close gaps + (*this)[write] = (*this)[read]; + } + + // always forward write position + write++; + } + } + + // correct size when length is reduced. write is always at + // last used position + 1 + if (size() > write) + { + if (0 == write) + { + // no valid entries at all, but not empty. This can only happen + // when all entries are below 0.0 or above 1.0 (else a gradient + // snippet spawning over both would have been detected) + if (basegfx::fTools::less(back().getStopOffset(), 0.0)) + { + // all outside too low, rescue last due to being closest to content + const BColor aBackColor(back().getStopColor()); + clear(); + emplace_back(0.0, aBackColor); + } + else // if (basegfx::fTools::more(front().getStopOffset(), 1.0)) + { + // all outside too high, rescue first due to being closest to content + const BColor aFrontColor(front().getStopColor()); + clear(); + emplace_back(1.0, aFrontColor); + } + } + else + { + resize(write); + } + } +} + +bool BColorStops::checkPenultimate() const +{ + // not needed when no ColorStops + if (empty()) + return false; + + // not needed when last ColorStop at the end or outside + if (basegfx::fTools::moreOrEqual(back().getStopOffset(), 1.0)) + return false; + + // get penultimate entry + const auto penultimate(rbegin() + 1); + + // if there is none, we need no correction and are done + if (penultimate == rend()) + return false; + + // not needed when the last two ColorStops have different offset, then + // a visible range will be processed already + if (!basegfx::fTools::equal(back().getStopOffset(), penultimate->getStopOffset())) + return false; + + // not needed when the last two ColorStops have the same Color, then the + // range before solves the problem + if (back().getStopColor() == penultimate->getStopColor()) + return false; + + return true; +} + +/* Tooling method to fill a awt::ColorStopSequence with + the data from the given ColorStops. This is used in + UNO API implementations. + */ +css::awt::ColorStopSequence BColorStops::getAsColorStopSequence() const +{ + css::awt::ColorStopSequence aRetval(size()); + // rColorStopSequence.realloc(rColorStops.size()); + css::awt::ColorStop* pTargetColorStop(aRetval.getArray()); + + for (const auto& candidate : *this) + { + pTargetColorStop->StopOffset = candidate.getStopOffset(); + pTargetColorStop->StopColor = css::rendering::RGBColor(candidate.getStopColor().getRed(), + candidate.getStopColor().getGreen(), + candidate.getStopColor().getBlue()); + pTargetColorStop++; + } + + return aRetval; +} + +/* Tooling method to check if a ColorStop vector is defined + by a single color. It returns true if this is the case. + If true is returned, rSingleColor contains that single + color for convenience. + NOTE: If no ColorStop is defined, a fallback to BColor-default + (which is black) and true will be returned + */ +bool BColorStops::isSingleColor(BColor& rSingleColor) const +{ + if (empty()) + { + rSingleColor = BColor(); + return true; + } + + if (1 == size()) + { + rSingleColor = front().getStopColor(); + return true; + } + + rSingleColor = front().getStopColor(); + + for (auto const& rCandidate : *this) + { + if (rCandidate.getStopColor() != rSingleColor) + return false; + } + + return true; +} + +/* Tooling method to reverse ColorStops, including offsets. + When also mirroring offsets a valid sort keeps valid. + */ +void BColorStops::reverseColorStops() +{ + // can use std::reverse, but also need to adapt offset(s) + std::reverse(begin(), end()); + for (auto& candidate : *this) + candidate = BColorStop(1.0 - candidate.getStopOffset(), candidate.getStopColor()); +} + +std::string BGradient::GradientStyleToString(css::awt::GradientStyle eStyle) +{ + switch (eStyle) + { + case css::awt::GradientStyle::GradientStyle_LINEAR: + return "LINEAR"; + + case css::awt::GradientStyle::GradientStyle_AXIAL: + return "AXIAL"; + + case css::awt::GradientStyle::GradientStyle_RADIAL: + return "RADIAL"; + + case css::awt::GradientStyle::GradientStyle_ELLIPTICAL: + return "ELLIPTICAL"; + + case css::awt::GradientStyle::GradientStyle_SQUARE: + return "SQUARE"; + + case css::awt::GradientStyle::GradientStyle_RECT: + return "RECT"; + + case css::awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE: + return "MAKE_FIXED_SIZE"; + } + + return ""; +} + +BGradient BGradient::fromJSON(std::u16string_view rJSON) +{ + StringMap aMap(lcl_jsonToStringMap(rJSON)); + return lcl_buildGradientFromStringMap(aMap); +} + +BGradient::BGradient() + : eStyle(css::awt::GradientStyle_LINEAR) + , aColorStops() + , nAngle(0) + , nBorder(0) + , nOfsX(50) + , nOfsY(50) + , nIntensStart(100) + , nIntensEnd(100) + , nStepCount(0) +{ + aColorStops.emplace_back(0.0, BColor(0.0, 0.0, 0.0)); // COL_BLACK + aColorStops.emplace_back(1.0, BColor(1.0, 1.0, 1.0)); // COL_WHITE +} + +BGradient::BGradient(const basegfx::BColorStops& rColorStops, css::awt::GradientStyle eTheStyle, + Degree10 nTheAngle, sal_uInt16 nXOfs, sal_uInt16 nYOfs, sal_uInt16 nTheBorder, + sal_uInt16 nStartIntens, sal_uInt16 nEndIntens, sal_uInt16 nSteps) + : eStyle(eTheStyle) + , aColorStops(rColorStops) + , nAngle(nTheAngle) + , nBorder(nTheBorder) + , nOfsX(nXOfs) + , nOfsY(nYOfs) + , nIntensStart(nStartIntens) + , nIntensEnd(nEndIntens) + , nStepCount(nSteps) +{ + SetColorStops(aColorStops); +} + +BGradient::BGradient(const css::awt::Gradient2& rGradient2) +{ + // set values + SetGradientStyle(rGradient2.Style); + SetAngle(Degree10(rGradient2.Angle)); + SetBorder(rGradient2.Border); + SetXOffset(rGradient2.XOffset); + SetYOffset(rGradient2.YOffset); + SetStartIntens(rGradient2.StartIntensity); + SetEndIntens(rGradient2.EndIntensity); + SetSteps(rGradient2.StepCount); + + // set ColorStops + aColorStops = BColorStops(rGradient2.ColorStops); + aColorStops.sortAndCorrect(); +} + +BGradient::BGradient(const css::uno::Any& rVal) + : BGradient() +{ + if (rVal.has<css::awt::Gradient2>()) + { + // we can use awt::Gradient2 directly + css::awt::Gradient2 aGradient2; + rVal >>= aGradient2; + + // set values + SetGradientStyle(aGradient2.Style); + SetAngle(Degree10(aGradient2.Angle)); + SetBorder(aGradient2.Border); + SetXOffset(aGradient2.XOffset); + SetYOffset(aGradient2.YOffset); + SetStartIntens(aGradient2.StartIntensity); + SetEndIntens(aGradient2.EndIntensity); + SetSteps(aGradient2.StepCount); + + // set ColorStops + aColorStops = BColorStops(aGradient2.ColorStops); + aColorStops.sortAndCorrect(); + } + else if (rVal.has<css::awt::Gradient>()) + { + // use awt::Gradient + css::awt::Gradient aGradient; + rVal >>= aGradient; + + // set values + SetGradientStyle(aGradient.Style); + SetAngle(Degree10(aGradient.Angle)); + SetBorder(aGradient.Border); + SetXOffset(aGradient.XOffset); + SetYOffset(aGradient.YOffset); + SetStartIntens(aGradient.StartIntensity); + SetEndIntens(aGradient.EndIntensity); + SetSteps(aGradient.StepCount); + + // complete data by creating ColorStops from fixe Start/EndColor + aColorStops = BColorStops{ + BColorStop(0.0, ColorToBColorConverter(aGradient.StartColor).getBColor()), + BColorStop(1.0, ColorToBColorConverter(aGradient.EndColor).getBColor()) + }; + } +} + +bool BGradient::operator==(const BGradient& rGradient) const +{ + return (eStyle == rGradient.eStyle && aColorStops == rGradient.aColorStops + && nAngle == rGradient.nAngle && nBorder == rGradient.nBorder + && nOfsX == rGradient.nOfsX && nOfsY == rGradient.nOfsY + && nIntensStart == rGradient.nIntensStart && nIntensEnd == rGradient.nIntensEnd + && nStepCount == rGradient.nStepCount); +} + +void BGradient::SetColorStops(const basegfx::BColorStops& rSteps) +{ + aColorStops = rSteps; + aColorStops.sortAndCorrect(); + if (aColorStops.empty()) + aColorStops.emplace_back(0.0, basegfx::BColor()); +} + +namespace +{ +OUString AsRGBHexString(const ColorToBColorConverter& rVal) +{ + std::stringstream ss; + ss << std::hex << std::setfill('0') << std::setw(6) << sal_uInt32(rVal); + return OUString::createFromAscii(ss.str()); +} +} + +boost::property_tree::ptree BGradient::dumpAsJSON() const +{ + boost::property_tree::ptree aTree; + + aTree.put("style", BGradient::GradientStyleToString(eStyle)); + const ColorToBColorConverter aStart(GetColorStops().front().getStopColor()); + aTree.put("startcolor", AsRGBHexString(aStart.GetRGBColor())); + const ColorToBColorConverter aEnd(GetColorStops().back().getStopColor()); + aTree.put("endcolor", AsRGBHexString(aEnd.GetRGBColor())); + aTree.put("angle", std::to_string(nAngle.get())); + aTree.put("border", std::to_string(nBorder)); + aTree.put("x", std::to_string(nOfsX)); + aTree.put("y", std::to_string(nOfsY)); + aTree.put("intensstart", std::to_string(nIntensStart)); + aTree.put("intensend", std::to_string(nIntensEnd)); + aTree.put("stepcount", std::to_string(nStepCount)); + + return aTree; +} + +css::awt::Gradient2 BGradient::getAsGradient2() const +{ + css::awt::Gradient2 aRetval; + + // standard values + aRetval.Style = GetGradientStyle(); + aRetval.Angle = static_cast<short>(GetAngle()); + aRetval.Border = GetBorder(); + aRetval.XOffset = GetXOffset(); + aRetval.YOffset = GetYOffset(); + aRetval.StartIntensity = GetStartIntens(); + aRetval.EndIntensity = GetEndIntens(); + aRetval.StepCount = GetSteps(); + + // for compatibility, still set StartColor/EndColor + // const basegfx::BColorStops& rColorStops(GetColorStops()); + aRetval.StartColor + = static_cast<sal_Int32>(ColorToBColorConverter(aColorStops.front().getStopColor())); + aRetval.EndColor + = static_cast<sal_Int32>(ColorToBColorConverter(aColorStops.back().getStopColor())); + + // fill ColorStops to extended Gradient2 + aRetval.ColorStops = aColorStops.getAsColorStopSequence(); + // fillColorStopSequenceFromColorStops(rGradient2.ColorStops, rColorStops); + + return aRetval; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx index 12bbebd39c11..c909108932cc 100644 --- a/basegfx/source/tools/gradienttools.cxx +++ b/basegfx/source/tools/gradienttools.cxx @@ -265,38 +265,6 @@ namespace basegfx namespace utils { - /* Internal helper to convert ::Color from tools::color.hxx to BColor - without the need to link against tools library. Be on the - safe side by using the same union - */ - namespace { - struct ColorToBColorConverter - { - union { - sal_uInt32 mValue; - struct { -#ifdef OSL_BIGENDIAN - sal_uInt8 T; - sal_uInt8 R; - sal_uInt8 G; - sal_uInt8 B; -#else - sal_uInt8 B; - sal_uInt8 G; - sal_uInt8 R; - sal_uInt8 T; -#endif - }; - }; - - ColorToBColorConverter(sal_uInt32 nColor) : mValue(nColor) { T=0; } - BColor getBColor() const - { - return BColor(R / 255.0, G / 255.0, B / 255.0); - } - }; - } - /// Tooling method to fill awt::Gradient2 from data contained in the given Any bool fillGradient2FromAny(css::awt::Gradient2& rGradient, const css::uno::Any& rVal) { @@ -327,11 +295,10 @@ namespace basegfx rGradient.StepCount = aTmp.StepCount; // complete data by creating ColorStops for awt::Gradient2 - fillColorStopSequenceFromColorStops( - rGradient.ColorStops, - ColorStops { - ColorStop(0.0, ColorToBColorConverter(aTmp.StartColor).getBColor()), - ColorStop(1.0, ColorToBColorConverter(aTmp.EndColor).getBColor()) }); + const BColorStops aTempColorStops { + BColorStop(0.0, ColorToBColorConverter(aTmp.StartColor).getBColor()), + BColorStop(1.0, ColorToBColorConverter(aTmp.EndColor).getBColor()) }; + rGradient.ColorStops = aTempColorStops.getAsColorStopSequence(); bRetval = true; } } @@ -355,12 +322,12 @@ namespace basegfx */ void prepareColorStops( const com::sun::star::awt::Gradient2& rGradient, - ColorStops& rColorStops, + BColorStops& rColorStops, BColor& rSingleColor) { - fillColorStopsFromGradient2(rColorStops, rGradient); + rColorStops = BColorStops(rGradient.ColorStops); - if (isSingleColor(rColorStops, rSingleColor)) + if (rColorStops.isSingleColor(rSingleColor)) { // when single color, preserve value in rSingleColor // and clear the ColorStops, done. @@ -371,15 +338,14 @@ namespace basegfx if (rGradient.StartIntensity != 100 || rGradient.EndIntensity != 100) { // apply 'old' blend stuff, blend against black - blendColorStopsToIntensity( - rColorStops, + rColorStops.blendToIntensity( 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)) + if (rColorStops.isSingleColor(rSingleColor)) { rColorStops.clear(); return; @@ -393,7 +359,7 @@ namespace basegfx // mechanism does not need entries at 0.0 and 1.0. // In case this is needed, do that in the caller const double fFactor(rGradient.Border * 0.01); - ColorStops aNewStops; + BColorStops aNewStops; for (const auto& candidate : rColorStops) { @@ -437,8 +403,8 @@ namespace basegfx 'FillTransparenceGradient' method (at import time). */ void synchronizeColorStops( - ColorStops& rColorStops, - ColorStops& rAlphaStops, + BColorStops& rColorStops, + BColorStops& rAlphaStops, const BColor& rSingleColor, const BColor& rSingleAlpha) { @@ -448,12 +414,12 @@ namespace basegfx { // 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) }; + rColorStops = BColorStops { + BColorStop(0.0, rSingleColor), + BColorStop(1.0, rSingleColor) }; + rAlphaStops = BColorStops { + BColorStop(0.0, rSingleAlpha), + BColorStop(1.0, rSingleAlpha) }; } else { @@ -489,8 +455,8 @@ namespace basegfx if (!bNeedToSyncronize) { // check for same StopOffsets - ColorStops::const_iterator aCurrColor(rColorStops.begin()); - ColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); + BColorStops::const_iterator aCurrColor(rColorStops.begin()); + BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); while (!bNeedToSyncronize && aCurrColor != rColorStops.end() && @@ -511,12 +477,12 @@ namespace basegfx 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; + BColorStops::const_iterator aCurrColor(rColorStops.begin()); + BColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); + BColorStops aNewColor; + BColorStops aNewAlpha; + BColorStops::BColorStopRange aColorStopRange; + BColorStops::BColorStopRange aAlphaStopRange; bool bRealChange(false); do { @@ -532,14 +498,14 @@ namespace basegfx { // copy color, create alpha aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor()); - aNewAlpha.emplace_back(fColorOff, utils::modifyBColor(rAlphaStops, fColorOff, 0, aAlphaStopRange)); + aNewAlpha.emplace_back(fColorOff, rAlphaStops.getInterpolatedBColor(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)); + aNewColor.emplace_back(fAlphaOff, rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange)); aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor()); bRealChange = true; aCurrAlpha++; @@ -556,14 +522,14 @@ namespace basegfx else if (bColor) { const double fColorOff(aCurrColor->getStopOffset()); - aNewAlpha.emplace_back(fColorOff, utils::modifyBColor(rAlphaStops, fColorOff, 0, aAlphaStopRange)); + aNewAlpha.emplace_back(fColorOff, rAlphaStops.getInterpolatedBColor(fColorOff, 0, aAlphaStopRange)); bRealChange = true; aCurrColor++; } else if (bAlpha) { const double fAlphaOff(aCurrAlpha->getStopOffset()); - aNewColor.emplace_back(fAlphaOff, utils::modifyBColor(rColorStops, fAlphaOff, 0, aColorStopRange)); + aNewColor.emplace_back(fAlphaOff, rColorStops.getInterpolatedBColor(fAlphaOff, 0, aColorStopRange)); bRealChange = true; aCurrAlpha++; } @@ -586,467 +552,6 @@ namespace basegfx } } - /* Tooling method to linearly blend the Colors contained in - a given ColorStop vector against a given Color using the - given intensity values. - The intensity values fStartIntensity, fEndIntensity are - in the range of [0.0 .. 1.0] and describe how much the - blend is supposed to be done at the start color position - and the end color position resprectively, where 0.0 means - to fully use the given BlendColor, 1.0 means to not change - the existing color in the ColorStop. - Every color entry in the given ColorStop is blended - relative to it's StopPosition, interpolating the - given intensities with the range [0.0 .. 1.0] to do so. - */ - void blendColorStopsToIntensity(ColorStops& rColorStops, double fStartIntensity, double fEndIntensity, const basegfx::BColor& rBlendColor) - { - // no entries, done - if (rColorStops.empty()) - return; - - // correct intensities (maybe assert when input was wrong) - fStartIntensity = std::max(std::min(1.0, fStartIntensity), 0.0); - fEndIntensity = std::max(std::min(1.0, fEndIntensity), 0.0); - - // all 100%, no real blend, done - if (basegfx::fTools::equal(fStartIntensity, 1.0) && basegfx::fTools::equal(fEndIntensity, 1.0)) - return; - - // blend relative to StopOffset position - for (auto& candidate : rColorStops) - { - const double fOffset(candidate.getStopOffset()); - const double fIntensity((fStartIntensity * (1.0 - fOffset)) + (fEndIntensity * fOffset)); - candidate = basegfx::ColorStop( - fOffset, - basegfx::interpolate(rBlendColor, candidate.getStopColor(), fIntensity)); - } - } - - /* Tooling method to check if a ColorStop vector is defined - by a single color. It returns true if this is the case. - If true is returned, rSingleColor contains that single - color for convenience. - NOTE: If no ColorStop is defined, a fallback to BColor-default - (which is black) and true will be returned - */ - bool isSingleColor(const ColorStops& rColorStops, BColor& rSingleColor) - { - if (rColorStops.empty()) - { - rSingleColor = BColor(); - return true; - } - - if (1 == rColorStops.size()) - { - rSingleColor = rColorStops.front().getStopColor(); - return true; - } - - rSingleColor = rColorStops.front().getStopColor(); - - for (auto const& rCandidate : rColorStops) - { - if (rCandidate.getStopColor() != rSingleColor) - return false; - } - - return true; - } - - /* Tooling method to reverse ColorStops, including offsets. - When also mirroring offsets a valid sort keeps valid. - */ - void reverseColorStops(ColorStops& rColorStops) - { - // can use std::reverse, but also need to adapt offset(s) - std::reverse(rColorStops.begin(), rColorStops.end()); - for (auto& candidate : rColorStops) - candidate = ColorStop(1.0 - candidate.getStopOffset(), candidate.getStopColor()); - } - - /* Tooling method to convert UNO API data to ColorStops. - This will try to extract ColorStop data from the given - awt::Gradient2. - */ - void fillColorStopsFromGradient2(ColorStops& rColorStops, const com::sun::star::awt::Gradient2& rGradient) - { - const sal_Int32 nLen(rGradient.ColorStops.getLength()); - - if (0 == nLen) - return; - - // we have ColorStops - rColorStops.clear(); - rColorStops.reserve(nLen); - const css::awt::ColorStop* pSourceColorStop(rGradient.ColorStops.getConstArray()); - - for (sal_Int32 a(0); a < nLen; a++, pSourceColorStop++) - { - rColorStops.emplace_back( - pSourceColorStop->StopOffset, - BColor(pSourceColorStop->StopColor.Red, pSourceColorStop->StopColor.Green, pSourceColorStop->StopColor.Blue)); - } - } - - /* 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. - */ - void fillColorStopSequenceFromColorStops(css::awt::ColorStopSequence& rColorStopSequence, const ColorStops& rColorStops) - { - // fill ColorStops to extended Gradient2 - rColorStopSequence.realloc(rColorStops.size()); - css::awt::ColorStop* pTargetColorStop(rColorStopSequence.getArray()); - - for (const auto& candidate : rColorStops) - { - pTargetColorStop->StopOffset = candidate.getStopOffset(); - pTargetColorStop->StopColor = css::rendering::RGBColor( - candidate.getStopColor().getRed(), - candidate.getStopColor().getGreen(), - candidate.getStopColor().getBlue()); - pTargetColorStop++; - } - } - - /* Tooling method that allows to replace the StartColor in a - vector of ColorStops. A vector in 'ordered state' is expected, - so you may use/have used sortAndCorrectColorStops, see below. - This method is for convenience & backwards compatibility, please - think about handling multi-colored gradients directly. - */ - void replaceStartColor(ColorStops& rColorStops, const BColor& rStart) - { - ColorStops::iterator a1stNonStartColor(rColorStops.begin()); - - // search for highest existing non-StartColor - while (a1stNonStartColor != rColorStops.end() && basegfx::fTools::lessOrEqual(a1stNonStartColor->getStopOffset(), 0.0)) - a1stNonStartColor++; - - // create new ColorStops by 1st adding new one and then all - // non-StartColor entries - ColorStops aNewColorStops; - - aNewColorStops.reserve(rColorStops.size() + 1); - aNewColorStops.emplace_back(0.0, rStart); - aNewColorStops.insert(aNewColorStops.end(), a1stNonStartColor, rColorStops.end()); - - // assign & done - rColorStops = aNewColorStops; - } - - /* Tooling method that allows to replace the EndColor in a - vector of ColorStops. A vector in 'ordered state' is expected, - so you may use/have used sortAndCorrectColorStops, see below. - This method is for convenience & backwards compatibility, please - think about handling multi-colored gradients directly. - */ - void replaceEndColor(ColorStops& rColorStops, const BColor& rEnd) - { - // erase all evtl. existing EndColor(s) - while (!rColorStops.empty() && basegfx::fTools::moreOrEqual(rColorStops.back().getStopOffset(), 1.0)) - rColorStops.pop_back(); - - // add at the end of existing ColorStops - rColorStops.emplace_back(1.0, rEnd); - } - - // Tooling method to quickly create a ColorStop vector for a given set of Start/EndColor - ColorStops createColorStopsFromStartEndColor(const BColor& rStart, const BColor& rEnd) - { - return ColorStops { - ColorStop(0.0, rStart), - ColorStop(1.0, rEnd) }; - } - - /* Tooling method to guarantee sort and correctness for - the given ColorStops vector. - A vector fulfilling these conditions is called to be - in 'ordered state'. - - At return, the following conditions are guaranteed: - - contains no ColorStops with offset < 0.0 (will - be removed) - - contains no ColorStops with offset > 1.0 (will - be removed) - - ColorStops with identical offsets are now allowed - - will be sorted from lowest offset to highest - - Some more notes: - - It can happen that the result is empty - - It is allowed to have consecutive entries with - the same color, this represents single-color - regions inside the gradient - - A entry with 0.0 is not required or forced, so - no 'StartColor' is technically required - - A entry with 1.0 is not required or forced, so - no 'EndColor' is technically required - - All this is done in one run (sort + O(N)) without - creating a copy of the data in any form - */ - void sortAndCorrectColorStops(ColorStops& rColorStops) - { - // no content, we are done - if (rColorStops.empty()) - return; - - if (1 == rColorStops.size()) - { - // no gradient at all, but preserve given color - // evtl. correct offset to be in valid range [0.0 .. 1.0] - // NOTE: This does not move it to 0.0 or 1.0, it *can* still - // be somewhere in-between what is allowed - rColorStops[0] = ColorStop( - std::max(0.0, std::min(1.0, rColorStops[0].getStopOffset())), - rColorStops[0].getStopColor()); - - // done - return; - } - - // start with sorting the input data. Remember that - // this preserves the order of equal entries, where - // equal is defined here by offset (see use operator==) - std::sort(rColorStops.begin(), rColorStops.end()); - - // prepare status values - size_t write(0); - - // use the paradigm of a band machine with two heads, read - // and write with write <= read all the time. Step over the - // data using read and check for valid entry. If valid, decide - // how to keep it - for (size_t read(0); read < rColorStops.size(); read++) - { - // get offset of entry at read position - double fOff(rColorStops[read].getStopOffset()); - - if (basegfx::fTools::less(fOff, 0.0) && read + 1 < rColorStops.size()) - { - // value < 0.0 and we have a next entry. check for gradient snippet - // containing 0.0 resp. StartColor - const double fOff2(rColorStops[read + 1].getStopOffset()); - - if (basegfx::fTools::more(fOff2, 0.0)) - { - // read is the start of a gradient snippet containing 0.0. Correct - // entry to StartColor, interpolate to correct StartColor - rColorStops[read] = ColorStop(0.0, basegfx::interpolate( - rColorStops[read].getStopColor(), - rColorStops[read + 1].getStopColor(), - (0.0 - fOff) / (fOff2 - fOff))); - - // adapt fOff - fOff = 0.0; - } - } - - // step over < 0 values, these are outside and will be removed - if (basegfx::fTools::less(fOff, 0.0)) - { - continue; - } - - if (basegfx::fTools::less(fOff, 1.0) && read + 1 < rColorStops.size()) - { - // value < 1.0 and we have a next entry. check for gradient snippet - // containing 1.0 resp. EndColor - const double fOff2(rColorStops[read + 1].getStopOffset()); - - if (basegfx::fTools::more(fOff2, 1.0)) - { - // read is the start of a gradient snippet containing 1.0. Correct - // next entry to EndColor, interpolate to correct EndColor - rColorStops[read + 1] = ColorStop(1.0, basegfx::interpolate( - rColorStops[read].getStopColor(), - rColorStops[read + 1].getStopColor(), - (1.0 - fOff) / (fOff2 - fOff))); - - // adapt fOff - fOff = 1.0; - } - } - - // step over > 1 values; even break, since all following - // entries will also be bigger due to being sorted, so done - if (basegfx::fTools::more(fOff, 1.0)) - { - break; - } - - // entry is valid value at read position - // copy if write target is empty (write at start) or when - // write target is different to read in color or offset - if (0 == write || !(rColorStops[read] == rColorStops[write-1])) - { - if (write != read) - { - // copy read to write backwards to close gaps - rColorStops[write] = rColorStops[read]; - } - - // always forward write position - write++; - } - } - - // correct size when length is reduced. write is always at - // last used position + 1 - if (rColorStops.size() > write) - { - if (0 == write) - { - // no valid entries at all, but not empty. This can only happen - // when all entries are below 0.0 or above 1.0 (else a gradient - // snippet spawning over both would have been detected) - if (basegfx::fTools::less(rColorStops.back().getStopOffset(), 0.0)) - { - // all outside too low, rescue last due to being closest to content - rColorStops = ColorStops { ColorStop(0.0, rColorStops.back().getStopColor()) }; - } - else // if (basegfx::fTools::more(rColorStops.front().getStopOffset(), 1.0)) - { - // all outside too high, rescue first due to being closest to content - rColorStops = ColorStops { ColorStop(1.0, rColorStops.front().getStopColor()) }; - } - } - else - { - rColorStops.resize(write); - } - } - } - - BColor modifyBColor( - const ColorStops& rColorStops, - double fScaler, - sal_uInt32 nRequestedSteps, - ColorStopRange& rLastColorStopRange) - { - // no color at all, done - if (rColorStops.empty()) - return BColor(); - - // outside range -> at start - const double fMin(rColorStops.front().getStopOffset()); - if (fScaler < fMin) - return rColorStops.front().getStopColor(); - - // outside range -> at end - const double fMax(rColorStops.back().getStopOffset()); - if (fScaler > fMax) - return rColorStops.back().getStopColor(); - - // special case for the 'classic' case with just two colors: - // we can optimize that and keep the speed/resources low - // by avoiding some calculations and an O(log(N)) array access - if (2 == rColorStops.size()) - { - if (fTools::equal(fMin, fMax)) - return rColorStops.front().getStopColor(); - - const basegfx::BColor aCStart(rColorStops.front().getStopColor()); - const basegfx::BColor aCEnd(rColorStops.back().getStopColor()); - const sal_uInt32 nSteps( - calculateNumberOfSteps( - nRequestedSteps, - aCStart, - aCEnd)); - - // we need to extend the interpolation to the local - // range of ColorStops. Despite having two ColorStops - // these are not necessarily at 0.0 and 1.0, so mabe - // not the classical Start/EndColor (what is allowed) - fScaler = (fScaler - fMin) / (fMax - fMin); - return basegfx::interpolate( - aCStart, - aCEnd, - nSteps > 1 ? floor(fScaler * nSteps) / double(nSteps - 1) : fScaler); - } - - // check if we need to newly populate the needed interpolation data - // or if we can re-use from last time. - // If this scope is not entered, we do not need the binary search. It's - // only a single buffered entry, and only used when more than three - // ColorStops exist, but makes a huge difference compared with acessing - // the sorted ColorStop vector each time. - // NOTE: with this simple change I get very high hit rates, e.g. rotating - // a donut with gradient test '1' hit rate is at 0.99909440357755486 - if (rLastColorStopRange.mfOffsetStart == rLastColorStopRange.mfOffsetEnd - || fScaler < rLastColorStopRange.mfOffsetStart - || fScaler > rLastColorStopRange.mfOffsetEnd) - { - // access needed spot in sorted array using binary search - // NOTE: This *seems* slow(er) when developing compared to just - // looping/accessing, but that's just due to the extensive - // debug test code created by the stl. In a pro version, - // all is good/fast as expected - const auto upperBound( - std::upper_bound( - rColorStops.begin(), - rColorStops.end(), - ColorStop(fScaler), - [](const ColorStop& x, const ColorStop& y) { return x.getStopOffset() < y.getStopOffset(); })); - - // no upper bound, done - if (rColorStops.end() == upperBound) - return rColorStops.back().getStopColor(); - - // lower bound is one entry back, access that - const auto lowerBound(upperBound - 1); - - // no lower bound, done - if (rColorStops.end() == lowerBound) - return rColorStops.back().getStopColor(); - - // we have lower and upper bound, get colors and offsets - rLastColorStopRange.maColorStart = lowerBound->getStopColor(); - rLastColorStopRange.maColorEnd = upperBound->getStopColor(); - rLastColorStopRange.mfOffsetStart = lowerBound->getStopOffset(); - rLastColorStopRange.mfOffsetEnd = upperBound->getStopOffset(); - } - - // when there are just two color steps this cannot happen, but when using - // a range of colors this *may* be used inside the range to represent - // single-colored regions inside a ColorRange. Use that color & done - if (rLastColorStopRange.maColorStart == rLastColorStopRange.maColorEnd) - return rLastColorStopRange.maColorStart; - - // calculate number of steps and adapted proportinal - // range for scaler in [0.0 .. 1.0] - const double fAdaptedScaler((fScaler - rLastColorStopRange.mfOffsetStart) / - (rLastColorStopRange.mfOffsetEnd - rLastColorStopRange.mfOffsetStart)); - const sal_uInt32 nSteps( - calculateNumberOfSteps( - nRequestedSteps, - rLastColorStopRange.maColorStart, - rLastColorStopRange.maColorEnd)); - - // interpolate & evtl. apply steps - return interpolate( - rLastColorStopRange.maColorStart, - rLastColorStopRange.maColorEnd, - nSteps > 1 ? floor(fAdaptedScaler * nSteps) / double(nSteps - 1) : fAdaptedScaler); - } - sal_uInt32 calculateNumberOfSteps( sal_uInt32 nRequestedSteps, const BColor& rStart, diff --git a/chart2/inc/pch/precompiled_chartcontroller.hxx b/chart2/inc/pch/precompiled_chartcontroller.hxx index 6839b807696d..ba4ccbddf15e 100644 --- a/chart2/inc/pch/precompiled_chartcontroller.hxx +++ b/chart2/inc/pch/precompiled_chartcontroller.hxx @@ -409,7 +409,6 @@ #include <svx/svxdllapi.h> #include <svx/xdash.hxx> #include <svx/xdef.hxx> -#include <svx/xgrad.hxx> #include <svx/xhatch.hxx> #include <svx/xit.hxx> #include <svx/xpoly.hxx> diff --git a/chart2/qa/extras/chart2import.cxx b/chart2/qa/extras/chart2import.cxx index e309fa912c3a..243597a060ab 100644 --- a/chart2/qa/extras/chart2import.cxx +++ b/chart2/qa/extras/chart2import.cxx @@ -805,9 +805,7 @@ void Chart2ImportTest::testBnc889755() uno::Reference<beans::XPropertySet> xShapeProps(xPage->getByIndex(4), uno::UNO_QUERY_THROW); awt::Gradient2 aTransparence; xShapeProps->getPropertyValue("FillTransparenceGradient") >>= aTransparence; - - basegfx::ColorStops aColorStops; - basegfx::utils::fillColorStopsFromGradient2(aColorStops, aTransparence); + const basegfx::BColorStops aColorStops(aTransparence.ColorStops); CPPUNIT_ASSERT_EQUAL(size_t(3), aColorStops.size()); CPPUNIT_ASSERT(basegfx::fTools::equal(aColorStops[0].getStopOffset(), 0.0)); @@ -850,9 +848,7 @@ void Chart2ImportTest::testTransparencyGradientValue() uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY); uno::Any rTransparenceValue = xTransparenceGradient->getByName(sTranspGradientName); CPPUNIT_ASSERT(rTransparenceValue >>= aTransparenceGradient); - - basegfx::ColorStops aColorStops; - basegfx::utils::fillColorStopsFromGradient2(aColorStops, aTransparenceGradient); + const basegfx::BColorStops aColorStops(aTransparenceGradient.ColorStops); // MCGR: Use the whole completely imported transparency gradient to check for correctness CPPUNIT_ASSERT_EQUAL(size_t(2), aColorStops.size()); diff --git a/chart2/source/controller/main/ChartController_Tools.cxx b/chart2/source/controller/main/ChartController_Tools.cxx index 8cd3fd99045c..b24ed653099b 100644 --- a/chart2/source/controller/main/ChartController_Tools.cxx +++ b/chart2/source/controller/main/ChartController_Tools.cxx @@ -74,7 +74,6 @@ #include <svx/unoapi.hxx> #include <svx/unopage.hxx> #include <svx/unoshape.hxx> -#include <svx/xgrad.hxx> #include <PropertyHelper.hxx> #include <LibreOfficeKit/LibreOfficeKitEnums.h> @@ -957,8 +956,8 @@ void ChartController::executeDispatch_FillColor(sal_uInt32 nColor) void ChartController::executeDispatch_FillGradient(std::u16string_view sJSONGradient) { - XGradient aXGradient = XGradient::fromJSON(sJSONGradient); - css::awt::Gradient aGradient = aXGradient.toGradientUNO(); + basegfx::BGradient aBGradient = basegfx::BGradient::fromJSON(sJSONGradient); + css::awt::Gradient aGradient = aBGradient.getAsGradient2(); try { @@ -973,9 +972,9 @@ void ChartController::executeDispatch_FillGradient(std::u16string_view sJSONGrad if( xPropSet.is() ) { OUString aPrefferedName = - OUString::number(static_cast<sal_Int32>(Color(aXGradient.GetColorStops().front().getStopColor()))) - + OUString::number(static_cast<sal_Int32>(Color(aXGradient.GetColorStops().back().getStopColor()))) - + OUString::number(static_cast<sal_Int32>(aXGradient.GetAngle().get())); + OUString::number(static_cast<sal_Int32>(Color(aBGradient.GetColorStops().front().getStopColor()))) + + OUString::number(static_cast<sal_Int32>(Color(aBGradient.GetColorStops().back().getStopColor()))) + + OUString::number(static_cast<sal_Int32>(aBGradient.GetAngle().get())); OUString aNewName = PropertyHelper::addGradientUniqueNameToTable(css::uno::Any(aGradient), xChartModel, diff --git a/cui/source/inc/cuitabarea.hxx b/cui/source/inc/cuitabarea.hxx index 55470628e0ae..b05ce7af425f 100644 --- a/cui/source/inc/cuitabarea.hxx ... etc. - the rest is truncated