diff options
author | Regina Henschel <rb.henschel@t-online.de> | 2022-01-13 13:26:25 +0100 |
---|---|---|
committer | Regina Henschel <rb.henschel@t-online.de> | 2022-02-17 14:14:01 +0100 |
commit | 157b027c4cfca2582b1c1bdb66992560084ac008 (patch) | |
tree | 41ae4086069acc86fb36ccbb23a1eb82bfa5117c /filter | |
parent | 424cd919c4234d6e6fd4da89acd90197771f7b9d (diff) |
tdf#145700 Improve lighting in extruded custom shapes
The fix tries to make rendering similar to MS Office.
The ODF standard follows closely the extrusion in RTF and MS binary
format. Rendering uses the 3D scene engine.
The main problem was, that the z-component of the direction was
interpreted with opposite sign. As result the maximum of a light was at
false position. Especially a direction from the observer to the object
has produced a light behind the shape and so looks as if light was off.
The wrong z-direction has produced lighting, which was less intensive
than in MS Office. To compensate that, a third light source was added
as workaround. That part is removed.
Second problem was wrong use of 3D-scene D3DMaterialSpecularIntensity
and D3DMaterialSpecular (= UI Specular color). That was not only wrong
in OOo but in my previous patch too.
D3DMaterialSpecularIntensity corresponds to MS property 'c3DShininess'.
Relationship is Intensity = 2^c3DShininess.
D3DMaterialSpecular is calculated from MS property c3DSpecularAmt and
and c3DKeyIntensity. The light source was missing, but needs to be
included, because c3DSpecularAmt is 'the ratio of incident to specular
light that is reflected on a shape'.
The old unit tests are adapted to this change.
MS gives no information how it softens a light in case of harsh=false.
ODF specifies it as 'implementation-defined'. The patch uses four
additional lights, which have directions in 60° angle to the original
light. The light intensity is distributed. That comes near to rendering
in MS Office. Changing our 3D engine to provide 'soft' lights was not
doable for me.
The way MS Office renders a 'metal' surface is different from ODF
specification. To distinguish the kinds, I have introduced a new
property MetalType. I have discussed it with the ODF TC (see minutes
from 2022-02-07) and got the advise to use namespaced values.
Therefore the datatype is not boolean.
The 'Surface' drop-down in the extrusion bar is changed to make the two
kinds of rendering 'Metal' available to the user.
If a user sets surface 'Metal' in the UI of MS Office, it sets not only
fc3DMetallic but reduces the value of c3DDiffuseAmt in addition. Our
3D-scene engine has the corresponding ODF attribute dr3d:diffuse-color
not implemented. To get a similar rendering I change the material color
of the 3D-objects as workaround.
Change-Id: Ia986b9c318b4c79688e0c0e2d858215b9d612fdc
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128449
Tested-by: Jenkins
Reviewed-by: Regina Henschel <rb.henschel@t-online.de>
Diffstat (limited to 'filter')
-rw-r--r-- | filter/source/msfilter/escherex.cxx | 9 | ||||
-rw-r--r-- | filter/source/msfilter/msdffimp.cxx | 121 |
2 files changed, 88 insertions, 42 deletions
diff --git a/filter/source/msfilter/escherex.cxx b/filter/source/msfilter/escherex.cxx index 9ea8ab91a7e0..3f817a72bcb1 100644 --- a/filter/source/msfilter/escherex.cxx +++ b/filter/source/msfilter/escherex.cxx @@ -91,6 +91,7 @@ #include <sal/log.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/numeric/ftools.hxx> #include <osl/diagnose.h> #include <algorithm> @@ -2858,7 +2859,11 @@ void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeT { double fExtrusionShininess = 0; if ( rrProp.Value >>= fExtrusionShininess ) - AddOpt( DFF_Prop_c3DShininess, static_cast<sal_Int32>( fExtrusionShininess * 655.36 ) ); + { + // ODF to MS Office conversion invers to msdffimp.cxx + fExtrusionShininess = basegfx::fround(fExtrusionShininess / 10.0); + AddOpt( DFF_Prop_c3DShininess, static_cast<sal_Int32>(fExtrusionShininess) ); + } } else if ( rrProp.Name == "Skew" ) { @@ -2875,7 +2880,7 @@ void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeT { double fExtrusionSpecularity = 0; if ( rrProp.Value >>= fExtrusionSpecularity ) - AddOpt( DFF_Prop_c3DSpecularAmt, static_cast<sal_Int32>( fExtrusionSpecularity * 1333 ) ); + AddOpt( DFF_Prop_c3DSpecularAmt, static_cast<sal_Int32>( fExtrusionSpecularity * 655.36 ) ); } else if ( rrProp.Name == "ProjectionMode" ) { diff --git a/filter/source/msfilter/msdffimp.cxx b/filter/source/msfilter/msdffimp.cxx index a0ecad074ac6..76813d38df24 100644 --- a/filter/source/msfilter/msdffimp.cxx +++ b/filter/source/msfilter/msdffimp.cxx @@ -149,6 +149,7 @@ #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp> #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> #include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp> #include <com/sun/star/beans/PropertyValues.hpp> #include <com/sun/star/beans/XPropertySetInfo.hpp> #include <com/sun/star/beans/XPropertySet.hpp> @@ -1675,14 +1676,20 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt aExtrusionPropVec.push_back( aProp ); // "Brightness" + // MS Office default 0x00004E20 16.16 FixedPoint, 20000/65536=0.30517, ODF default 33%. + // Thus must set value even if default. + double fBrightness = 20000.0; if ( IsProperty( DFF_Prop_c3DAmbientIntensity ) ) { - double fBrightness = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DAmbientIntensity, 0 )); - fBrightness /= 655.36; - aProp.Name = "Brightness"; - aProp.Value <<= fBrightness; - aExtrusionPropVec.push_back( aProp ); + // Value must be in range 0.0 to 1.0 in MS Office binary specification, but larger + // values are in fact interpreted. + fBrightness = GetPropertyValue( DFF_Prop_c3DAmbientIntensity, 0 ); } + fBrightness /= 655.36; + aProp.Name = "Brightness"; + aProp.Value <<= fBrightness; + aExtrusionPropVec.push_back( aProp ); + // "Depth" in 1/100mm if ( IsProperty( DFF_Prop_c3DExtrudeBackward ) || IsProperty( DFF_Prop_c3DExtrudeForward ) ) { @@ -1700,14 +1707,17 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt aExtrusionPropVec.push_back( aProp ); } // "Diffusion" + // ODF default is 0%, MS Office default is 100%. Thus must set value even if default. + double fDiffusion = 100; if ( IsProperty( DFF_Prop_c3DDiffuseAmt ) ) { - double fDiffusion = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DDiffuseAmt, 0 )); + fDiffusion = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DDiffuseAmt, 0 )); fDiffusion /= 655.36; - aProp.Name = "Diffusion"; - aProp.Value <<= fDiffusion; - aExtrusionPropVec.push_back( aProp ); } + aProp.Name = "Diffusion"; + aProp.Value <<= fDiffusion; + aExtrusionPropVec.push_back( aProp ); + // "NumberOfLineSegments" if ( IsProperty( DFF_Prop_c3DTolerance ) ) { @@ -1730,24 +1740,35 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt aProp.Name = "SecondLightHarsh"; aProp.Value <<= bExtrusionSecondLightHarsh; aExtrusionPropVec.push_back( aProp ); + // "FirstLightLevel" + // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%. + // Thus must set value even if default. + double fFirstLightLevel = 38000.0; if ( IsProperty( DFF_Prop_c3DKeyIntensity ) ) { - double fFirstLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyIntensity, 0 )); - fFirstLightLevel /= 655.36; - aProp.Name = "FirstLightLevel"; - aProp.Value <<= fFirstLightLevel; - aExtrusionPropVec.push_back( aProp ); + // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here. + fFirstLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyIntensity, 0 )); } + fFirstLightLevel /= 655.36; + aProp.Name = "FirstLightLevel"; + aProp.Value <<= fFirstLightLevel; + aExtrusionPropVec.push_back( aProp ); + // "SecondLightLevel" + // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%. + // Thus must set value even if default. + double fSecondLightLevel = 38000.0; if ( IsProperty( DFF_Prop_c3DFillIntensity ) ) { - double fSecondLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillIntensity, 0 )); - fSecondLightLevel /= 655.36; - aProp.Name = "SecondLightLevel"; - aProp.Value <<= fSecondLightLevel; - aExtrusionPropVec.push_back( aProp ); + // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here. + fSecondLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillIntensity, 0 )); } + fSecondLightLevel /= 655.36; + aProp.Name = "SecondLightLevel"; + aProp.Value <<= fSecondLightLevel; + aExtrusionPropVec.push_back( aProp ); + // "FirstLightDirection" if ( IsProperty( DFF_Prop_c3DKeyX ) || IsProperty( DFF_Prop_c3DKeyY ) || IsProperty( DFF_Prop_c3DKeyZ ) ) { @@ -1776,6 +1797,10 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt aProp.Name = "Metal"; aProp.Value <<= bExtrusionMetal; aExtrusionPropVec.push_back( aProp ); + aProp.Name = "MetalType"; + aProp.Value <<= css::drawing::EnhancedCustomShapeMetalType::MetalMSCompatible; + aExtrusionPropVec.push_back(aProp); + // "ShadeMode" if ( IsProperty( DFF_Prop_c3DRenderMode ) ) { @@ -1788,7 +1813,7 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt aProp.Value <<= eExtrusionShadeMode; aExtrusionPropVec.push_back( aProp ); } - // "RotateAngle" in Grad + // "RotateAngle" in Degree if ( IsProperty( DFF_Prop_c3DXRotationAngle ) || IsProperty( DFF_Prop_c3DYRotationAngle ) ) { double fAngleX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXRotationAngle, 0 ))) / 65536.0; @@ -1821,34 +1846,44 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt } } // "Shininess" + // MS Office default 5, ODF default 50%. if ( IsProperty( DFF_Prop_c3DShininess ) ) { double fShininess = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DShininess, 0 )); - fShininess /= 655.36; + fShininess *= 10.0; // error in [MS ODRAW] (2021), type is not FixedPoint but long. aProp.Name = "Shininess"; aProp.Value <<= fShininess; aExtrusionPropVec.push_back( aProp ); } + // "Skew" + // MS Office angle file value is 16.16 FixedPoint, default 0xFF790000, + // -8847360/65536=-135, ODF default 45. Thus must set value even if default. + double fSkewAngle = -135.0; + // MS Office amount file value is signed integer in range 0xFFFFFF9C to 0x00000064, + // default 0x00000032, ODF default 50.0 + double fSkewAmount = 50.0; if ( IsProperty( DFF_Prop_c3DSkewAmount ) || IsProperty( DFF_Prop_c3DSkewAngle ) ) { - double fSkewAmount = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAmount, 50 )); - double fSkewAngle = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAngle, sal::static_int_cast< sal_uInt32 >(-135 * 65536) ))) / 65536.0; + fSkewAmount = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAmount, 50 )); + fSkewAngle = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAngle, sal::static_int_cast< sal_uInt32 >(-135 * 65536) )); + fSkewAngle /= 65536.0; + } + EnhancedCustomShapeParameterPair aSkewPair; + aSkewPair.First.Value <<= fSkewAmount; + aSkewPair.First.Type = EnhancedCustomShapeParameterType::NORMAL; + aSkewPair.Second.Value <<= fSkewAngle; + aSkewPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL; + aProp.Name = "Skew"; + aProp.Value <<= aSkewPair; + aExtrusionPropVec.push_back( aProp ); - EnhancedCustomShapeParameterPair aSkewPair; - aSkewPair.First.Value <<= fSkewAmount; - aSkewPair.First.Type = EnhancedCustomShapeParameterType::NORMAL; - aSkewPair.Second.Value <<= fSkewAngle; - aSkewPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL; - aProp.Name = "Skew"; - aProp.Value <<= aSkewPair; - aExtrusionPropVec.push_back( aProp ); - } // "Specularity" + // Type Fixed point 16.16, percent in API if ( IsProperty( DFF_Prop_c3DSpecularAmt ) ) { double fSpecularity = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSpecularAmt, 0 )); - fSpecularity /= 1333; + fSpecularity /= 655.36; aProp.Name = "Specularity"; aProp.Value <<= fSpecularity; aExtrusionPropVec.push_back( aProp ); @@ -1860,16 +1895,22 @@ void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxIt aExtrusionPropVec.push_back( aProp ); // "ViewPoint" in 1/100mm + // MS Office default 1250000 EMU=3472.222 Hmm, ODF default 3.5cm + // Thus must set value even if default. + double fViewX = 1250000.0 / 360.0; + double fViewY = -1250000.0 / 360.0;; + double fViewZ = 9000000.0 / 360.0; if ( IsProperty( DFF_Prop_c3DXViewpoint ) || IsProperty( DFF_Prop_c3DYViewpoint ) || IsProperty( DFF_Prop_c3DZViewpoint ) ) { - double fViewX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXViewpoint, 1250000 ))) / 360.0; - double fViewY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DYViewpoint, sal_uInt32(-1250000) )))/ 360.0; - double fViewZ = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DZViewpoint, 9000000 ))) / 360.0; - css::drawing::Position3D aExtrusionViewPoint( fViewX, fViewY, fViewZ ); - aProp.Name = "ViewPoint"; - aProp.Value <<= aExtrusionViewPoint; - aExtrusionPropVec.push_back( aProp ); + fViewX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXViewpoint, 1250000 ))) / 360.0; + fViewY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DYViewpoint, sal_uInt32(-1250000) )))/ 360.0; + fViewZ = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DZViewpoint, 9000000 ))) / 360.0; } + css::drawing::Position3D aExtrusionViewPoint( fViewX, fViewY, fViewZ ); + aProp.Name = "ViewPoint"; + aProp.Value <<= aExtrusionViewPoint; + aExtrusionPropVec.push_back( aProp ); + // "Origin" if ( IsProperty( DFF_Prop_c3DOriginX ) || IsProperty( DFF_Prop_c3DOriginY ) ) { |