basegfx/source/tools/gradienttools.cxx | 32 ++++++ include/basegfx/utils/gradienttools.hxx | 9 + include/oox/helper/modelobjecthelper.hxx | 3 oox/source/drawingml/fillproperties.cxx | 110 ++++++++++++++++++++- oox/source/drawingml/shapepropertymap.cxx | 16 ++- oox/source/helper/modelobjecthelper.cxx | 12 ++ svx/source/sdr/primitive2d/sdrattributecreator.cxx | 21 +--- 7 files changed, 184 insertions(+), 19 deletions(-)
New commits: commit ce86e5b4a8d54eb55fdde7756ad6fde6e6967d55 Author: Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de> AuthorDate: Wed Mar 29 11:04:27 2023 +0200 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Sun Apr 2 13:19:27 2023 +0200 MCGR: 1st additions to OOXML MCGR import This change provides 1st changes to get Gradients with muti color stops imported from MSO in the oox import filter. It supports currently multiple ColorStops and transparency. Also 'border'(s) should work, but -remember- this is work in progress. Since it is work in progress it is currently and temporaily secured by ENV VAR "MCGR_TEST=0", so when not using this the master version will not be touched at all. The number defines various ColorStop tests, 0 for none, but some changes are active, e.g. MSO import. You may try 1 or 16 to see all your Gradients hard replaced by something using that feature. I will take care fo cleaning this up again when the feature progresses/gets complete. Change-Id: I92e10d8cd5150733741a6def20a542abf97bd903 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149682 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx index a98eeddf641c..49cf831da262 100644 --- a/basegfx/source/tools/gradienttools.cxx +++ b/basegfx/source/tools/gradienttools.cxx @@ -264,6 +264,38 @@ namespace basegfx namespace utils { + /* 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. */ diff --git a/include/basegfx/utils/gradienttools.hxx b/include/basegfx/utils/gradienttools.hxx index 33f87717528b..c7123bc81db3 100644 --- a/include/basegfx/utils/gradienttools.hxx +++ b/include/basegfx/utils/gradienttools.hxx @@ -196,6 +196,15 @@ namespace basegfx namespace utils { + /* 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 + */ + BASEGFX_DLLPUBLIC bool isSingleColor(const ColorStops& rColorStops, BColor& rSingleColor); + /* Tooling method to reverse ColorStops, including offsets. When also mirroring offsets a valid sort keeps valid. */ diff --git a/include/oox/helper/modelobjecthelper.hxx b/include/oox/helper/modelobjecthelper.hxx index 9aba2538fdc4..6d4b1fea1925 100644 --- a/include/oox/helper/modelobjecthelper.hxx +++ b/include/oox/helper/modelobjecthelper.hxx @@ -28,6 +28,7 @@ namespace com::sun::star { namespace awt { struct Gradient; + struct Gradient2; class XBitmap; } namespace graphic { class XGraphic; } namespace container { class XNameContainer; } @@ -102,8 +103,10 @@ public: /** Inserts a new named fill gradient, returns the gradient name, based on an internal constant name with a new unused index appended. */ + OUString insertFillGradient( const css::awt::Gradient2& rGradient ); OUString insertFillGradient( const css::awt::Gradient& rGradient ); + OUString insertTransGrandient( const css::awt::Gradient2& rGradient ); OUString insertTransGrandient( const css::awt::Gradient& rGradient ); OUString insertFillHatch( const css::drawing::Hatch& rHatch ); diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx index 98a048d7f8ac..fd3ba99b2c3f 100644 --- a/oox/source/drawingml/fillproperties.cxx +++ b/oox/source/drawingml/fillproperties.cxx @@ -27,9 +27,10 @@ #include <vcl/BitmapFilter.hxx> #include <vcl/BitmapMonochromeFilter.hxx> #include <docmodel/uno/UnoThemeColor.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <com/sun/star/beans/XPropertySet.hpp> -#include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/awt/Gradient2.hpp> #include <com/sun/star/text/GraphicCrop.hpp> #include <com/sun/star/awt/Size.hpp> #include <com/sun/star/drawing/BitmapMode.hpp> @@ -479,7 +480,112 @@ void FillProperties::pushToPropMap(ShapePropertyMap& rPropMap, const GraphicHelp case XML_gradFill: // do not create gradient struct if property is not supported... - if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) ) + static bool bMCGR(nullptr != std::getenv("MCGR_TEST")); + + if( bMCGR && rPropMap.supportsProperty( ShapeProperty::FillGradient ) ) + { + // use awt::Gradient2, prepare ColorStops + awt::Gradient2 aGradient; + basegfx::ColorStops aColorStops; + basegfx::ColorStops aTransparencyStops; + bool bContainsTransparency(false); + + // set defaults + aGradient.Angle = 900; + aGradient.StartIntensity = 100; + aGradient.EndIntensity = 100; + aGradient.Style = awt::GradientStyle_LINEAR; + + // convert to ColorStops, check for contained transparency + for (const auto& rCandidate : maGradientProps.maGradientStops) + { + const ::Color aColor(rCandidate.second.getColor(rGraphicHelper, nPhClr)); + aColorStops.emplace_back(rCandidate.first, aColor.getBColor()); + bContainsTransparency = bContainsTransparency || rCandidate.second.hasTransparency(); + } + + // if we have transparency, convert to ColorStops + if (bContainsTransparency) + { + for (const auto& rCandidate : maGradientProps.maGradientStops) + { + const double fTrans(rCandidate.second.getTransparency() * (1.0/100.0)); + aTransparencyStops.emplace_back(rCandidate.first, basegfx::BColor(fTrans, fTrans, fTrans)); + } + } + + // "rotate with shape" set to false -> do not rotate + if (!maGradientProps.moRotateWithShape.value_or(true)) + { + nShapeRotation = 0; + } + + if (maGradientProps.moGradientPath.has_value()) + { + IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.value_or( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) ); + sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2; + aGradient.XOffset = getLimitedValue<sal_Int16, sal_Int32>( + nCenterX / PER_PERCENT, 0, 100); + sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2; + aGradient.YOffset = getLimitedValue<sal_Int16, sal_Int32>( + nCenterY / PER_PERCENT, 0, 100); + + if( maGradientProps.moGradientPath.value() == XML_circle ) + { + // Style should be radial at least when the horizontal center is at 50%. + // Otherwise import as a linear gradient, because it is the most similar to the MSO radial style. + // aGradient.Style = awt::GradientStyle_LINEAR; + if( aGradient.XOffset == 100 && aGradient.YOffset == 100 ) + aGradient.Angle = 450; + else if( aGradient.XOffset == 0 && aGradient.YOffset == 100 ) + aGradient.Angle = 3150; + else if( aGradient.XOffset == 100 && aGradient.YOffset == 0 ) + aGradient.Angle = 1350; + else if( aGradient.XOffset == 0 && aGradient.YOffset == 0 ) + aGradient.Angle = 2250; + else + aGradient.Style = awt::GradientStyle_RADIAL; + } + else + { + aGradient.Style = awt::GradientStyle_RECT; + } + + basegfx::utils::reverseColorStops(aColorStops); + basegfx::utils::reverseColorStops(aTransparencyStops); + } + else if (!maGradientProps.maGradientStops.empty()) + { + // aGradient.Style = awt::GradientStyle_LINEAR; + sal_Int32 nShadeAngle = maGradientProps.moShadeAngle.value_or( 0 ); + // Adjust for flips + if ( bFlipH ) + nShadeAngle = 180*60000 - nShadeAngle; + if ( bFlipV ) + nShadeAngle = -nShadeAngle; + const sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation; + // convert DrawingML angle (in 1/60000 degrees) to API angle (in 1/10 degrees) + aGradient.Angle = static_cast< sal_Int16 >( (8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 ); + } + + // set ColorStops using UNO API + basegfx::utils::fillColorStopSequenceFromColorStops(aGradient.ColorStops, aColorStops); + + // push gradient or named gradient to property map + if (rPropMap.setProperty(ShapeProperty::FillGradient, aGradient)) + { + eFillStyle = FillStyle_GRADIENT; + } + + // push gradient transparency to property map if it exists + if (!aTransparencyStops.empty()) + { + basegfx::utils::fillColorStopSequenceFromColorStops(aGradient.ColorStops, aTransparencyStops); + rPropMap.setProperty(ShapeProperty::GradientTransparency, aGradient); + } + } + + if( !bMCGR && rPropMap.supportsProperty( ShapeProperty::FillGradient ) ) { sal_Int32 nEndTrans = 0; sal_Int32 nStartTrans = 0; diff --git a/oox/source/drawingml/shapepropertymap.cxx b/oox/source/drawingml/shapepropertymap.cxx index 57014b4780a6..253b9259f7aa 100644 --- a/oox/source/drawingml/shapepropertymap.cxx +++ b/oox/source/drawingml/shapepropertymap.cxx @@ -19,7 +19,7 @@ #include <oox/drawingml/shapepropertymap.hxx> -#include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/awt/Gradient2.hpp> #include <com/sun/star/beans/NamedValue.hpp> #include <com/sun/star/drawing/LineDash.hpp> #include <com/sun/star/drawing/Hatch.hpp> @@ -166,7 +166,12 @@ bool ShapePropertyMap::setFillGradient( sal_Int32 nPropId, const Any& rValue ) return setAnyProperty( nPropId, rValue ); // create named gradient and push its name - if( rValue.has< awt::Gradient >() ) + if( rValue.has< awt::Gradient2 >() ) + { + OUString aGradientName = mrModelObjHelper.insertFillGradient( rValue.get< awt::Gradient2 >() ); + return !aGradientName.isEmpty() && setProperty( nPropId, aGradientName ); + } + else if( rValue.has< awt::Gradient >() ) { OUString aGradientName = mrModelObjHelper.insertFillGradient( rValue.get< awt::Gradient >() ); return !aGradientName.isEmpty() && setProperty( nPropId, aGradientName ); @@ -194,7 +199,12 @@ bool ShapePropertyMap::setFillHatch( sal_Int32 nPropId, const Any& rValue ) bool ShapePropertyMap::setGradientTrans( sal_Int32 nPropId, const Any& rValue ) { // create named gradient and push its name - if( rValue.has< awt::Gradient >() ) + if( rValue.has< awt::Gradient2 >() ) + { + OUString aGradientName = mrModelObjHelper.insertTransGrandient( rValue.get< awt::Gradient2 >() ); + return !aGradientName.isEmpty() && setProperty( nPropId, aGradientName ); + } + else if( rValue.has< awt::Gradient >() ) { OUString aGradientName = mrModelObjHelper.insertTransGrandient( rValue.get< awt::Gradient >() ); return !aGradientName.isEmpty() && setProperty( nPropId, aGradientName ); diff --git a/oox/source/helper/modelobjecthelper.cxx b/oox/source/helper/modelobjecthelper.cxx index b68af8084fe0..e721d2ef3413 100644 --- a/oox/source/helper/modelobjecthelper.cxx +++ b/oox/source/helper/modelobjecthelper.cxx @@ -19,7 +19,7 @@ #include <oox/helper/modelobjecthelper.hxx> -#include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/awt/Gradient2.hpp> #include <com/sun/star/container/XNameContainer.hpp> #include <com/sun/star/drawing/LineDash.hpp> #include <com/sun/star/drawing/Hatch.hpp> @@ -123,11 +123,21 @@ OUString ModelObjectHelper::insertLineDash( const LineDash& rDash ) return maDashContainer.insertObject( gaDashNameBase, Any( rDash ), true ); } +OUString ModelObjectHelper::insertFillGradient( const awt::Gradient2& rGradient ) +{ + return maGradientContainer.insertObject( gaGradientNameBase, Any( rGradient ), true ); +} + OUString ModelObjectHelper::insertFillGradient( const awt::Gradient& rGradient ) { return maGradientContainer.insertObject( gaGradientNameBase, Any( rGradient ), true ); } +OUString ModelObjectHelper::insertTransGrandient( const awt::Gradient2& rGradient ) +{ + return maTransGradContainer.insertObject( gaTransGradNameBase, Any( rGradient ), true ); +} + OUString ModelObjectHelper::insertTransGrandient( const awt::Gradient& rGradient ) { return maTransGradContainer.insertObject( gaTransGradNameBase, Any( rGradient ), true ); diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx index a7670606adff..813c9884bc9b 100644 --- a/svx/source/sdr/primitive2d/sdrattributecreator.cxx +++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx @@ -444,9 +444,9 @@ namespace drawinglayer::primitive2d && pGradientItem->IsEnabled()) { const XGradient& rGradient = pGradientItem->GetGradientValue(); - const sal_uInt8 nStartLuminance(Color(rGradient.GetColorStops().front().getStopColor()).GetLuminance()); - const sal_uInt8 nEndLuminance(Color(rGradient.GetColorStops().back().getStopColor()).GetLuminance()); - const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance); + basegfx::BColor aSingleColor; + const bool bSingleColor(basegfx::utils::isSingleColor(rGradient.GetColorStops(), aSingleColor)); + const bool bCompletelyTransparent(bSingleColor && basegfx::fTools::equal(aSingleColor.luminance(), 1.0)); if(bCompletelyTransparent) { @@ -921,10 +921,10 @@ namespace drawinglayer::primitive2d { // test if float transparence is completely transparent const XGradient& rGradient = pGradientItem->GetGradientValue(); - const sal_uInt8 nStartLuminance(Color(rGradient.GetColorStops().front().getStopColor()).GetLuminance()); - const sal_uInt8 nEndLuminance(Color(rGradient.GetColorStops().back().getStopColor()).GetLuminance()); - const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance); - const bool bNotTransparent(0x00 == nStartLuminance && 0x00 == nEndLuminance); + basegfx::BColor aSingleColor; + const bool bSingleColor(basegfx::utils::isSingleColor(rGradient.GetColorStops(), aSingleColor)); + const bool bCompletelyTransparent(bSingleColor && basegfx::fTools::equal(aSingleColor.luminance(), 1.0)); + const bool bNotTransparent(bSingleColor && basegfx::fTools::equalZero(aSingleColor.luminance())); // create nothing when completely transparent: This case is already checked for the // normal fill attributes, XFILL_NONE will be used. @@ -932,18 +932,13 @@ namespace drawinglayer::primitive2d // Both cases are optimizations, always creating FillGradientAttribute will work, too if(!bNotTransparent && !bCompletelyTransparent) { - const double fStartLum(nStartLuminance / 255.0); - const double fEndLum(nEndLuminance / 255.0); - return attribute::FillGradientAttribute( XGradientStyleToGradientStyle(rGradient.GetGradientStyle()), static_cast<double>(rGradient.GetBorder()) * 0.01, static_cast<double>(rGradient.GetXOffset()) * 0.01, static_cast<double>(rGradient.GetYOffset()) * 0.01, toRadians(rGradient.GetAngle()), - basegfx::utils::createColorStopsFromStartEndColor( - basegfx::BColor(fStartLum, fStartLum, fStartLum), - basegfx::BColor(fEndLum, fEndLum, fEndLum))); + rGradient.GetColorStops()); } }