summaryrefslogtreecommitdiff
path: root/filter
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2023-06-05 17:15:34 +0200
committerAndras Timar <andras.timar@collabora.com>2023-06-09 12:25:11 +0200
commit0f948fc383c716cd6583969b258805c3501589ba (patch)
tree1ef4bd2a717b0b6f2c57c1587336b3838136d5f2 /filter
parent2c8002555a9b288b756d1af0f69656bbace36e6c (diff)
MCGR: tdf#155479 repair gradient SVG export for MCGR
Unfortunately SVG export is based on metafiles and thus there is (in principle) no way to get the BGradient/ColorStop/MCGR data transfered as needed. For that, using UNO API to read the model or using B2DPrimitives would help - as is better for the export respectively. Since there is not the time to re-design SVG export I added this 'compromize' as a fix. It gets the needed data transported over the metafile (that part is the compromize). It then exports the MCGR data to SVG (at least - as was already there - if it's a linear/axial gradient). This happens now with all Gradient Stops when there is a MCGR gradient. That part is/will hopefully be re-usable if SVG export gets redesigned. I also added a handling for StepCount feature, so when used (in LO, others do not have that) 'hard' color stops get generated to make the gradient look identical for SVG export. Had to make adding of that extra-information in metafiles dependent on exporting really to SVG. There are 51 cases which use 'MetaActionType::COMMENT' which would potentially have to be adapted. Also added code to solve the problem for TransparencePrimitive2D at VclMetafileProcessor2D::processTransparencePrimitive2D. This will now - also only for SVG export - directly create the needed MetaFloatTransparentAction and add additional MCGR information. This will be used on SVG export to write a 'Mask' as was done before. This is now capable of creating fill MCGR-Masks in the sense that any number of TransparencyStops will be supported. Change-Id: Ic6d022714eae96b8fbc09e60652851ac5799b757 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152623 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'filter')
-rw-r--r--filter/source/svg/svgexport.cxx3
-rw-r--r--filter/source/svg/svgwriter.cxx198
-rw-r--r--filter/source/svg/svgwriter.hxx10
3 files changed, 124 insertions, 87 deletions
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index c27481baf79b..3114d80b592a 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -2380,7 +2380,8 @@ bool SVGFilter::implCreateObjectsFromShape( const Reference< css::drawing::XDraw
if( pObj )
{
- Graphic aGraphic(SdrExchangeView::GetObjGraphic(*pObj));
+ // tdf#155479 need to signal SVG export
+ Graphic aGraphic(SdrExchangeView::GetObjGraphic(*pObj, true));
// Writer graphic shapes are handled differently
if( mbWriterFilter && aGraphic.GetType() == GraphicType::NONE )
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 8c32dd7cfb0f..97e1c74c082b 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -39,6 +39,8 @@
#include <i18nlangtag/languagetag.hxx>
#include <o3tl/string_view.hxx>
#include <svx/svdomedia.hxx>
+#include <basegfx/utils/bgradient.hxx>
+#include <tools/vcompat.hxx>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XIndexReplace.hpp>
@@ -640,7 +642,7 @@ sal_Int32 SVGTextWriter::setTextPosition(const GDIMetaFile& rMtf, size_t& nCurAc
bEmpty = false;
mrActionWriter.StartMask(pA->GetPoint(), pA->GetSize(), pA->GetGradient(),
- nWriteFlags, &maTextOpacity);
+ nWriteFlags, pA->getSVGTransparencyColorStops(), &maTextOpacity);
}
}
break;
@@ -2312,12 +2314,12 @@ void SVGActionWriter::ImplWritePattern( const tools::PolyPolygon& rPolyPoly,
void SVGActionWriter::ImplWriteGradientEx( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient,
- sal_uInt32 nWriteFlags)
+ sal_uInt32 nWriteFlags, const basegfx::BColorStops* pColorStops)
{
if ( rGradient.GetStyle() == css::awt::GradientStyle_LINEAR ||
rGradient.GetStyle() == css::awt::GradientStyle_AXIAL )
{
- ImplWriteGradientLinear( rPolyPoly, rGradient );
+ ImplWriteGradientLinear( rPolyPoly, rGradient, pColorStops );
}
else
{
@@ -2327,7 +2329,7 @@ void SVGActionWriter::ImplWriteGradientEx( const tools::PolyPolygon& rPolyPoly,
void SVGActionWriter::ImplWriteGradientLinear( const tools::PolyPolygon& rPolyPoly,
- const Gradient& rGradient )
+ const Gradient& rGradient, const basegfx::BColorStops* pColorStops )
{
if( !rPolyPoly.Count() )
return;
@@ -2370,60 +2372,53 @@ void SVGActionWriter::ImplWriteGradientLinear( const tools::PolyPolygon& rPolyPo
{
SvXMLElementExport aElemLinearGradient( mrExport, XML_NAMESPACE_NONE, aXMLElemLinearGradient, true, true );
+ basegfx::BColorStops aColorStops;
- const Color aStartColor = ImplGetColorWithIntensity( rGradient.GetStartColor(), rGradient.GetStartIntensity() );
- const Color aEndColor = ImplGetColorWithIntensity( rGradient.GetEndColor(), rGradient.GetEndIntensity() );
- double fBorderOffset = rGradient.GetBorder() / 100.0;
- const sal_uInt16 nSteps = rGradient.GetSteps();
- if( rGradient.GetStyle() == css::awt::GradientStyle_LINEAR )
+ if (nullptr != pColorStops && pColorStops->size() > 1)
{
- // Emulate non-smooth gradient
- if( 0 < nSteps && nSteps < 100 )
- {
- double fOffsetStep = ( 1.0 - fBorderOffset ) / static_cast<double>(nSteps);
- for( sal_uInt16 i = 0; i < nSteps; i++ ) {
- Color aColor = ImplGetGradientColor( aStartColor, aEndColor, i / static_cast<double>(nSteps) );
- ImplWriteGradientStop( aColor, fBorderOffset + ( i + 1 ) * fOffsetStep );
- aColor = ImplGetGradientColor( aStartColor, aEndColor, ( i + 1 ) / static_cast<double>(nSteps) );
- ImplWriteGradientStop( aColor, fBorderOffset + ( i + 1 ) * fOffsetStep );
- }
- }
- else
- {
- ImplWriteGradientStop( aStartColor, fBorderOffset );
- ImplWriteGradientStop( aEndColor, 1.0 );
- }
+ // if we got the real colr stops, use them. That way we are
+ // now capable in the SVG export to export real multi color gradients
+ aColorStops = *pColorStops;
}
else
{
- fBorderOffset /= 2;
- // Emulate non-smooth gradient
- if( 0 < nSteps && nSteps < 100 )
- {
- double fOffsetStep = ( 0.5 - fBorderOffset ) / static_cast<double>(nSteps);
- // Upper half
- for( sal_uInt16 i = 0; i < nSteps; i++ )
- {
- Color aColor = ImplGetGradientColor( aEndColor, aStartColor, i / static_cast<double>(nSteps) );
- ImplWriteGradientStop( aColor, fBorderOffset + i * fOffsetStep );
- aColor = ImplGetGradientColor( aEndColor, aStartColor, (i + 1 ) / static_cast<double>(nSteps) );
- ImplWriteGradientStop( aColor, fBorderOffset + i * fOffsetStep );
- }
- // Lower half
- for( sal_uInt16 i = 0; i < nSteps; i++ )
- {
- Color aColor = ImplGetGradientColor( aStartColor, aEndColor, i / static_cast<double>(nSteps) );
- ImplWriteGradientStop( aColor, 0.5 + (i + 1) * fOffsetStep );
- aColor = ImplGetGradientColor( aStartColor, aEndColor, (i + 1 ) / static_cast<double>(nSteps) );
- ImplWriteGradientStop( aColor, 0.5 + (i + 1) * fOffsetStep );
- }
- }
- else
- {
- ImplWriteGradientStop( aEndColor, fBorderOffset );
- ImplWriteGradientStop( aStartColor, 0.5 );
- ImplWriteGradientStop( aEndColor, 1.0 - fBorderOffset );
- }
+ // else create color stops with 'old' start/endColor
+ aColorStops.emplace_back(0.0, rGradient.GetStartColor().getBColor());
+ aColorStops.emplace_back(1.0, rGradient.GetEndColor().getBColor());
+ }
+
+ // create a basegfx::BGradient with the info to be able to directly
+ // use the tooling it offers
+ basegfx::BGradient aGradient(
+ aColorStops,
+ rGradient.GetStyle(),
+ rGradient.GetAngle(),
+ rGradient.GetOfsX(),
+ rGradient.GetOfsY(),
+ rGradient.GetBorder(),
+ rGradient.GetStartIntensity(),
+ rGradient.GetEndIntensity(),
+ rGradient.GetSteps());
+
+ // apply Start/EndIntensity to the whole color stops - if used
+ aGradient.tryToApplyStartEndIntensity();
+
+ // apply border to color stops - if used
+ aGradient.tryToApplyBorder();
+
+ // convert from 'axial' to linear - if needed and used
+ aGradient.tryToApplyAxial();
+
+ // apply 'Steps' as hard gradient stops - if used
+ aGradient.tryToApplySteps();
+
+ // write prepared gradient stops
+ for (const auto& rCand : aGradient.GetColorStops())
+ {
+ ImplWriteGradientStop(
+ Color(rCand.getStopColor()),
+ rCand.getStopOffset());
+ // aStartColor, fBorderOffset );
}
}
}
@@ -2461,28 +2456,9 @@ Color SVGActionWriter::ImplGetColorWithIntensity( const Color& rColor,
}
-Color SVGActionWriter::ImplGetGradientColor( const Color& rStartColor,
- const Color& rEndColor,
- double fOffset )
-{
- tools::Long nRedStep = rEndColor.GetRed() - rStartColor.GetRed();
- tools::Long nNewRed = rStartColor.GetRed() + static_cast<tools::Long>( nRedStep * fOffset );
- nNewRed = ( nNewRed < 0 ) ? 0 : ( nNewRed > 0xFF) ? 0xFF : nNewRed;
-
- tools::Long nGreenStep = rEndColor.GetGreen() - rStartColor.GetGreen();
- tools::Long nNewGreen = rStartColor.GetGreen() + static_cast<tools::Long>( nGreenStep * fOffset );
- nNewGreen = ( nNewGreen < 0 ) ? 0 : ( nNewGreen > 0xFF) ? 0xFF : nNewGreen;
-
- tools::Long nBlueStep = rEndColor.GetBlue() - rStartColor.GetBlue();
- tools::Long nNewBlue = rStartColor.GetBlue() + static_cast<tools::Long>( nBlueStep * fOffset );
- nNewBlue = ( nNewBlue < 0 ) ? 0 : ( nNewBlue > 0xFF) ? 0xFF : nNewBlue;
-
- return Color( static_cast<sal_uInt8>(nNewRed), static_cast<sal_uInt8>(nNewGreen), static_cast<sal_uInt8>(nNewBlue) );
-}
-
void SVGActionWriter::StartMask(const Point& rDestPt, const Size& rDestSize,
const Gradient& rGradient, sal_uInt32 nWriteFlags,
- OUString* pTextFillOpacity)
+ const basegfx::BColorStops* pColorStops, OUString* pTextFillOpacity)
{
OUString aStyle;
if (rGradient.GetStartColor() == rGradient.GetEndColor())
@@ -2523,7 +2499,19 @@ void SVGActionWriter::StartMask(const Point& rDestPt, const Size& rDestSize,
aGradient.SetEndColor(aTmpColor);
aGradient.SetEndIntensity(nTmpIntensity);
- ImplWriteGradientEx(aPolyPolygon, aGradient, nWriteFlags);
+ // tdf#155479 prep local ColorStops. The code above
+ // implies that the ColorStops need to be reversed,
+ // so do so & use change of local ptr to represent this
+ basegfx::BColorStops aLocalColorStops;
+
+ if (nullptr != pColorStops)
+ {
+ aLocalColorStops = *pColorStops;
+ aLocalColorStops.reverseColorStops();
+ pColorStops = &aLocalColorStops;
+ }
+
+ ImplWriteGradientEx(aPolyPolygon, aGradient, nWriteFlags, pColorStops);
}
}
@@ -2533,7 +2521,7 @@ void SVGActionWriter::StartMask(const Point& rDestPt, const Size& rDestSize,
}
void SVGActionWriter::ImplWriteMask(GDIMetaFile& rMtf, const Point& rDestPt, const Size& rDestSize,
- const Gradient& rGradient, sal_uInt32 nWriteFlags)
+ const Gradient& rGradient, sal_uInt32 nWriteFlags, const basegfx::BColorStops* pColorStops)
{
Point aSrcPt(rMtf.GetPrefMapMode().GetOrigin());
const Size aSrcSize(rMtf.GetPrefSize());
@@ -2560,7 +2548,7 @@ void SVGActionWriter::ImplWriteMask(GDIMetaFile& rMtf, const Point& rDestPt, con
std::unique_ptr<SvXMLElementExport> pElemG;
if (!maTextWriter.hasTextOpacity())
{
- StartMask(rDestPt, rDestSize, rGradient, nWriteFlags);
+ StartMask(rDestPt, rDestSize, rGradient, nWriteFlags, pColorStops);
pElemG.reset(
new SvXMLElementExport(mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true));
}
@@ -3283,7 +3271,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
const tools::Polygon aRectPoly( pA->GetRect() );
const tools::PolyPolygon aRectPolyPoly( aRectPoly );
- ImplWriteGradientEx( aRectPolyPoly, pA->GetGradient(), nWriteFlags );
+ ImplWriteGradientEx( aRectPolyPoly, pA->GetGradient(), nWriteFlags, nullptr );
}
}
break;
@@ -3293,7 +3281,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
if( nWriteFlags & SVGWRITER_WRITE_FILL )
{
const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
- ImplWriteGradientEx( pA->GetPolyPolygon(), pA->GetGradient(), nWriteFlags );
+ ImplWriteGradientEx( pA->GetPolyPolygon(), pA->GetGradient(), nWriteFlags, nullptr );
}
}
break;
@@ -3340,7 +3328,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
ImplWriteMask( aTmpMtf, pA->GetPoint(), pA->GetSize(),
- pA->GetGradient(), nWriteFlags );
+ pA->GetGradient(), nWriteFlags, pA->getSVGTransparencyColorStops() );
}
}
break;
@@ -3374,7 +3362,55 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
{
const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
- if( ( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN") ) &&
+ if (pA->GetComment().equalsIgnoreAsciiCase("BGRAD_SEQ_BEGIN"))
+ {
+ // detect and use the new BGRAD_SEQ_* metafile comment actions
+ const MetaGradientExAction* pGradAction(nullptr);
+ bool bDone(false);
+
+ while (!bDone && (++nCurAction < nCount))
+ {
+ pAction = rMtf.GetAction(nCurAction);
+
+ if (MetaActionType::GRADIENTEX == pAction->GetType())
+ {
+ // remember the 'paint' data action
+ pGradAction = static_cast<const MetaGradientExAction*>(pAction);
+ }
+ else if (MetaActionType::COMMENT == pAction->GetType()
+ && static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("BGRAD_SEQ_END"))
+ {
+ // end action found
+ bDone = true;
+ }
+ }
+
+ if (nullptr != pGradAction)
+ {
+ // we have a complete actions sequence of BGRAD_SEQ_*, so we can now
+ // read the correct color stops here
+ basegfx::BColorStops aColorStops;
+ SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ);
+ VersionCompatRead aCompat(aMemStm);
+ sal_uInt16 nTmp;
+ double fOff, fR, fG, fB;
+ aMemStm.ReadUInt16( nTmp );
+
+ for (sal_uInt16 a(0); a < nTmp; a++)
+ {
+ aMemStm.ReadDouble(fOff);
+ aMemStm.ReadDouble(fR);
+ aMemStm.ReadDouble(fG);
+ aMemStm.ReadDouble(fB);
+
+ aColorStops.emplace_back(fOff, basegfx::BColor(fR, fG, fB));
+ }
+
+ // export with real Color Stops
+ ImplWriteGradientEx(pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), nWriteFlags, &aColorStops);
+ }
+ }
+ else if( ( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN") ) &&
( nWriteFlags & SVGWRITER_WRITE_FILL ) )
{
const MetaGradientExAction* pGradAction = nullptr;
@@ -3395,7 +3431,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
}
if( pGradAction )
- ImplWriteGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), nWriteFlags );
+ ImplWriteGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), nWriteFlags, nullptr );
}
else if( ( pA->GetComment().equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN") ) &&
( nWriteFlags & SVGWRITER_WRITE_FILL ) && !( nWriteFlags & SVGWRITER_NO_SHAPE_COMMENTS ) &&
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index 022c2585c5ab..4a84f3ae4d9f 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -41,6 +41,7 @@
#include <stack>
#include <unordered_map>
+namespace basegfx { class BColorStops; }
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::container;
@@ -340,12 +341,11 @@ private:
void ImplStartClipRegion(sal_Int32 nClipPathId);
void ImplEndClipRegion();
void ImplWriteClipPath( const tools::PolyPolygon& rPolyPoly );
- void ImplWriteGradientEx( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, sal_uInt32 nWriteFlags);
- void ImplWriteGradientLinear( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient );
+ void ImplWriteGradientEx( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, sal_uInt32 nWriteFlags, const basegfx::BColorStops* pColorStops);
+ void ImplWriteGradientLinear( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, const basegfx::BColorStops* pColorStops );
void ImplWriteGradientStop( const Color& rColor, double fOffset );
static Color ImplGetColorWithIntensity( const Color& rColor, sal_uInt16 nIntensity );
- static Color ImplGetGradientColor( const Color& rStartColor, const Color& rEndColor, double fOffset );
- void ImplWriteMask( GDIMetaFile& rMtf, const Point& rDestPt, const Size& rDestSize, const Gradient& rGradient, sal_uInt32 nWriteFlags );
+ void ImplWriteMask( GDIMetaFile& rMtf, const Point& rDestPt, const Size& rDestSize, const Gradient& rGradient, sal_uInt32 nWriteFlags, const basegfx::BColorStops* pColorStops);
void ImplWriteText( const Point& rPos, const OUString& rText, KernArraySpan pDXArray, tools::Long nWidth );
void ImplWriteText( const Point& rPos, const OUString& rText, KernArraySpan pDXArray, tools::Long nWidth, Color aTextColor );
void ImplWriteBmp( const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz, const Point& rSrcPt, const Size& rSrcSz, const css::uno::Reference<css::drawing::XShape>* pShape);
@@ -377,7 +377,7 @@ public:
void SetEmbeddedBitmapRefs( const MetaBitmapActionMap* pEmbeddedBitmapsMap );
void StartMask(const Point& rDestPt, const Size& rDestSize, const Gradient& rGradient,
- sal_uInt32 nWriteFlags, OUString* pTextStyle = nullptr);
+ sal_uInt32 nWriteFlags, const basegfx::BColorStops* pColorStops, OUString* pTextStyle = nullptr);
void SetPreviewMode(bool bState = true) { mbIsPreview = bState; }
};