diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx index 226e62d7eec4..c6ff38df3aef 100644 --- a/cppcanvas/source/mtfrenderer/implrenderer.cxx +++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx @@ -1662,7 +1662,7 @@ namespace cppcanvas::internal SvMemoryStream aMemStm( const_cast(pData), pAct->GetDataSize(), StreamMode::READ ); SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + ReadSvtGraphicFill( aMemStm, aFill, nullptr ); // TODO(P2): Also handle gradients and // hatches like this diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index 9b536bcbd3d7..9954755958ee 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -290,26 +290,28 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient( o_rVCLGradient.SetStyle(rFiGrAtt.getStyle()); } -void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill) +void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill, const basegfx::BColorStops* pColorStops) { if (pSvtGraphicFill && !mnSvtGraphicFillCount) { SvMemoryStream aMemStm; - WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill); + WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill, pColorStops); mpMetaFile->AddAction(new MetaCommentAction( - "XPATHFILL_SEQ_BEGIN", 0, static_cast(aMemStm.GetData()), + nullptr == pColorStops ? "XPATHFILL_SEQ_BEGIN" : "XGRAD_SEQ_BEGIN", + 0, static_cast(aMemStm.GetData()), aMemStm.TellEnd())); mnSvtGraphicFillCount++; } } -void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill) +void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill, const basegfx::BColorStops* pColorStops) { if (pSvtGraphicFill && mnSvtGraphicFillCount) { mnSvtGraphicFillCount--; - mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END")); + mpMetaFile->AddAction(new MetaCommentAction( + nullptr == pColorStops ? "XPATHFILL_SEQ_END" : "XGRAD_SEQ_END")); } } @@ -1962,61 +1964,82 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D( void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate) { - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - - maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + bool useDecompose(false); - if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) + if (!useDecompose) { - // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly. - // This is because VCL Gradient mechanism does *not* support to rotate the gradient - // with objects and this case is not expressible in a Metafile (and cannot be added - // since the FileFormats used, e.g. *.wmf, do not support it either). - // Such cases happen when a graphic object uses a Metafile as graphic information or - // a fill style definition uses a Metafile. In this cases the graphic content is - // rotated with the graphic or filled object; this is not supported by the target - // format of this conversion renderer - Metafiles. - // To solve this, not a Gradient is written, but the decomposition of this object - // is written to the Metafile. This is the PolyPolygons building the gradient fill. - // These will need more space and time, but the result will be as if the Gradient - // was rotated with the object. - // This mechanism is used by all exporters still not using Primitives (e.g. Print, - // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile - // transfers. One more reason to *change* these to primitives. - // BTW: One more example how useful the principles of primitives are; the decomposition - // is by definition a simpler, maybe more expensive representation of the same content. - process(rGradientCandidate); - return; + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + + maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + + // detect if transformation is rotated, sheared or mirrored in X and/or Y + if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX) + || aScale.getX() < 0.0 || aScale.getY() < 0.0) + { + // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly. + // This is because VCL Gradient mechanism does *not* support to rotate the gradient + // with objects and this case is not expressible in a Metafile (and cannot be added + // since the FileFormats used, e.g. *.wmf, do not support it either). + // Such cases happen when a graphic object uses a Metafile as graphic information or + // a fill style definition uses a Metafile. In this cases the graphic content is + // rotated with the graphic or filled object; this is not supported by the target + // format of this conversion renderer - Metafiles. + // To solve this, not a Gradient is written, but the decomposition of this object + // is written to the Metafile. This is the PolyPolygons building the gradient fill. + // These will need more space and time, but the result will be as if the Gradient + // was rotated with the object. + // This mechanism is used by all exporters still not using Primitives (e.g. Print, + // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile + // transfers. One more reason to *change* these to primitives. + // BTW: One more example how useful the principles of primitives are; the decomposition + // is by definition a simpler, maybe more expensive representation of the same content. + useDecompose = true; + // process(rGradientCandidate); + // return; + } } // tdf#150551 for PDF export, use the decomposition for better gradient visualization - if (nullptr != mpPDFExtOutDevData) + if (!useDecompose && nullptr != mpPDFExtOutDevData) { - process(rGradientCandidate); - return; + useDecompose = true; + // process(rGradientCandidate); + // return; } basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon()); - if (aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange()) + if (!useDecompose && aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange()) { // the range which defines the gradient is different from the range of the // geometry (used for writer frames). This cannot be done calling vcl, thus use // decomposition here - process(rGradientCandidate); - return; + useDecompose = true; + // process(rGradientCandidate); + // return; } - if (!rGradientCandidate.getFillGradient().getColorStops().empty()) + if (!useDecompose && rGradientCandidate.getFillGradient().cannotBeHandledByVCL()) { // MCGR: if we have ColorStops, do not try to fallback to old VCL-Gradient, // that will *not* be capable of representing this properly. Use the // correct decomposition instead - process(rGradientCandidate); - return; + useDecompose = true; } + // if (!rGradientCandidate.getFillGradient().getColorStops().empty()) + // { + // process(rGradientCandidate); + // return; + // } + + // if (useDecompose) + // { + // process(rGradientCandidate); + // return; + // } + // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points // per polygon. Split polygon until there are less than that fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); @@ -2069,9 +2092,25 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( } // call VCL directly; encapsulate with SvtGraphicFill - impStartSvtGraphicFill(pSvtGraphicFill.get()); - mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); - impEndSvtGraphicFill(pSvtGraphicFill.get()); + if (useDecompose) + { + const basegfx::BColorStops& rColorStops(rGradientCandidate.getFillGradient().getColorStops()); + impStartSvtGraphicFill(pSvtGraphicFill.get(), &rColorStops); + + // use decompose + process(rGradientCandidate); + + impEndSvtGraphicFill(pSvtGraphicFill.get(), &rColorStops); + } + else + { + impStartSvtGraphicFill(pSvtGraphicFill.get()); + + // call VCL directly + mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); + + impEndSvtGraphicFill(pSvtGraphicFill.get()); + } } void VclMetafileProcessor2D::processPolyPolygonColorPrimitive2D( diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx index 730b7aac11f2..84c72fe194c5 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx @@ -25,6 +25,7 @@ #include #include #include // vcl::PDFExtOutDevData support +#include class GDIMetaFile; namespace tools @@ -95,8 +96,8 @@ private: impConvertFillGradientAttributeToVCLGradient(Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt, bool bIsTransparenceGradient) const; - void impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill); - void impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill); + void impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill, const basegfx::BColorStops* pColorStops = nullptr); + void impEndSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill, const basegfx::BColorStops* pColorStops = nullptr); std::unique_ptr impTryToCreateSvtGraphicStroke(const basegfx::B2DPolygon& rB2DPolygon, const basegfx::BColor* pColor, diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx index f2446e1864de..9ed34af77c08 100644 --- a/emfio/source/reader/mtftools.cxx +++ b/emfio/source/reader/mtftools.cxx @@ -1545,7 +1545,7 @@ namespace emfio SvMemoryStream aMemStm; - WriteSvtGraphicFill( aMemStm, aFill ); + WriteSvtGraphicFill( aMemStm, aFill, nullptr ); mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, static_cast(aMemStm.GetData()), diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index e5f918afb0aa..5428c3d65bae 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -3396,7 +3396,28 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, } if( pGradAction ) + { ImplWriteGradientEx( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), nWriteFlags ); + } + else + { + // read SvtGraphicFill + SvtGraphicFill aFilling; + basegfx::BColorStops aColorStops; + SvMemoryStream aMemStm(const_cast(pA->GetData()), pA->GetDataSize(),StreamMode::READ); + ReadSvtGraphicFill( aMemStm, aFilling, &aColorStops ); + + if (!aColorStops.empty()) + { + bool bBla = true; + } + + tools::PolyPolygon aPath; + aFilling.getPath(aPath); + Gradient aGradient(css::awt::GradientStyle_LINEAR, COL_BLACK, COL_WHITE); + + ImplWriteGradientEx( aPath, aGradient, nWriteFlags ); + } } else if( ( pA->GetComment().equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN") ) && ( nWriteFlags & SVGWRITER_WRITE_FILL ) && !( nWriteFlags & SVGWRITER_NO_SHAPE_COMMENTS ) && @@ -3412,7 +3433,7 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf, SvMemoryStream aMemStm( const_cast(pA->GetData()), pA->GetDataSize(), StreamMode::READ ); SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + ReadSvtGraphicFill( aMemStm, aFill, nullptr ); bool bGradient = SvtGraphicFill::fillGradient == aFill.getFillType() && ( SvtGraphicFill::GradientType::Linear == aFill.getGradientType() || diff --git a/include/vcl/graphictools.hxx b/include/vcl/graphictools.hxx index ab2371bb795b..2c134c949cb5 100644 --- a/include/vcl/graphictools.hxx +++ b/include/vcl/graphictools.hxx @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -345,8 +346,8 @@ public: private: // friends - VCL_DLLPUBLIC friend SvStream& WriteSvtGraphicFill( SvStream& rOStm, const SvtGraphicFill& rClass ); - VCL_DLLPUBLIC friend SvStream& ReadSvtGraphicFill( SvStream& rIStm, SvtGraphicFill& rClass ); + VCL_DLLPUBLIC friend SvStream& WriteSvtGraphicFill( SvStream& rOStm, const SvtGraphicFill& rClass, const basegfx::BColorStops* pColorStops ); + VCL_DLLPUBLIC friend SvStream& ReadSvtGraphicFill( SvStream& rIStm, SvtGraphicFill& rClass, basegfx::BColorStops* pColorStops ); tools::PolyPolygon maPath; Color maFillColor; diff --git a/vcl/source/filter/eps/eps.cxx b/vcl/source/filter/eps/eps.cxx index 455e89cc543a..3d7d21e16217 100644 --- a/vcl/source/filter/eps/eps.cxx +++ b/vcl/source/filter/eps/eps.cxx @@ -1267,7 +1267,7 @@ void PSWriter::ImplWriteActions( const GDIMetaFile& rMtf, VirtualDevice& rVDev ) { sSeqEnd = "XPATHFILL_SEQ_END"; SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + ReadSvtGraphicFill( aMemStm, aFill, nullptr ); switch( aFill.getFillType() ) { case SvtGraphicFill::fillSolid : diff --git a/vcl/source/gdi/gdimetafiletools.cxx b/vcl/source/gdi/gdimetafiletools.cxx index ce7977599bc2..9f0e932ce41f 100644 --- a/vcl/source/gdi/gdimetafiletools.cxx +++ b/vcl/source/gdi/gdimetafiletools.cxx @@ -248,7 +248,7 @@ namespace { // write SvtGraphicFill SvMemoryStream aMemStm; - WriteSvtGraphicFill( aMemStm, rFilling ); + WriteSvtGraphicFill( aMemStm, rFilling, nullptr ); rTarget.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", @@ -875,7 +875,7 @@ void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource) { // read SvtGraphicFill SvMemoryStream aMemStm(const_cast(pA->GetData()), pA->GetDataSize(),StreamMode::READ); - ReadSvtGraphicFill( aMemStm, aFilling ); + ReadSvtGraphicFill( aMemStm, aFilling, nullptr ); } aFilling.getPath(aPath); diff --git a/vcl/source/gdi/gdimtf.cxx b/vcl/source/gdi/gdimtf.cxx index 6b1300e589d6..a878706dc1e5 100644 --- a/vcl/source/gdi/gdimtf.cxx +++ b/vcl/source/gdi/gdimtf.cxx @@ -1138,11 +1138,12 @@ void GDIMetaFile::Rotate( Degree10 nAngle10 ) else { SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + basegfx::BColorStops aColorStops; + ReadSvtGraphicFill( aMemStm, aFill, &aColorStops ); tools::PolyPolygon aPath; aFill.getPath( aPath ); aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) ); - WriteSvtGraphicFill( aDest, aFill ); + WriteSvtGraphicFill( aDest, aFill, &aColorStops ); aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, static_cast( aDest.GetData()), aDest.Tell() ) ); } diff --git a/vcl/source/gdi/graphictools.cxx b/vcl/source/gdi/graphictools.cxx index 8308da0415f5..fe445faa185d 100644 --- a/vcl/source/gdi/graphictools.cxx +++ b/vcl/source/gdi/graphictools.cxx @@ -225,9 +225,10 @@ void SvtGraphicFill::setPath( const tools::PolyPolygon& rPath ) maPath = rPath; } -SvStream& WriteSvtGraphicFill( SvStream& rOStm, const SvtGraphicFill& rClass ) +SvStream& WriteSvtGraphicFill( SvStream& rOStm, const SvtGraphicFill& rClass, const basegfx::BColorStops* pColorStops ) { - VersionCompatWrite aCompat( rOStm, 1 ); + const bool bColorStops(nullptr != pColorStops); + VersionCompatWrite aCompat( rOStm, bColorStops ? 2 : 1 ); rClass.maPath.Write( rOStm ); TypeSerializer aSerializer(rOStm); @@ -252,10 +253,25 @@ SvStream& WriteSvtGraphicFill( SvStream& rOStm, const SvtGraphicFill& rClass ) rOStm.WriteInt32( rClass.maGradientStepCount ); aSerializer.writeGraphic(rClass.maFillGraphic); + if (bColorStops) + { + nTmp = sal::static_int_cast(pColorStops->size()); + rOStm.WriteUInt16( nTmp ); + + for (auto const& rCand : *pColorStops) + { + rOStm.WriteDouble(rCand.getStopOffset()); + const basegfx::BColor& rColor(rCand.getStopColor()); + rOStm.WriteDouble(rColor.getRed()); + rOStm.WriteDouble(rColor.getGreen()); + rOStm.WriteDouble(rColor.getBlue()); + } + } + return rOStm; } -SvStream& ReadSvtGraphicFill( SvStream& rIStm, SvtGraphicFill& rClass ) +SvStream& ReadSvtGraphicFill( SvStream& rIStm, SvtGraphicFill& rClass, basegfx::BColorStops* pColorStops ) { VersionCompatRead aCompat( rIStm ); @@ -283,6 +299,27 @@ SvStream& ReadSvtGraphicFill( SvStream& rIStm, SvtGraphicFill& rClass ) rIStm.ReadInt32( rClass.maGradientStepCount ); aSerializer.readGraphic(rClass.maFillGraphic); + if (nullptr != pColorStops && aCompat.GetVersion() > 1) + { + rIStm.ReadUInt16( nTmp ); + + if (0 != nTmp) + { + pColorStops->clear(); + double fOff, fR, fG, fB; + + for (sal_uInt16 a(0); a < nTmp; a++) + { + rIStm.ReadDouble(fOff); + rIStm.ReadDouble(fR); + rIStm.ReadDouble(fG); + rIStm.ReadDouble(fB); + + pColorStops->emplace_back(fOff, basegfx::BColor(fR, fG, fB)); + } + } + } + return rIStm; } diff --git a/vcl/source/gdi/metaact.cxx b/vcl/source/gdi/metaact.cxx index 22093a95735e..8903ebcfe18f 100644 --- a/vcl/source/gdi/metaact.cxx +++ b/vcl/source/gdi/metaact.cxx @@ -2068,14 +2068,14 @@ void MetaCommentAction::Move( tools::Long nXMove, tools::Long nYMove ) else { SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + ReadSvtGraphicFill( aMemStm, aFill, nullptr ); tools::PolyPolygon aPath; aFill.getPath( aPath ); aPath.Move( nXMove, nYMove ); aFill.setPath( aPath ); - WriteSvtGraphicFill( aDest, aFill ); + WriteSvtGraphicFill( aDest, aFill, nullptr ); } mpData.reset(); ImplInitDynamicData( static_cast( aDest.GetData() ), aDest.Tell() ); @@ -2108,12 +2108,12 @@ void MetaCommentAction::Scale( double fXScale, double fYScale ) else { SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + ReadSvtGraphicFill( aMemStm, aFill, nullptr ); tools::PolyPolygon aPath; aFill.getPath( aPath ); aPath.Scale( fXScale, fYScale ); aFill.setPath( aPath ); - WriteSvtGraphicFill( aDest, aFill ); + WriteSvtGraphicFill( aDest, aFill, nullptr ); } mpData.reset(); ImplInitDynamicData( static_cast( aDest.GetData() ), aDest.Tell() ); diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx b/vcl/source/gdi/pdfwriter_impl2.cxx index e309cc21a3b9..e7bb5b8703a3 100644 --- a/vcl/source/gdi/pdfwriter_impl2.cxx +++ b/vcl/source/gdi/pdfwriter_impl2.cxx @@ -676,7 +676,7 @@ void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevDa { sSeqEnd = OString("XPATHFILL_SEQ_END"); SvtGraphicFill aFill; - ReadSvtGraphicFill( aMemStm, aFill ); + ReadSvtGraphicFill( aMemStm, aFill, nullptr ); if ( ( aFill.getFillType() == SvtGraphicFill::fillSolid ) && ( aFill.getFillRule() == SvtGraphicFill::fillEvenOdd ) ) {