summaryrefslogtreecommitdiff
path: root/drawinglayer
diff options
context:
space:
mode:
authorArmin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de>2023-06-05 17:15:34 +0200
committerXisco Fauli <xiscofauli@libreoffice.org>2023-06-13 23:20:41 +0200
commit9b8c21acd31f08a0c3f8d88ddac57c80ef5997a1 (patch)
tree5361329018ac0b8573a858a6a28426f6f5684008 /drawinglayer
parente691869486cd4ee4ee1dfb8f7a41440f7f6500fd (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> (cherry picked from commit a6e72e2b314e64f3199f3eaf1ecf78157446f6dd) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152882 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx188
1 files changed, 151 insertions, 37 deletions
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 9b536bcbd3d7..feb30d33e1ed 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -66,6 +66,7 @@
#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // for Title/Description metadata
#include <drawinglayer/converters.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/vcompat.hxx>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/i18n/BreakIterator.hpp>
@@ -1962,58 +1963,126 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate)
{
- basegfx::B2DVector aScale, aTranslate;
- double fRotate, fShearX;
+ bool useDecompose(false);
- maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
-
- 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;
+ }
}
// tdf#150551 for PDF export, use the decomposition for better gradient visualization
- if (nullptr != mpPDFExtOutDevData)
+ if (!useDecompose && nullptr != mpPDFExtOutDevData)
{
- process(rGradientCandidate);
- return;
+ useDecompose = true;
}
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;
}
- if (!rGradientCandidate.getFillGradient().getColorStops().empty())
+ const attribute::FillGradientAttribute& rFillGradient(rGradientCandidate.getFillGradient());
+
+ if (!useDecompose && rFillGradient.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
+ useDecompose = true;
+ }
+
+ if (useDecompose)
+ {
+ GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile());
+
+ // tdf#155479 only add 'BGRAD_SEQ_BEGIN' if SVG export
+ if (nullptr != pMetaFile && pMetaFile->getSVG())
+ {
+ // write the color stops to a memory stream
+ SvMemoryStream aMemStm;
+ VersionCompatWrite aCompat(aMemStm, 1);
+
+ const basegfx::BColorStops& rColorStops(rFillGradient.getColorStops());
+ sal_uInt16 nTmp(sal::static_int_cast<sal_uInt16>(rColorStops.size()));
+ aMemStm.WriteUInt16(nTmp);
+
+ for (auto const& rCand : rColorStops)
+ {
+ aMemStm.WriteDouble(rCand.getStopOffset());
+ const basegfx::BColor& rColor(rCand.getStopColor());
+ aMemStm.WriteDouble(rColor.getRed());
+ aMemStm.WriteDouble(rColor.getGreen());
+ aMemStm.WriteDouble(rColor.getBlue());
+ }
+
+ // Add a new MetaCommentAction section of type 'BGRAD_SEQ_BEGIN/BGRAD_SEQ_END'
+ // that is capable of holding the new color step information, plus the
+ // already used MetaActionType::GRADIENTEX.
+ // With that combination only places that know about that new BGRAD_SEQ_* will
+ // use it while all others will work on the created decomposition of the
+ // gradient for compatibility - which are single-color filled polygons
+ pMetaFile->AddAction(new MetaCommentAction(
+ "BGRAD_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ aMemStm.TellEnd()));
+
+ // create MetaActionType::GRADIENTEX
+ // NOTE: with the new BGRAD_SEQ_* we could use basegfx::B2DPolygon and
+ // basegfx::BGradient here directly, but may have to add streaming OPs
+ // for these, so for now just go with what we use all the time. The real
+ // work for improvement should not go to this 'compromize' but to a real
+ // re-work of the SVG export (or/and others) to no longer work on metafiles
+ // but on UNO API or primitives (whatever fits best to the specific export)
+ fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
+ Gradient aVCLGradient;
+ impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false);
+ aLocalPolyPolygon.transform(maCurrentTransformation);
+ const tools::PolyPolygon aToolsPolyPolygon(
+ getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon)));
+ mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
+ }
+
+ // use decompose to draw, will create PolyPolygon ColorFill actions
process(rGradientCandidate);
+
+ // tdf#155479 only add 'BGRAD_SEQ_END' if SVG export
+ if (nullptr != pMetaFile && pMetaFile->getSVG())
+ {
+ // close the BGRAD_SEQ_* actions range
+ pMetaFile->AddAction(new MetaCommentAction("BGRAD_SEQ_END"));
+ }
+
return;
}
@@ -2025,8 +2094,7 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
// it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
// re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
Gradient aVCLGradient;
- impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(),
- false);
+ impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false);
aLocalPolyPolygon.transform(maCurrentTransformation);
// #i82145# ATM VCL printing of gradients using curved shapes does not work,
@@ -2296,6 +2364,10 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
pFiGradient = nullptr;
}
+ // tdf#155479 preps for holding extra-MCGR infos
+ bool bSVGTransparencyColorStops(false);
+ basegfx::BColorStops aSVGTransparencyColorStops;
+
// MCGR: tdf#155437 If we have identified a transparency gradient,
// check if VCL is able to handle it at all
if (nullptr != pFiGradient && pFiGradient->getFillGradient().cannotBeHandledByVCL())
@@ -2314,7 +2386,21 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
// available in metafiles, with the known limitations (not backward comp, all
// places using it would need adaption, ...), but combined with knowing that nearly
// all usages ignore or render it locally anyways makes that a non-option.
- pFiGradient = nullptr;
+
+ // tdf#155479 Yepp, as already mentionmed above we need to add
+ // some MCGR infos in case of SVG export, prepare that here
+ if (nullptr != mpOutputDevice->GetConnectMetaFile()
+ && mpOutputDevice->GetConnectMetaFile()->getSVG())
+ {
+ // for SVG, do not use decompose & prep extra data
+ bSVGTransparencyColorStops = true;
+ aSVGTransparencyColorStops = pFiGradient->getFillGradient().getColorStops();
+ }
+ else
+ {
+ // use decomposition
+ pFiGradient = nullptr;
+ }
}
if (nullptr != pFiGradient)
@@ -2322,6 +2408,13 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
// this combination of Gradient can be expressed/handled by
// vcl/metafile, so add it directly. various content, create content-metafile
GDIMetaFile aContentMetafile;
+
+ // tdf#155479 do not forget to forward SVG flag for sub-content
+ if (bSVGTransparencyColorStops)
+ {
+ aContentMetafile.setSVG(true);
+ }
+
const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
// re-create a VCL-gradient from FillGradientPrimitive2D
@@ -2329,9 +2422,30 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(),
true);
- // render it to VCL (creates MetaFloatTransparentAction)
- mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
- aPrimitiveRectangle.GetSize(), aVCLGradient);
+ if (bSVGTransparencyColorStops)
+ {
+ // tdf#155479 create action directly & add extra
+ // MCGR infos to the metafile, do that by adding - ONLY in
+ // case of SVG export - to the MetaFileAction. For that
+ // reason, do what OutputDevice::DrawTransparent will do,
+ // but locally.
+ // NOTE: That would be good for this whole
+ // VclMetafileProcessor2D anyways to allow to get it
+ // completely independent from OutputDevice in the long run
+ GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile());
+ rtl::Reference<::MetaFloatTransparentAction> pAction(
+ new MetaFloatTransparentAction(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient));
+
+ pAction->addSVGTransparencyColorStops(aSVGTransparencyColorStops);
+ pMetaFile->AddAction(pAction);
+ }
+ else
+ {
+ // render it to VCL (creates MetaFloatTransparentAction)
+ mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient);
+ }
return;
}