summaryrefslogtreecommitdiff
path: root/drawinglayer/source
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source')
-rw-r--r--drawinglayer/source/animation/animationtiming.cxx20
-rw-r--r--drawinglayer/source/attribute/fillgradientattribute.cxx98
-rw-r--r--drawinglayer/source/attribute/fillgraphicattribute.cxx5
-rw-r--r--drawinglayer/source/attribute/fontattribute.cxx11
-rw-r--r--drawinglayer/source/attribute/linestartendattribute.cxx5
-rw-r--r--drawinglayer/source/attribute/sdrallattribute3d.cxx21
-rw-r--r--drawinglayer/source/attribute/sdrfillattribute.cxx29
-rw-r--r--drawinglayer/source/attribute/sdrfillgraphicattribute.cxx5
-rw-r--r--drawinglayer/source/attribute/sdrglowattribute.cxx2
-rw-r--r--drawinglayer/source/attribute/sdrlightingattribute3d.cxx4
-rw-r--r--drawinglayer/source/attribute/sdrlinestartendattribute.cxx9
-rw-r--r--drawinglayer/source/attribute/sdrshadowattribute.cxx13
-rw-r--r--drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx19
-rw-r--r--drawinglayer/source/dumper/XShapeDumper.cxx9
-rw-r--r--drawinglayer/source/geometry/viewinformation2d.cxx291
-rw-r--r--drawinglayer/source/geometry/viewinformation3d.cxx88
-rw-r--r--drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx157
-rw-r--r--drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx125
-rw-r--r--drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx98
-rw-r--r--drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx42
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx56
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx21
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx17
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx25
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx23
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx30
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx15
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx31
-rw-r--r--drawinglayer/source/primitive2d/Primitive2DContainer.cxx62
-rw-r--r--drawinglayer/source/primitive2d/Tools.cxx37
-rw-r--r--drawinglayer/source/primitive2d/animatedprimitive2d.cxx9
-rw-r--r--drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx36
-rw-r--r--drawinglayer/source/primitive2d/baseprimitive2d.cxx77
-rw-r--r--drawinglayer/source/primitive2d/bitmapprimitive2d.cxx24
-rw-r--r--drawinglayer/source/primitive2d/borderlineprimitive2d.cxx61
-rw-r--r--drawinglayer/source/primitive2d/controlprimitive2d.cxx122
-rw-r--r--drawinglayer/source/primitive2d/cropprimitive2d.cxx11
-rw-r--r--drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx10
-rw-r--r--drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx58
-rw-r--r--drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx23
-rw-r--r--drawinglayer/source/primitive2d/epsprimitive2d.cxx16
-rw-r--r--drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx55
-rw-r--r--drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx328
-rw-r--r--drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx25
-rw-r--r--drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx25
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx303
-rw-r--r--drawinglayer/source/primitive2d/graphicprimitive2d.cxx45
-rw-r--r--drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx127
-rw-r--r--drawinglayer/source/primitive2d/gridprimitive2d.cxx31
-rw-r--r--drawinglayer/source/primitive2d/groupprimitive2d.cxx11
-rw-r--r--drawinglayer/source/primitive2d/helplineprimitive2d.cxx29
-rw-r--r--drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx16
-rw-r--r--drawinglayer/source/primitive2d/maskprimitive2d.cxx5
-rw-r--r--drawinglayer/source/primitive2d/mediaprimitive2d.cxx25
-rw-r--r--drawinglayer/source/primitive2d/metafileprimitive2d.cxx85
-rw-r--r--drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx5
-rw-r--r--drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx13
-rw-r--r--drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx23
-rw-r--r--drawinglayer/source/primitive2d/patternfillprimitive2d.cxx59
-rw-r--r--drawinglayer/source/primitive2d/polygonprimitive2d.cxx388
-rw-r--r--drawinglayer/source/primitive2d/primitivetools2d.cxx40
-rw-r--r--drawinglayer/source/primitive2d/sceneprimitive2d.cxx180
-rw-r--r--drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx7
-rw-r--r--drawinglayer/source/primitive2d/shadowprimitive2d.cxx398
-rw-r--r--drawinglayer/source/primitive2d/softedgeprimitive2d.cxx315
-rw-r--r--drawinglayer/source/primitive2d/structuretagprimitive2d.cxx20
-rw-r--r--drawinglayer/source/primitive2d/svggradientprimitive2d.cxx132
-rw-r--r--drawinglayer/source/primitive2d/textbreakuphelper.cxx16
-rw-r--r--drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx212
-rw-r--r--drawinglayer/source/primitive2d/texteffectprimitive2d.cxx43
-rw-r--r--drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx13
-rw-r--r--drawinglayer/source/primitive2d/textlayoutdevice.cxx113
-rw-r--r--drawinglayer/source/primitive2d/textlineprimitive2d.cxx27
-rw-r--r--drawinglayer/source/primitive2d/textprimitive2d.cxx45
-rw-r--r--drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx34
-rw-r--r--drawinglayer/source/primitive2d/transformprimitive2d.cxx21
-rw-r--r--drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx10
-rw-r--r--drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx336
-rw-r--r--drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx11
-rw-r--r--drawinglayer/source/primitive3d/baseprimitive3d.cxx3
-rw-r--r--drawinglayer/source/primitive3d/groupprimitive3d.cxx7
-rw-r--r--drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx7
-rw-r--r--drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx5
-rw-r--r--drawinglayer/source/primitive3d/polygonprimitive3d.cxx13
-rw-r--r--drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx50
-rw-r--r--drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx5
-rw-r--r--drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx5
-rw-r--r--drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx32
-rw-r--r--drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx13
-rw-r--r--drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx13
-rw-r--r--drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx5
-rw-r--r--drawinglayer/source/primitive3d/sdrprimitive3d.cxx9
-rw-r--r--drawinglayer/source/primitive3d/shadowprimitive3d.cxx5
-rw-r--r--drawinglayer/source/primitive3d/textureprimitive3d.cxx14
-rw-r--r--drawinglayer/source/primitive3d/transformprimitive3d.cxx5
-rw-r--r--drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx302
-rw-r--r--drawinglayer/source/processor2d/baseprocessor2d.cxx22
-rw-r--r--drawinglayer/source/processor2d/cairopixelprocessor2d.cxx975
-rw-r--r--drawinglayer/source/processor2d/contourextractor2d.cxx10
-rw-r--r--drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx2191
-rw-r--r--drawinglayer/source/processor2d/getdigitlanguage.cxx2
-rw-r--r--drawinglayer/source/processor2d/helperwrongspellrenderer.cxx6
-rw-r--r--drawinglayer/source/processor2d/hittestprocessor2d.cxx69
-rw-r--r--drawinglayer/source/processor2d/linegeometryextractor2d.cxx10
-rw-r--r--drawinglayer/source/processor2d/processor2dtools.cxx84
-rw-r--r--drawinglayer/source/processor2d/processorfromoutputdevice.cxx50
-rw-r--r--drawinglayer/source/processor2d/textaspolygonextractor2d.cxx116
-rw-r--r--drawinglayer/source/processor2d/textextractor2d.cxx88
-rw-r--r--drawinglayer/source/processor2d/vclhelperbufferdevice.cxx301
-rw-r--r--drawinglayer/source/processor2d/vclhelperbufferdevice.hxx73
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx935
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx12
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx455
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.hxx12
-rw-r--r--drawinglayer/source/processor2d/vclprocessor2d.cxx668
-rw-r--r--drawinglayer/source/processor2d/vclprocessor2d.hxx16
-rw-r--r--drawinglayer/source/processor3d/baseprocessor3d.cxx6
-rw-r--r--drawinglayer/source/processor3d/defaultprocessor3d.cxx78
-rw-r--r--drawinglayer/source/processor3d/geometry2dextractor.cxx11
-rw-r--r--drawinglayer/source/processor3d/shadow3dextractor.cxx12
-rw-r--r--drawinglayer/source/processor3d/zbufferprocessor3d.cxx12
-rw-r--r--drawinglayer/source/texture/texture.cxx668
-rw-r--r--drawinglayer/source/texture/texture3d.cxx19
-rw-r--r--drawinglayer/source/tools/converters.cxx451
-rw-r--r--drawinglayer/source/tools/emfpbrush.cxx83
-rw-r--r--drawinglayer/source/tools/emfpbrush.hxx8
-rw-r--r--drawinglayer/source/tools/emfpcustomlinecap.cxx35
-rw-r--r--drawinglayer/source/tools/emfpcustomlinecap.hxx4
-rw-r--r--drawinglayer/source/tools/emfphelperdata.cxx729
-rw-r--r--drawinglayer/source/tools/emfphelperdata.hxx28
-rw-r--r--drawinglayer/source/tools/emfpimage.cxx4
-rw-r--r--drawinglayer/source/tools/emfpimageattributes.cxx1
-rw-r--r--drawinglayer/source/tools/emfppath.cxx123
-rw-r--r--drawinglayer/source/tools/emfppath.hxx8
-rw-r--r--drawinglayer/source/tools/emfppen.cxx185
-rw-r--r--drawinglayer/source/tools/emfppen.hxx13
-rw-r--r--drawinglayer/source/tools/primitive2dxmldump.cxx376
-rw-r--r--drawinglayer/source/tools/wmfemfhelper.cxx218
138 files changed, 10809 insertions, 4237 deletions
diff --git a/drawinglayer/source/animation/animationtiming.cxx b/drawinglayer/source/animation/animationtiming.cxx
index c1471e43bd71..364ae899c9ca 100644
--- a/drawinglayer/source/animation/animationtiming.cxx
+++ b/drawinglayer/source/animation/animationtiming.cxx
@@ -116,7 +116,7 @@ namespace drawinglayer::animation
double AnimationEntryLinear::getStateAtTime(double fTime) const
{
- if(basegfx::fTools::more(mfDuration, 0.0))
+ if(mfDuration > 0.0)
{
const double fFactor(fTime / mfDuration);
@@ -193,19 +193,15 @@ namespace drawinglayer::animation
bool AnimationEntryList::operator==(const AnimationEntry& rCandidate) const
{
- const AnimationEntryList* pCompare = dynamic_cast< const AnimationEntryList* >(&rCandidate);
+ const AnimationEntryList* pCompare = dynamic_cast<const AnimationEntryList*>(&rCandidate);
- if(pCompare && mfDuration == pCompare->mfDuration)
+ if (pCompare && mfDuration == pCompare->mfDuration)
{
- for(size_t a(0); a < maEntries.size(); a++)
- {
- if(!(*maEntries[a] == *pCompare->maEntries[a]))
- {
- return false;
- }
- }
-
- return true;
+ return std::equal(maEntries.cbegin(), maEntries.cend(),
+ pCompare->maEntries.cbegin(), pCompare->maEntries.cend(),
+ [](const auto& lhs, const auto& rhs) {
+ return *lhs == *rhs;
+ });
}
return false;
diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx b/drawinglayer/source/attribute/fillgradientattribute.cxx
index b8b06e20bbbf..e02fdd4a5dad 100644
--- a/drawinglayer/source/attribute/fillgradientattribute.cxx
+++ b/drawinglayer/source/attribute/fillgradientattribute.cxx
@@ -18,8 +18,7 @@
*/
#include <drawinglayer/attribute/fillgradientattribute.hxx>
-#include <basegfx/color/bcolor.hxx>
-
+#include <basegfx/utils/gradienttools.hxx>
namespace drawinglayer::attribute
{
@@ -31,29 +30,42 @@ namespace drawinglayer::attribute
double mfOffsetX;
double mfOffsetY;
double mfAngle;
- basegfx::BColor maStartColor;
- basegfx::BColor maEndColor;
- GradientStyle meStyle;
+ basegfx::BColorStops maColorStops;
+ css::awt::GradientStyle meStyle;
sal_uInt16 mnSteps;
ImpFillGradientAttribute(
- GradientStyle eStyle,
+ css::awt::GradientStyle eStyle,
double fBorder,
double fOffsetX,
double fOffsetY,
double fAngle,
- const basegfx::BColor& rStartColor,
- const basegfx::BColor& rEndColor,
+ const basegfx::BColorStops& rColorStops,
sal_uInt16 nSteps)
: mfBorder(fBorder),
mfOffsetX(fOffsetX),
mfOffsetY(fOffsetY),
mfAngle(fAngle),
- maStartColor(rStartColor),
- maEndColor(rEndColor),
+ maColorStops(rColorStops), // copy ColorStops
meStyle(eStyle),
mnSteps(nSteps)
{
+ // Correct the local ColorStops. That will guarantee that the
+ // content does contain no offsets < 0.0, > 1.0 or double
+ // ones, also secures sorted arrangement and checks for
+ // double colors, too (see there for more information).
+ // This is what the usages of this in primitives need.
+ // Since FillGradientAttribute is read-only doing this
+ // once here in the constructor is sufficient
+ maColorStops.sortAndCorrect();
+
+ // sortAndCorrectColorStops is rigid and can return
+ // an empty result. To keep things simple, add a single
+ // fallback value
+ if (maColorStops.empty())
+ {
+ maColorStops.emplace_back(0.0, basegfx::BColor());
+ }
}
ImpFillGradientAttribute()
@@ -61,19 +73,21 @@ namespace drawinglayer::attribute
mfOffsetX(0.0),
mfOffsetY(0.0),
mfAngle(0.0),
- meStyle(GradientStyle::Linear),
+ maColorStops(),
+ meStyle(css::awt::GradientStyle_LINEAR),
mnSteps(0)
{
+ // always add a fallback color, see above
+ maColorStops.emplace_back(0.0, basegfx::BColor());
}
// data read access
- GradientStyle getStyle() const { return meStyle; }
+ css::awt::GradientStyle getStyle() const { return meStyle; }
double getBorder() const { return mfBorder; }
double getOffsetX() const { return mfOffsetX; }
double getOffsetY() const { return mfOffsetY; }
double getAngle() const { return mfAngle; }
- const basegfx::BColor& getStartColor() const { return maStartColor; }
- const basegfx::BColor& getEndColor() const { return maEndColor; }
+ const basegfx::BColorStops& getColorStops() const { return maColorStops; }
sal_uInt16 getSteps() const { return mnSteps; }
bool operator==(const ImpFillGradientAttribute& rCandidate) const
@@ -83,8 +97,7 @@ namespace drawinglayer::attribute
&& getOffsetX() == rCandidate.getOffsetX()
&& getOffsetY() == rCandidate.getOffsetY()
&& getAngle() == rCandidate.getAngle()
- && getStartColor() == rCandidate.getStartColor()
- && getEndColor() == rCandidate.getEndColor()
+ && getColorStops() == rCandidate.getColorStops()
&& getSteps() == rCandidate.getSteps());
}
};
@@ -99,16 +112,15 @@ namespace drawinglayer::attribute
}
FillGradientAttribute::FillGradientAttribute(
- GradientStyle eStyle,
+ css::awt::GradientStyle eStyle,
double fBorder,
double fOffsetX,
double fOffsetY,
double fAngle,
- const basegfx::BColor& rStartColor,
- const basegfx::BColor& rEndColor,
+ const basegfx::BColorStops& rColorStops,
sal_uInt16 nSteps)
: mpFillGradientAttribute(ImpFillGradientAttribute(
- eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rStartColor, rEndColor, nSteps))
+ eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rColorStops, nSteps))
{
}
@@ -128,6 +140,41 @@ namespace drawinglayer::attribute
return mpFillGradientAttribute.same_object(theGlobalDefault());
}
+ // MCGR: Check if rendering cannot be handled by old vcl stuff
+ bool FillGradientAttribute::cannotBeHandledByVCL() const
+ {
+ // MCGR: If GradientStops are used, use decomposition since vcl is not able
+ // to render multi-color gradients
+ if (getColorStops().size() != 2)
+ {
+ return true;
+ }
+
+ // MCGR: If GradientStops do not start and stop at traditional Start/EndColor,
+ // use decomposition since vcl is not able to render this
+ if (!getColorStops().empty())
+ {
+ if (!basegfx::fTools::equalZero(getColorStops().front().getStopOffset())
+ || !basegfx::fTools::equal(getColorStops().back().getStopOffset(), 1.0))
+ {
+ return true;
+ }
+ }
+
+ // VCL should be able to handle all styles, but for tdf#133477 the VCL result
+ // is different from processing the gradient manually by drawinglayer
+ // (and the Writer unittest for it fails). Keep using the drawinglayer code
+ // until somebody founds out what's wrong and fixes it.
+ if (getStyle() != css::awt::GradientStyle_LINEAR
+ && getStyle() != css::awt::GradientStyle_AXIAL
+ && getStyle() != css::awt::GradientStyle_RADIAL)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
FillGradientAttribute& FillGradientAttribute::operator=(const FillGradientAttribute&) = default;
FillGradientAttribute& FillGradientAttribute::operator=(FillGradientAttribute&&) = default;
@@ -141,14 +188,9 @@ namespace drawinglayer::attribute
return rCandidate.mpFillGradientAttribute == mpFillGradientAttribute;
}
- const basegfx::BColor& FillGradientAttribute::getStartColor() const
- {
- return mpFillGradientAttribute->getStartColor();
- }
-
- const basegfx::BColor& FillGradientAttribute::getEndColor() const
+ const basegfx::BColorStops& FillGradientAttribute::getColorStops() const
{
- return mpFillGradientAttribute->getEndColor();
+ return mpFillGradientAttribute->getColorStops();
}
double FillGradientAttribute::getBorder() const
@@ -171,7 +213,7 @@ namespace drawinglayer::attribute
return mpFillGradientAttribute->getAngle();
}
- GradientStyle FillGradientAttribute::getStyle() const
+ css::awt::GradientStyle FillGradientAttribute::getStyle() const
{
return mpFillGradientAttribute->getStyle();
}
diff --git a/drawinglayer/source/attribute/fillgraphicattribute.cxx b/drawinglayer/source/attribute/fillgraphicattribute.cxx
index b36520d4f981..300d6f6123f1 100644
--- a/drawinglayer/source/attribute/fillgraphicattribute.cxx
+++ b/drawinglayer/source/attribute/fillgraphicattribute.cxx
@@ -22,6 +22,7 @@
#include <algorithm>
#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <utility>
#include <vcl/graph.hxx>
namespace drawinglayer::attribute
@@ -41,12 +42,12 @@ namespace drawinglayer::attribute
double mfOffsetY;
ImpFillGraphicAttribute(
- const Graphic& rGraphic,
+ Graphic aGraphic,
const basegfx::B2DRange& rGraphicRange,
bool bTiling,
double fOffsetX,
double fOffsetY)
- : maGraphic(rGraphic),
+ : maGraphic(std::move(aGraphic)),
maGraphicRange(rGraphicRange),
mbTiling(bTiling),
mfOffsetX(fOffsetX),
diff --git a/drawinglayer/source/attribute/fontattribute.cxx b/drawinglayer/source/attribute/fontattribute.cxx
index 8ae3836d8aa7..c1f0ab000d86 100644
--- a/drawinglayer/source/attribute/fontattribute.cxx
+++ b/drawinglayer/source/attribute/fontattribute.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/attribute/fontattribute.hxx>
#include <rtl/ustring.hxx>
+#include <utility>
namespace drawinglayer::attribute
{
@@ -38,11 +39,11 @@ public:
bool mbBiDiStrong : 1; // BiDi Flag
bool mbMonospaced : 1;
- ImpFontAttribute(const OUString& rFamilyName, const OUString& rStyleName, sal_uInt16 nWeight,
- bool bSymbol, bool bVertical, bool bItalic, bool bMonospaced, bool bOutline,
- bool bRTL, bool bBiDiStrong)
- : maFamilyName(rFamilyName)
- , maStyleName(rStyleName)
+ ImpFontAttribute(OUString aFamilyName, OUString aStyleName, sal_uInt16 nWeight, bool bSymbol,
+ bool bVertical, bool bItalic, bool bMonospaced, bool bOutline, bool bRTL,
+ bool bBiDiStrong)
+ : maFamilyName(std::move(aFamilyName))
+ , maStyleName(std::move(aStyleName))
, mnWeight(nWeight)
, mbSymbol(bSymbol)
, mbVertical(bVertical)
diff --git a/drawinglayer/source/attribute/linestartendattribute.cxx b/drawinglayer/source/attribute/linestartendattribute.cxx
index 57a680db377c..33ac17c70599 100644
--- a/drawinglayer/source/attribute/linestartendattribute.cxx
+++ b/drawinglayer/source/attribute/linestartendattribute.cxx
@@ -20,6 +20,7 @@
#include <drawinglayer/attribute/linestartendattribute.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <utility>
namespace drawinglayer::attribute
@@ -35,10 +36,10 @@ namespace drawinglayer::attribute
ImpLineStartEndAttribute(
double fWidth,
- const basegfx::B2DPolyPolygon& rPolyPolygon,
+ basegfx::B2DPolyPolygon aPolyPolygon,
bool bCentered)
: mfWidth(fWidth),
- maPolyPolygon(rPolyPolygon),
+ maPolyPolygon(std::move(aPolyPolygon)),
mbCentered(bCentered)
{
}
diff --git a/drawinglayer/source/attribute/sdrallattribute3d.cxx b/drawinglayer/source/attribute/sdrallattribute3d.cxx
index 079f655796d9..8c74306ca1dd 100644
--- a/drawinglayer/source/attribute/sdrallattribute3d.cxx
+++ b/drawinglayer/source/attribute/sdrallattribute3d.cxx
@@ -18,21 +18,22 @@
*/
#include <drawinglayer/attribute/sdrallattribute3d.hxx>
+#include <utility>
namespace drawinglayer::attribute
{
SdrLineFillShadowAttribute3D::SdrLineFillShadowAttribute3D(
- const SdrLineAttribute& rLine,
- const SdrFillAttribute& rFill,
- const SdrLineStartEndAttribute& rLineStartEnd,
- const SdrShadowAttribute& rShadow,
- const FillGradientAttribute& rFillFloatTransGradient)
- : maLine(rLine),
- maFill(rFill),
- maLineStartEnd(rLineStartEnd),
- maShadow(rShadow),
- maFillFloatTransGradient(rFillFloatTransGradient)
+ SdrLineAttribute aLine,
+ SdrFillAttribute aFill,
+ SdrLineStartEndAttribute aLineStartEnd,
+ SdrShadowAttribute aShadow,
+ FillGradientAttribute aFillFloatTransGradient)
+ : maLine(std::move(aLine)),
+ maFill(std::move(aFill)),
+ maLineStartEnd(std::move(aLineStartEnd)),
+ maShadow(std::move(aShadow)),
+ maFillFloatTransGradient(std::move(aFillFloatTransGradient))
{
}
diff --git a/drawinglayer/source/attribute/sdrfillattribute.cxx b/drawinglayer/source/attribute/sdrfillattribute.cxx
index b582ee0a11d0..8cee8f98d1e9 100644
--- a/drawinglayer/source/attribute/sdrfillattribute.cxx
+++ b/drawinglayer/source/attribute/sdrfillattribute.cxx
@@ -22,6 +22,7 @@
#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
#include <drawinglayer/attribute/fillhatchattribute.hxx>
#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <utility>
namespace drawinglayer::attribute
@@ -40,14 +41,14 @@ namespace drawinglayer::attribute
ImpSdrFillAttribute(
double fTransparence,
const basegfx::BColor& rColor,
- const FillGradientAttribute& rGradient,
- const FillHatchAttribute& rHatch,
- const SdrFillGraphicAttribute& rFillGraphic)
+ FillGradientAttribute aGradient,
+ FillHatchAttribute aHatch,
+ SdrFillGraphicAttribute aFillGraphic)
: mfTransparence(fTransparence),
maColor(rColor),
- maGradient(rGradient),
- maHatch(rHatch),
- maFillGraphic(rFillGraphic)
+ maGradient(std::move(aGradient)),
+ maHatch(std::move(aHatch)),
+ maFillGraphic(std::move(aFillGraphic))
{
}
@@ -81,6 +82,11 @@ namespace drawinglayer::attribute
static SdrFillAttribute::ImplType SINGLETON;
return SINGLETON;
}
+ SdrFillAttribute::ImplType& slideBackgroundFillGlobalDefault()
+ {
+ static SdrFillAttribute::ImplType SINGLETON2;
+ return SINGLETON2;
+ }
}
SdrFillAttribute::SdrFillAttribute(
@@ -94,8 +100,10 @@ namespace drawinglayer::attribute
{
}
- SdrFillAttribute::SdrFillAttribute()
- : mpSdrFillAttribute(theGlobalDefault())
+ SdrFillAttribute::SdrFillAttribute(bool bSlideBackgroundFill)
+ : mpSdrFillAttribute(bSlideBackgroundFill
+ ? slideBackgroundFillGlobalDefault()
+ : theGlobalDefault())
{
}
@@ -110,6 +118,11 @@ namespace drawinglayer::attribute
return mpSdrFillAttribute.same_object(theGlobalDefault());
}
+ bool SdrFillAttribute::isSlideBackgroundFill() const
+ {
+ return mpSdrFillAttribute.same_object(slideBackgroundFillGlobalDefault());
+ }
+
SdrFillAttribute& SdrFillAttribute::operator=(const SdrFillAttribute&) = default;
SdrFillAttribute& SdrFillAttribute::operator=(SdrFillAttribute&&) = default;
diff --git a/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx b/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx
index 14f53cf03db3..b78f3e322c38 100644
--- a/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx
+++ b/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx
@@ -23,6 +23,7 @@
#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <utility>
#include <vcl/graph.hxx>
@@ -44,7 +45,7 @@ namespace drawinglayer::attribute
bool mbLogSize : 1;
ImpSdrFillGraphicAttribute(
- const Graphic& rFillGraphic,
+ Graphic aFillGraphic,
const basegfx::B2DVector& rGraphicLogicSize,
const basegfx::B2DVector& rSize,
const basegfx::B2DVector& rOffset,
@@ -53,7 +54,7 @@ namespace drawinglayer::attribute
bool bTiling,
bool bStretch,
bool bLogSize)
- : maFillGraphic(rFillGraphic),
+ : maFillGraphic(std::move(aFillGraphic)),
maGraphicLogicSize(rGraphicLogicSize),
maSize(rSize),
maOffset(rOffset),
diff --git a/drawinglayer/source/attribute/sdrglowattribute.cxx b/drawinglayer/source/attribute/sdrglowattribute.cxx
index 36339dac0933..c27390d64d6d 100644
--- a/drawinglayer/source/attribute/sdrglowattribute.cxx
+++ b/drawinglayer/source/attribute/sdrglowattribute.cxx
@@ -23,8 +23,6 @@ SdrGlowAttribute::SdrGlowAttribute(const SdrGlowAttribute&) = default;
SdrGlowAttribute::SdrGlowAttribute(SdrGlowAttribute&&) = default;
-SdrGlowAttribute::~SdrGlowAttribute() = default;
-
SdrGlowAttribute& SdrGlowAttribute::operator=(const SdrGlowAttribute&) = default;
SdrGlowAttribute& SdrGlowAttribute::operator=(SdrGlowAttribute&&) = default;
diff --git a/drawinglayer/source/attribute/sdrlightingattribute3d.cxx b/drawinglayer/source/attribute/sdrlightingattribute3d.cxx
index 4f9b75cd1ff2..0f625756383c 100644
--- a/drawinglayer/source/attribute/sdrlightingattribute3d.cxx
+++ b/drawinglayer/source/attribute/sdrlightingattribute3d.cxx
@@ -140,7 +140,7 @@ namespace drawinglayer::attribute
const Sdr3DLightAttribute& rLight(rLightVector[a]);
const double fCosFac(rLight.getDirection().scalar(aEyeNormal));
- if(basegfx::fTools::more(fCosFac, 0.0))
+ if(fCosFac > 0.0)
{
aRetval += (rLight.getColor() * rColor) * fCosFac;
@@ -151,7 +151,7 @@ namespace drawinglayer::attribute
aSpecularNormal.normalize();
double fCosFac2(aSpecularNormal.scalar(aEyeNormal));
- if(basegfx::fTools::more(fCosFac2, 0.0))
+ if(fCosFac2 > 0.0)
{
fCosFac2 = pow(fCosFac2, static_cast<double>(nSpecularIntensity));
aRetval += rSpecular * fCosFac2;
diff --git a/drawinglayer/source/attribute/sdrlinestartendattribute.cxx b/drawinglayer/source/attribute/sdrlinestartendattribute.cxx
index aa052236cd72..911f8aef8bf5 100644
--- a/drawinglayer/source/attribute/sdrlinestartendattribute.cxx
+++ b/drawinglayer/source/attribute/sdrlinestartendattribute.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <utility>
namespace drawinglayer::attribute
@@ -38,16 +39,16 @@ namespace drawinglayer::attribute
bool mbEndCentered : 1; // Line is centered on line end point
ImpSdrLineStartEndAttribute(
- const basegfx::B2DPolyPolygon& rStartPolyPolygon,
- const basegfx::B2DPolyPolygon& rEndPolyPolygon,
+ basegfx::B2DPolyPolygon aStartPolyPolygon,
+ basegfx::B2DPolyPolygon aEndPolyPolygon,
double fStartWidth,
double fEndWidth,
bool bStartActive,
bool bEndActive,
bool bStartCentered,
bool bEndCentered)
- : maStartPolyPolygon(rStartPolyPolygon),
- maEndPolyPolygon(rEndPolyPolygon),
+ : maStartPolyPolygon(std::move(aStartPolyPolygon)),
+ maEndPolyPolygon(std::move(aEndPolyPolygon)),
mfStartWidth(fStartWidth),
mfEndWidth(fEndWidth),
mbStartActive(bStartActive),
diff --git a/drawinglayer/source/attribute/sdrshadowattribute.cxx b/drawinglayer/source/attribute/sdrshadowattribute.cxx
index 6e046f1f07c7..1eb1b3ea687c 100644
--- a/drawinglayer/source/attribute/sdrshadowattribute.cxx
+++ b/drawinglayer/source/attribute/sdrshadowattribute.cxx
@@ -20,6 +20,7 @@
#include <drawinglayer/attribute/sdrshadowattribute.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/color/bcolor.hxx>
+#include <docmodel/theme/FormatScheme.hxx>
namespace drawinglayer::attribute
@@ -32,6 +33,7 @@ namespace drawinglayer::attribute
basegfx::B2DVector maSize; // [0.0 .. 2.0]
double mfTransparence; // [0.0 .. 1.0], 0.0==no transp.
sal_Int32 mnBlur; // [0 .. 180], radius of the blur
+ model::RectangleAlignment meAlignment{model::RectangleAlignment::Unset}; // alignment of the shadow
basegfx::BColor maColor; // color of shadow
ImpSdrShadowAttribute(
@@ -39,11 +41,13 @@ namespace drawinglayer::attribute
const basegfx::B2DVector& rSize,
double fTransparence,
sal_Int32 nBlur,
+ model::RectangleAlignment eAlignment,
const basegfx::BColor& rColor)
: maOffset(rOffset),
maSize(rSize),
mfTransparence(fTransparence),
mnBlur(nBlur),
+ meAlignment(eAlignment),
maColor(rColor)
{
}
@@ -67,6 +71,7 @@ namespace drawinglayer::attribute
&& getSize() == rCandidate.getSize()
&& getTransparence() == rCandidate.getTransparence()
&& getBlur() == rCandidate.getBlur()
+ && meAlignment == rCandidate.meAlignment
&& getColor() == rCandidate.getColor());
}
};
@@ -86,9 +91,10 @@ namespace drawinglayer::attribute
const basegfx::B2DVector& rSize,
double fTransparence,
sal_Int32 nBlur,
+ model::RectangleAlignment eAlignment,
const basegfx::BColor& rColor)
: mpSdrShadowAttribute(ImpSdrShadowAttribute(
- rOffset, rSize, fTransparence,nBlur, rColor))
+ rOffset, rSize, fTransparence, nBlur, eAlignment, rColor))
{
}
@@ -141,6 +147,11 @@ namespace drawinglayer::attribute
return mpSdrShadowAttribute->getBlur();
}
+ model::RectangleAlignment SdrShadowAttribute::getAlignment() const
+ {
+ return mpSdrShadowAttribute->meAlignment;
+ }
+
const basegfx::BColor& SdrShadowAttribute::getColor() const
{
return mpSdrShadowAttribute->getColor();
diff --git a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
index abca8a310925..22a20f095d24 100644
--- a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
+++ b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
@@ -24,7 +24,6 @@
#include <com/sun/star/uno/XComponentContext.hpp>
#include <cppuhelper/implbase2.hxx>
#include <cppuhelper/supportsservice.hxx>
-#include <comphelper/sequence.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <vcl/bitmapex.hxx>
@@ -44,7 +43,7 @@ namespace drawinglayer::unorenderer
namespace {
class XPrimitive2DRenderer:
- public cppu::WeakAggImplHelper2<
+ public cppu::WeakImplHelper<
css::graphic::XPrimitive2DRenderer, css::lang::XServiceInfo>
{
public:
@@ -100,7 +99,7 @@ namespace drawinglayer::unorenderer
const double fWidth(aRange.getWidth());
const double fHeight(aRange.getHeight());
- if(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0))
+ if(fWidth > 0.0 && fHeight > 0.0)
{
if(0 == DPI_X)
{
@@ -117,7 +116,15 @@ namespace drawinglayer::unorenderer
MaximumQuadraticPixels = 500000;
}
- const auto aViewInformation2D = geometry::createViewInformation2D(aViewInformationSequence);
+ auto aViewInformation2D = geometry::createViewInformation2D(aViewInformationSequence);
+
+ if(aViewInformation2D.getViewport().isEmpty())
+ {
+ // we have a Viewport since we create a discrete pixel device, use it
+ // if none is given
+ aViewInformation2D.setViewport(aRange);
+ }
+
const sal_uInt32 nDiscreteWidth(basegfx::fround(o3tl::convert(fWidth, eRangeUnit, o3tl::Length::in) * DPI_X));
const sal_uInt32 nDiscreteHeight(basegfx::fround(o3tl::convert(fHeight, eRangeUnit, o3tl::Length::in) * DPI_Y));
@@ -133,7 +140,7 @@ namespace drawinglayer::unorenderer
const primitive2d::Primitive2DReference xEmbedRef(
new primitive2d::TransformPrimitive2D(
aEmbedding,
- comphelper::sequenceToContainer<primitive2d::Primitive2DContainer>(aPrimitive2DSequence)));
+ aPrimitive2DSequence));
primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
BitmapEx aBitmapEx(
@@ -147,7 +154,7 @@ namespace drawinglayer::unorenderer
if(!aBitmapEx.IsEmpty())
{
aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
- aBitmapEx.SetPrefSize(Size(basegfx::fround(fWidth), basegfx::fround(fHeight)));
+ aBitmapEx.SetPrefSize(Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight)));
XBitmap = vcl::unotools::xBitmapFromBitmapEx(aBitmapEx);
}
}
diff --git a/drawinglayer/source/dumper/XShapeDumper.cxx b/drawinglayer/source/dumper/XShapeDumper.cxx
index 1434de2ba219..cf0da17e4398 100644
--- a/drawinglayer/source/dumper/XShapeDumper.cxx
+++ b/drawinglayer/source/dumper/XShapeDumper.cxx
@@ -1077,9 +1077,8 @@ void dumpInteropGrabBagAsElement(const uno::Sequence< beans::PropertyValue>& aIn
{
(void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "InteropGrabBag" ));
- sal_Int32 nLength = aInteropGrabBag.getLength();
- for (sal_Int32 i = 0; i < nLength; ++i)
- dumpPropertyValueAsElement(aInteropGrabBag[i], xmlWriter);
+ for (const auto& item: aInteropGrabBag)
+ dumpPropertyValueAsElement(item, xmlWriter);
(void)xmlTextWriterEndElement( xmlWriter );
}
@@ -1957,7 +1956,7 @@ OUString XShapeDumper::dump(const uno::Reference<drawing::XShapes>& xPageShapes,
(void)xmlTextWriterEndDocument( xmlWriter );
xmlFreeTextWriter( xmlWriter );
- return OUString::fromUtf8(aString.makeStringAndClear());
+ return OUString::fromUtf8(aString);
}
OUString XShapeDumper::dump(const uno::Reference<drawing::XShape>& xPageShapes, bool bDumpInteropProperties)
@@ -1981,7 +1980,7 @@ OUString XShapeDumper::dump(const uno::Reference<drawing::XShape>& xPageShapes,
(void)xmlTextWriterEndDocument( xmlWriter );
xmlFreeTextWriter( xmlWriter );
- return OUString::fromUtf8(aString.makeStringAndClear());
+ return OUString::fromUtf8(aString);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/geometry/viewinformation2d.cxx b/drawinglayer/source/geometry/viewinformation2d.cxx
index 125689939b21..296e34a0def2 100644
--- a/drawinglayer/source/geometry/viewinformation2d.cxx
+++ b/drawinglayer/source/geometry/viewinformation2d.cxx
@@ -25,7 +25,12 @@
#include <com/sun/star/drawing/XDrawPage.hpp>
#include <com/sun/star/geometry/AffineMatrix2D.hpp>
#include <com/sun/star/geometry/RealRectangle2D.hpp>
-#include <com/sun/star/uno/Sequence.hxx>
+#include <o3tl/temporary.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <atomic>
+#include <utility>
using namespace com::sun::star;
@@ -39,6 +44,14 @@ constexpr OUStringLiteral g_PropertyName_Viewport = u"Viewport";
constexpr OUStringLiteral g_PropertyName_Time = u"Time";
constexpr OUStringLiteral g_PropertyName_VisualizedPage = u"VisualizedPage";
constexpr OUStringLiteral g_PropertyName_ReducedDisplayQuality = u"ReducedDisplayQuality";
+constexpr OUStringLiteral g_PropertyName_UseAntiAliasing = u"UseAntiAliasing";
+constexpr OUStringLiteral g_PropertyName_PixelSnapHairline = u"PixelSnapHairline";
+}
+
+namespace
+{
+bool bForwardsAreInitialized(false);
+bool bForwardPixelSnapHairline(true);
}
class ImpViewInformation2D
@@ -74,34 +87,69 @@ protected:
// the point in time
double mfViewTime;
+ // color to use for automatic color
+ Color maAutoColor;
+
+ // a hint that the View that is being painted has an active TextEdit. This
+ // is important for handling of TextHierarchyEditPrimitive2D to suppress
+ // the text for objects in TextEdit - the text is visualized by the
+ // active EditEngine/Outliner overlay, so it would be double visualized
+ bool mbTextEditActive : 1;
+
+ // processed view is an EditView
+ bool mbEditViewActive : 1;
+
+ // allow to reduce DisplayQuality (e.g. sw 3d fallback renderer for interactions)
bool mbReducedDisplayQuality : 1;
-public:
- ImpViewInformation2D(const basegfx::B2DHomMatrix& rObjectTransformation,
- const basegfx::B2DHomMatrix& rViewTransformation,
- const basegfx::B2DRange& rViewport,
- const uno::Reference<drawing::XDrawPage>& rxDrawPage, double fViewTime,
- bool bReducedDisplayQuality)
- : maObjectTransformation(rObjectTransformation)
- , maViewTransformation(rViewTransformation)
- , maViewport(rViewport)
- , mxVisualizedPage(rxDrawPage)
- , mfViewTime(fViewTime)
- , mbReducedDisplayQuality(bReducedDisplayQuality)
- {
- }
+ // determine if to use AntiAliasing on target pixel device
+ bool mbUseAntiAliasing : 1;
+
+ // determine if to use PixelSnapHairline on target pixel device
+ bool mbPixelSnapHairline : 1;
+public:
ImpViewInformation2D()
- : mfViewTime(0.0)
+ : maObjectTransformation()
+ , maViewTransformation()
+ , maObjectToViewTransformation()
+ , maInverseObjectToViewTransformation()
+ , maViewport()
+ , maDiscreteViewport()
+ , mxVisualizedPage()
+ , mfViewTime(0.0)
+ , maAutoColor(COL_AUTO)
+ , mbTextEditActive(false)
+ , mbEditViewActive(false)
, mbReducedDisplayQuality(false)
+ , mbUseAntiAliasing(ViewInformation2D::getGlobalAntiAliasing())
+ , mbPixelSnapHairline(mbUseAntiAliasing && bForwardPixelSnapHairline)
{
}
const basegfx::B2DHomMatrix& getObjectTransformation() const { return maObjectTransformation; }
+ void setObjectTransformation(const basegfx::B2DHomMatrix& rNew)
+ {
+ maObjectTransformation = rNew;
+ maObjectToViewTransformation.identity();
+ maInverseObjectToViewTransformation.identity();
+ }
const basegfx::B2DHomMatrix& getViewTransformation() const { return maViewTransformation; }
+ void setViewTransformation(const basegfx::B2DHomMatrix& rNew)
+ {
+ maViewTransformation = rNew;
+ maDiscreteViewport.reset();
+ maObjectToViewTransformation.identity();
+ maInverseObjectToViewTransformation.identity();
+ }
const basegfx::B2DRange& getViewport() const { return maViewport; }
+ void setViewport(const basegfx::B2DRange& rNew)
+ {
+ maViewport = rNew;
+ maDiscreteViewport.reset();
+ }
const basegfx::B2DRange& getDiscreteViewport() const
{
@@ -143,10 +191,37 @@ public:
}
double getViewTime() const { return mfViewTime; }
+ void setViewTime(double fNew)
+ {
+ if (fNew >= 0.0)
+ {
+ mfViewTime = fNew;
+ }
+ }
const uno::Reference<drawing::XDrawPage>& getVisualizedPage() const { return mxVisualizedPage; }
+ void setVisualizedPage(const uno::Reference<drawing::XDrawPage>& rNew)
+ {
+ mxVisualizedPage = rNew;
+ }
+
+ Color getAutoColor() const { return maAutoColor; }
+ void setAutoColor(Color aNew) { maAutoColor = aNew; }
+
+ bool getTextEditActive() const { return mbTextEditActive; }
+ void setTextEditActive(bool bNew) { mbTextEditActive = bNew; }
+
+ bool getEditViewActive() const { return mbEditViewActive; }
+ void setEditViewActive(bool bNew) { mbEditViewActive = bNew; }
bool getReducedDisplayQuality() const { return mbReducedDisplayQuality; }
+ void setReducedDisplayQuality(bool bNew) { mbReducedDisplayQuality = bNew; }
+
+ bool getUseAntiAliasing() const { return mbUseAntiAliasing; }
+ void setUseAntiAliasing(bool bNew) { mbUseAntiAliasing = bNew; }
+
+ bool getPixelSnapHairline() const { return mbPixelSnapHairline; }
+ void setPixelSnapHairline(bool bNew) { mbPixelSnapHairline = bNew; }
bool operator==(const ImpViewInformation2D& rCandidate) const
{
@@ -154,7 +229,12 @@ public:
&& maViewTransformation == rCandidate.maViewTransformation
&& maViewport == rCandidate.maViewport
&& mxVisualizedPage == rCandidate.mxVisualizedPage
- && mfViewTime == rCandidate.mfViewTime);
+ && mfViewTime == rCandidate.mfViewTime && maAutoColor == rCandidate.maAutoColor
+ && mbTextEditActive == rCandidate.mbTextEditActive
+ && mbEditViewActive == rCandidate.mbEditViewActive
+ && mbReducedDisplayQuality == rCandidate.mbReducedDisplayQuality
+ && mbUseAntiAliasing == rCandidate.mbUseAntiAliasing
+ && mbPixelSnapHairline == rCandidate.mbPixelSnapHairline);
}
};
@@ -167,20 +247,21 @@ ViewInformation2D::ImplType& theGlobalDefault()
}
}
-ViewInformation2D::ViewInformation2D(const basegfx::B2DHomMatrix& rObjectTransformation,
- const basegfx::B2DHomMatrix& rViewTransformation,
- const basegfx::B2DRange& rViewport,
- const uno::Reference<drawing::XDrawPage>& rxDrawPage,
- double fViewTime, bool bReducedDisplayQuality)
- : mpViewInformation2D(ImpViewInformation2D(rObjectTransformation, rViewTransformation,
- rViewport, rxDrawPage, fViewTime,
- bReducedDisplayQuality))
-{
-}
-
ViewInformation2D::ViewInformation2D()
: mpViewInformation2D(theGlobalDefault())
{
+ if (!bForwardsAreInitialized)
+ {
+ bForwardsAreInitialized = true;
+ if (!comphelper::IsFuzzing())
+ {
+ bForwardPixelSnapHairline
+ = officecfg::Office::Common::Drawinglayer::SnapHorVerLinesToDiscrete::get();
+ }
+ }
+
+ setUseAntiAliasing(ViewInformation2D::getGlobalAntiAliasing());
+ setPixelSnapHairline(bForwardPixelSnapHairline);
}
ViewInformation2D::ViewInformation2D(const ViewInformation2D&) = default;
@@ -203,23 +284,53 @@ const basegfx::B2DHomMatrix& ViewInformation2D::getObjectTransformation() const
return mpViewInformation2D->getObjectTransformation();
}
+void ViewInformation2D::setObjectTransformation(const basegfx::B2DHomMatrix& rNew)
+{
+ if (std::as_const(mpViewInformation2D)->getObjectTransformation() != rNew)
+ mpViewInformation2D->setObjectTransformation(rNew);
+}
+
const basegfx::B2DHomMatrix& ViewInformation2D::getViewTransformation() const
{
return mpViewInformation2D->getViewTransformation();
}
+void ViewInformation2D::setViewTransformation(const basegfx::B2DHomMatrix& rNew)
+{
+ if (std::as_const(mpViewInformation2D)->getViewTransformation() != rNew)
+ mpViewInformation2D->setViewTransformation(rNew);
+}
+
const basegfx::B2DRange& ViewInformation2D::getViewport() const
{
return mpViewInformation2D->getViewport();
}
+void ViewInformation2D::setViewport(const basegfx::B2DRange& rNew)
+{
+ if (rNew != std::as_const(mpViewInformation2D)->getViewport())
+ mpViewInformation2D->setViewport(rNew);
+}
+
double ViewInformation2D::getViewTime() const { return mpViewInformation2D->getViewTime(); }
+void ViewInformation2D::setViewTime(double fNew)
+{
+ if (fNew != std::as_const(mpViewInformation2D)->getViewTime())
+ mpViewInformation2D->setViewTime(fNew);
+}
+
const uno::Reference<drawing::XDrawPage>& ViewInformation2D::getVisualizedPage() const
{
return mpViewInformation2D->getVisualizedPage();
}
+void ViewInformation2D::setVisualizedPage(const uno::Reference<drawing::XDrawPage>& rNew)
+{
+ if (rNew != std::as_const(mpViewInformation2D)->getVisualizedPage())
+ mpViewInformation2D->setVisualizedPage(rNew);
+}
+
const basegfx::B2DHomMatrix& ViewInformation2D::getObjectToViewTransformation() const
{
return mpViewInformation2D->getObjectToViewTransformation();
@@ -240,55 +351,155 @@ bool ViewInformation2D::getReducedDisplayQuality() const
return mpViewInformation2D->getReducedDisplayQuality();
}
+void ViewInformation2D::setReducedDisplayQuality(bool bNew)
+{
+ if (bNew != std::as_const(mpViewInformation2D)->getReducedDisplayQuality())
+ mpViewInformation2D->setReducedDisplayQuality(bNew);
+}
+
+bool ViewInformation2D::getUseAntiAliasing() const
+{
+ return mpViewInformation2D->getUseAntiAliasing();
+}
+
+void ViewInformation2D::setUseAntiAliasing(bool bNew)
+{
+ if (bNew != std::as_const(mpViewInformation2D)->getUseAntiAliasing())
+ mpViewInformation2D->setUseAntiAliasing(bNew);
+}
+
+Color ViewInformation2D::getAutoColor() const { return mpViewInformation2D->getAutoColor(); }
+
+void ViewInformation2D::setAutoColor(Color aNew) { mpViewInformation2D->setAutoColor(aNew); }
+
+bool ViewInformation2D::getTextEditActive() const
+{
+ return mpViewInformation2D->getTextEditActive();
+}
+
+void ViewInformation2D::setTextEditActive(bool bNew)
+{
+ mpViewInformation2D->setTextEditActive(bNew);
+}
+
+bool ViewInformation2D::getEditViewActive() const
+{
+ return mpViewInformation2D->getEditViewActive();
+}
+
+void ViewInformation2D::setEditViewActive(bool bNew)
+{
+ mpViewInformation2D->setEditViewActive(bNew);
+}
+
+bool ViewInformation2D::getPixelSnapHairline() const
+{
+ return mpViewInformation2D->getPixelSnapHairline();
+}
+
+void ViewInformation2D::setPixelSnapHairline(bool bNew)
+{
+ if (bNew != std::as_const(mpViewInformation2D)->getPixelSnapHairline())
+ mpViewInformation2D->setPixelSnapHairline(bNew);
+}
+
+static std::atomic<bool>& globalAntiAliasing()
+{
+ static std::atomic<bool> g_GlobalAntiAliasing
+ = comphelper::IsFuzzing() || officecfg::Office::Common::Drawinglayer::AntiAliasing::get();
+ return g_GlobalAntiAliasing;
+}
+
+/**
+ * Some code like to turn this stuff on and off during a drawing operation
+ * so it can "tunnel" information down through several layers,
+ * so we don't want to actually do a config write all the time.
+ */
+void ViewInformation2D::setGlobalAntiAliasing(bool bAntiAliasing, bool bTemporary)
+{
+ if (globalAntiAliasing().compare_exchange_strong(o3tl::temporary(!bAntiAliasing), bAntiAliasing)
+ && !bTemporary)
+ {
+ auto batch = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Drawinglayer::AntiAliasing::set(bAntiAliasing, batch);
+ batch->commit();
+ }
+}
+bool ViewInformation2D::getGlobalAntiAliasing() { return globalAntiAliasing(); }
+
+void ViewInformation2D::forwardPixelSnapHairline(bool bPixelSnapHairline)
+{
+ bForwardPixelSnapHairline = bPixelSnapHairline;
+}
+
ViewInformation2D
createViewInformation2D(const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters)
{
if (!rViewParameters.hasElements())
return ViewInformation2D();
- bool bReducedDisplayQuality = false;
- basegfx::B2DHomMatrix aObjectTransformation;
- basegfx::B2DHomMatrix aViewTransformation;
- basegfx::B2DRange aViewport;
- double fViewTime = 0.0;
- uno::Reference<drawing::XDrawPage> xVisualizedPage;
+ ViewInformation2D aRetval;
for (auto const& rPropertyValue : rViewParameters)
{
if (rPropertyValue.Name == g_PropertyName_ReducedDisplayQuality)
{
- rPropertyValue.Value >>= bReducedDisplayQuality;
+ bool bNew(false);
+ rPropertyValue.Value >>= bNew;
+ aRetval.setReducedDisplayQuality(bNew);
+ }
+ else if (rPropertyValue.Name == g_PropertyName_PixelSnapHairline)
+ {
+ bool bNew(
+ true); //SvtOptionsDrawinglayer::IsAntiAliasing() && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete());
+ rPropertyValue.Value >>= bNew;
+ aRetval.setPixelSnapHairline(bNew);
+ }
+ else if (rPropertyValue.Name == g_PropertyName_UseAntiAliasing)
+ {
+ bool bNew(true); //SvtOptionsDrawinglayer::IsAntiAliasing());
+ rPropertyValue.Value >>= bNew;
+ aRetval.setUseAntiAliasing(bNew);
}
else if (rPropertyValue.Name == g_PropertyName_ObjectTransformation)
{
css::geometry::AffineMatrix2D aAffineMatrix2D;
rPropertyValue.Value >>= aAffineMatrix2D;
- basegfx::unotools::homMatrixFromAffineMatrix(aObjectTransformation, aAffineMatrix2D);
+ basegfx::B2DHomMatrix aTransformation;
+ basegfx::unotools::homMatrixFromAffineMatrix(aTransformation, aAffineMatrix2D);
+ aRetval.setObjectTransformation(aTransformation);
}
else if (rPropertyValue.Name == g_PropertyName_ViewTransformation)
{
css::geometry::AffineMatrix2D aAffineMatrix2D;
rPropertyValue.Value >>= aAffineMatrix2D;
- basegfx::unotools::homMatrixFromAffineMatrix(aViewTransformation, aAffineMatrix2D);
+ basegfx::B2DHomMatrix aTransformation;
+ basegfx::unotools::homMatrixFromAffineMatrix(aTransformation, aAffineMatrix2D);
+ aRetval.setViewTransformation(aTransformation);
}
else if (rPropertyValue.Name == g_PropertyName_Viewport)
{
css::geometry::RealRectangle2D aUnoViewport;
rPropertyValue.Value >>= aUnoViewport;
- aViewport = basegfx::unotools::b2DRectangleFromRealRectangle2D(aUnoViewport);
+ const basegfx::B2DRange aViewport(
+ basegfx::unotools::b2DRectangleFromRealRectangle2D(aUnoViewport));
+ aRetval.setViewport(aViewport);
}
else if (rPropertyValue.Name == g_PropertyName_Time)
{
+ double fViewTime(0.0);
rPropertyValue.Value >>= fViewTime;
+ aRetval.setViewTime(fViewTime);
}
else if (rPropertyValue.Name == g_PropertyName_VisualizedPage)
{
+ css::uno::Reference<css::drawing::XDrawPage> xVisualizedPage;
rPropertyValue.Value >>= xVisualizedPage;
+ aRetval.setVisualizedPage(xVisualizedPage);
}
}
- return ViewInformation2D(aObjectTransformation, aViewTransformation, aViewport, xVisualizedPage,
- fViewTime, bReducedDisplayQuality);
+ return aRetval;
}
} // end of namespace drawinglayer::geometry
diff --git a/drawinglayer/source/geometry/viewinformation3d.cxx b/drawinglayer/source/geometry/viewinformation3d.cxx
index a2a444ab3543..bfe601f976e8 100644
--- a/drawinglayer/source/geometry/viewinformation3d.cxx
+++ b/drawinglayer/source/geometry/viewinformation3d.cxx
@@ -23,6 +23,7 @@
#include <com/sun/star/geometry/AffineMatrix3D.hpp>
#include <basegfx/utils/canvastools.hxx>
#include <com/sun/star/uno/Sequence.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -69,50 +70,15 @@ namespace drawinglayer::geometry
uno::Sequence< beans::PropertyValue > mxExtendedInformation;
// the local UNO API strings
- static OUString getNamePropertyObjectTransformation()
- {
- return "ObjectTransformation";
- }
-
- static OUString getNamePropertyOrientation()
- {
- return "Orientation";
- }
-
- static OUString getNamePropertyProjection()
- {
- return "Projection";
- }
-
- static OUString getNamePropertyProjection_30()
- {
- return "Projection30";
- }
-
- static OUString getNamePropertyProjection_31()
- {
- return "Projection31";
- }
-
- static OUString getNamePropertyProjection_32()
- {
- return "Projection32";
- }
-
- static OUString getNamePropertyProjection_33()
- {
- return "Projection33";
- }
-
- static OUString getNamePropertyDeviceToView()
- {
- return "DeviceToView";
- }
-
- static OUString getNamePropertyTime()
- {
- return "Time";
- }
+ static constexpr OUString OBJECT_TRANSFORMATION = u"ObjectTransformation"_ustr;
+ static constexpr OUString ORIENTATION = u"Orientation"_ustr;
+ static constexpr OUString PROJECTION = u"Projection"_ustr;
+ static constexpr OUString PROJECTION30 = u"Projection30"_ustr;
+ static constexpr OUString PROJECTION31 = u"Projection31"_ustr;
+ static constexpr OUString PROJECTION32 = u"Projection32"_ustr;
+ static constexpr OUString PROJECTION33 = u"Projection33"_ustr;
+ static constexpr OUString DEVICE_TO_VIEW = u"DeviceToView"_ustr;
+ static constexpr OUString TIME = u"Time"_ustr;
// a central PropertyValue parsing method to allow transportation of
// all ViewParameters using UNO API
@@ -132,19 +98,19 @@ namespace drawinglayer::geometry
{
const beans::PropertyValue& rProp = rViewParameters[a];
- if(rProp.Name == getNamePropertyObjectTransformation())
+ if(rProp.Name == OBJECT_TRANSFORMATION)
{
css::geometry::AffineMatrix3D aAffineMatrix3D;
rProp.Value >>= aAffineMatrix3D;
maObjectTransformation = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
}
- else if(rProp.Name == getNamePropertyOrientation())
+ else if(rProp.Name == ORIENTATION)
{
css::geometry::AffineMatrix3D aAffineMatrix3D;
rProp.Value >>= aAffineMatrix3D;
maOrientation = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
}
- else if(rProp.Name == getNamePropertyProjection())
+ else if(rProp.Name == PROJECTION)
{
// projection may be defined using a frustum in which case the last line of
// the 4x4 matrix is not (0,0,0,1). Since AffineMatrix3D does not support that,
@@ -163,37 +129,37 @@ namespace drawinglayer::geometry
maProjection.set(3, 2, f_32);
maProjection.set(3, 3, f_33);
}
- else if(rProp.Name == getNamePropertyProjection_30())
+ else if(rProp.Name == PROJECTION30)
{
double f_30(0.0);
rProp.Value >>= f_30;
maProjection.set(3, 0, f_30);
}
- else if(rProp.Name == getNamePropertyProjection_31())
+ else if(rProp.Name == PROJECTION31)
{
double f_31(0.0);
rProp.Value >>= f_31;
maProjection.set(3, 1, f_31);
}
- else if(rProp.Name == getNamePropertyProjection_32())
+ else if(rProp.Name == PROJECTION32)
{
double f_32(0.0);
rProp.Value >>= f_32;
maProjection.set(3, 2, f_32);
}
- else if(rProp.Name == getNamePropertyProjection_33())
+ else if(rProp.Name == PROJECTION33)
{
double f_33(1.0);
rProp.Value >>= f_33;
maProjection.set(3, 3, f_33);
}
- else if(rProp.Name == getNamePropertyDeviceToView())
+ else if(rProp.Name == DEVICE_TO_VIEW)
{
css::geometry::AffineMatrix3D aAffineMatrix3D;
rProp.Value >>= aAffineMatrix3D;
maDeviceToView = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D);
}
- else if(rProp.Name == getNamePropertyTime())
+ else if(rProp.Name == TIME)
{
rProp.Value >>= mfViewTime;
}
@@ -210,16 +176,16 @@ namespace drawinglayer::geometry
public:
ImpViewInformation3D(
- const basegfx::B3DHomMatrix& rObjectTransformation,
- const basegfx::B3DHomMatrix& rOrientation,
- const basegfx::B3DHomMatrix& rProjection,
- const basegfx::B3DHomMatrix& rDeviceToView,
+ basegfx::B3DHomMatrix aObjectTransformation,
+ basegfx::B3DHomMatrix aOrientation,
+ basegfx::B3DHomMatrix aProjection,
+ basegfx::B3DHomMatrix aDeviceToView,
double fViewTime,
const uno::Sequence< beans::PropertyValue >& rExtendedParameters)
- : maObjectTransformation(rObjectTransformation),
- maOrientation(rOrientation),
- maProjection(rProjection),
- maDeviceToView(rDeviceToView),
+ : maObjectTransformation(std::move(aObjectTransformation)),
+ maOrientation(std::move(aOrientation)),
+ maProjection(std::move(aProjection)),
+ maDeviceToView(std::move(aDeviceToView)),
mfViewTime(fViewTime)
{
impInterpretPropertyValues(rExtendedParameters);
diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
new file mode 100644
index 000000000000..4a09d0490ce9
--- /dev/null
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+namespace
+{
+class LocalCallbackTimer : public salhelper::Timer
+{
+protected:
+ drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D* pCustomer;
+
+public:
+ explicit LocalCallbackTimer(
+ drawinglayer::primitive2d::BufferedDecompositionGroupPrimitive2D& rCustomer)
+ : pCustomer(&rCustomer)
+ {
+ }
+
+ void clearCallback() { pCustomer = nullptr; }
+
+protected:
+ virtual void SAL_CALL onShot() override;
+};
+
+void SAL_CALL LocalCallbackTimer::onShot()
+{
+ if (nullptr != pCustomer)
+ flushBufferedDecomposition(*pCustomer);
+}
+}
+
+namespace drawinglayer::primitive2d
+{
+void flushBufferedDecomposition(BufferedDecompositionGroupPrimitive2D& rTarget)
+{
+ rTarget.acquire();
+ rTarget.setBuffered2DDecomposition(Primitive2DContainer());
+ rTarget.release();
+}
+
+const Primitive2DContainer&
+BufferedDecompositionGroupPrimitive2D::getBuffered2DDecomposition() const
+{
+ if (0 != maCallbackSeconds && maCallbackTimer.is())
+ {
+ // decomposition was used, touch/restart time
+ maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+ }
+
+ return maBuffered2DDecomposition;
+}
+
+void BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&& rNew)
+{
+ if (0 == maCallbackSeconds)
+ {
+ // no flush used, just set
+ maBuffered2DDecomposition = std::move(rNew);
+ return;
+ }
+
+ if (maCallbackTimer.is())
+ {
+ if (rNew.empty())
+ {
+ // stop timer
+ maCallbackTimer->stop();
+ }
+ else
+ {
+ // decomposition changed, touch
+ maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+ if (!maCallbackTimer->isTicking())
+ maCallbackTimer->start();
+ }
+ }
+ else if (!rNew.empty())
+ {
+ // decomposition defined/set/changed, init & start timer
+ maCallbackTimer.set(new LocalCallbackTimer(*this));
+ maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+ maCallbackTimer->start();
+ }
+
+ // tdf#158913 need to secure change when flush/multithreading is in use
+ std::lock_guard Guard(maCallbackLock);
+ maBuffered2DDecomposition = std::move(rNew);
+}
+
+BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D(
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ , maCallbackTimer()
+ , maCallbackLock()
+ , maCallbackSeconds(0)
+{
+}
+
+BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D()
+{
+ if (maCallbackTimer.is())
+ {
+ // no more decomposition, end callback
+ static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback();
+ maCallbackTimer->stop();
+ }
+}
+
+void BufferedDecompositionGroupPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getBuffered2DDecomposition().empty())
+ {
+ Primitive2DContainer aNewSequence;
+ create2DDecomposition(aNewSequence, rViewInformation);
+ const_cast<BufferedDecompositionGroupPrimitive2D*>(this)->setBuffered2DDecomposition(
+ std::move(aNewSequence));
+ }
+
+ if (0 == maCallbackSeconds)
+ {
+ // no flush/multithreading is in use, just call
+ rVisitor.visit(getBuffered2DDecomposition());
+ return;
+ }
+
+ // tdf#158913 need to secure 'visit' when flush/multithreading is in use,
+ // so that the local non-ref-Counted instance of the decomposition gets not
+ // manipulated (e.g. deleted)
+ std::lock_guard Guard(maCallbackLock);
+ rVisitor.visit(getBuffered2DDecomposition());
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
index 6846a3bcdd26..ba8a4606cc83 100644
--- a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
@@ -20,30 +20,133 @@
#include <sal/config.h>
#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
-#include <drawinglayer/primitive2d/Tools.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
-#include <basegfx/utils/canvastools.hxx>
-#include <comphelper/sequence.hxx>
+
+namespace
+{
+class LocalCallbackTimer : public salhelper::Timer
+{
+protected:
+ drawinglayer::primitive2d::BufferedDecompositionPrimitive2D* pCustomer;
+
+public:
+ explicit LocalCallbackTimer(
+ drawinglayer::primitive2d::BufferedDecompositionPrimitive2D& rCustomer)
+ : pCustomer(&rCustomer)
+ {
+ }
+
+ void clearCallback() { pCustomer = nullptr; }
+
+protected:
+ virtual void SAL_CALL onShot() override;
+};
+
+void SAL_CALL LocalCallbackTimer::onShot()
+{
+ if (nullptr != pCustomer)
+ flushBufferedDecomposition(*pCustomer);
+}
+}
namespace drawinglayer::primitive2d
{
-BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() {}
+void flushBufferedDecomposition(BufferedDecompositionPrimitive2D& rTarget)
+{
+ rTarget.acquire();
+ rTarget.setBuffered2DDecomposition(nullptr);
+ rTarget.release();
+}
+
+const Primitive2DReference& BufferedDecompositionPrimitive2D::getBuffered2DDecomposition() const
+{
+ if (0 != maCallbackSeconds && maCallbackTimer.is())
+ {
+ // decomposition was used, touch/restart time
+ maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+ }
+
+ return maBuffered2DDecomposition;
+}
+
+void BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DReference rNew)
+{
+ if (0 == maCallbackSeconds)
+ {
+ // no flush used, just set
+ maBuffered2DDecomposition = std::move(rNew);
+ return;
+ }
+
+ if (maCallbackTimer.is())
+ {
+ if (!rNew)
+ {
+ // stop timer
+ maCallbackTimer->stop();
+ }
+ else
+ {
+ // decomposition changed, touch
+ maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+ if (!maCallbackTimer->isTicking())
+ maCallbackTimer->start();
+ }
+ }
+ else if (rNew)
+ {
+ // decomposition defined/set/changed, init & start timer
+ maCallbackTimer.set(new LocalCallbackTimer(*this));
+ maCallbackTimer->setRemainingTime(salhelper::TTimeValue(maCallbackSeconds, 0));
+ maCallbackTimer->start();
+ }
+
+ // tdf#158913 need to secure change when flush/multithreading is in use
+ std::lock_guard Guard(maCallbackLock);
+ maBuffered2DDecomposition = std::move(rNew);
+}
+
+BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D()
+ : maBuffered2DDecomposition()
+ , maCallbackTimer()
+ , maCallbackLock()
+ , maCallbackSeconds(0)
+{
+}
+
+BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D()
+{
+ if (maCallbackTimer.is())
+ {
+ // no more decomposition, end callback
+ static_cast<LocalCallbackTimer*>(maCallbackTimer.get())->clearCallback();
+ maCallbackTimer->stop();
+ }
+}
void BufferedDecompositionPrimitive2D::get2DDecomposition(
Primitive2DDecompositionVisitor& rVisitor,
const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard(m_aMutex);
-
- if (getBuffered2DDecomposition().empty())
+ if (!getBuffered2DDecomposition())
{
- Primitive2DContainer aNewSequence;
- create2DDecomposition(aNewSequence, rViewInformation);
+ Primitive2DReference aNew = create2DDecomposition(rViewInformation);
const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition(
- std::move(aNewSequence));
+ std::move(aNew));
+ }
+
+ if (0 == maCallbackSeconds)
+ {
+ // no flush/multithreading is in use, just call
+ rVisitor.visit(getBuffered2DDecomposition());
+ return;
}
- rVisitor.append(getBuffered2DDecomposition());
+ // tdf#158913 need to secure 'visit' when flush/multithreading is in use,
+ // so that the local non-ref-Counted instance of the decomposition gets not
+ // manipulated (e.g. deleted)
+ std::lock_guard Guard(maCallbackLock);
+ rVisitor.visit(getBuffered2DDecomposition());
}
} // end of namespace drawinglayer::primitive2d
diff --git a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx
new file mode 100644
index 000000000000..e1dcac421344
--- /dev/null
+++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "GlowSoftEgdeShadowTools.hxx"
+#include <vcl/bitmapex.hxx>
+#include <vcl/BitmapFilter.hxx>
+#include <vcl/BitmapBasicMorphologyFilter.hxx>
+#include <vcl/BitmapFilterStackBlur.hxx>
+
+namespace drawinglayer::primitive2d
+{
+/* Returns 8-bit alpha mask created from passed mask.
+
+ Negative fErodeDilateRadius values mean erode, positive - dilate.
+ nTransparency defines minimal transparency level.
+*/
+AlphaMask ProcessAndBlurAlphaMask(const AlphaMask& rMask, double fErodeDilateRadius,
+ double fBlurRadius, sal_uInt8 nTransparency, bool bConvertTo1Bit)
+{
+ // Invert it to operate in the transparency domain. Trying to update this method to
+ // work in the alpha domain is fraught with hazards.
+ AlphaMask tmpMask = rMask;
+ tmpMask.Invert();
+
+ // Only completely white pixels on the initial mask must be considered for transparency. Any
+ // other color must be treated as black. This creates 1-bit B&W bitmap.
+ BitmapEx mask(bConvertTo1Bit ? tmpMask.GetBitmap().CreateMask(COL_WHITE) : tmpMask.GetBitmap());
+
+ // Scaling down increases performance without noticeable quality loss. Additionally,
+ // current blur implementation can only handle blur radius between 2 and 254.
+ Size aSize = mask.GetSizePixel();
+ double fScale = 1.0;
+ while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000)
+ {
+ fScale /= 2;
+ fBlurRadius /= 2;
+ fErodeDilateRadius /= 2;
+ aSize /= 2;
+ }
+
+ // BmpScaleFlag::NearestNeighbor is important for following color replacement
+ mask.Scale(fScale, fScale, BmpScaleFlag::NearestNeighbor);
+
+ if (fErodeDilateRadius > 0)
+ BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius));
+ else if (fErodeDilateRadius < 0)
+ BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF));
+
+ if (nTransparency)
+ {
+ const Color aTransparency(nTransparency, nTransparency, nTransparency);
+ mask.Replace(COL_BLACK, aTransparency);
+ }
+
+ // We need 8-bit grey mask for blurring
+ mask.Convert(BmpConversion::N8BitGreys);
+
+ // calculate blurry effect
+ BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius));
+
+ mask.Scale(rMask.GetSizePixel());
+
+ // And switch to the alpha domain.
+ mask.Invert();
+
+ return AlphaMask(mask.GetBitmap());
+}
+
+drawinglayer::geometry::ViewInformation2D
+expandB2DRangeAtViewInformation2D(const drawinglayer::geometry::ViewInformation2D& rViewInfo,
+ double nAmount)
+{
+ drawinglayer::geometry::ViewInformation2D aRetval(rViewInfo);
+ basegfx::B2DRange viewport(rViewInfo.getViewport());
+ viewport.grow(nAmount);
+ aRetval.setViewport(viewport);
+ return aRetval;
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
new file mode 100644
index 000000000000..b6a62be8863e
--- /dev/null
+++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/alpha.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+/* Returns 8-bit alpha mask created from passed mask.
+
+ Negative fErodeDilateRadius values mean erode, positive - dilate.
+ nTransparency defines minimal transparency level.
+*/
+AlphaMask ProcessAndBlurAlphaMask(const AlphaMask& rMask, double fErodeDilateRadius,
+ double fBlurRadius, sal_uInt8 nTransparency,
+ bool bConvertTo1Bit = true);
+
+drawinglayer::geometry::ViewInformation2D
+expandB2DRangeAtViewInformation2D(const drawinglayer::geometry::ViewInformation2D& rViewInfo,
+ double nAmount);
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx
index b3aed429ba0c..6c7cf4bb365a 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx
@@ -20,15 +20,17 @@
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-PolyPolygonColorPrimitive2D::PolyPolygonColorPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor)
- : maPolyPolygon(rPolyPolygon)
+PolyPolygonColorPrimitive2D::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maBColor(rBColor)
{
}
@@ -60,6 +62,54 @@ sal_uInt32 PolyPolygonColorPrimitive2D::getPrimitive2DID() const
return PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D;
}
+FilledRectanglePrimitive2D::FilledRectanglePrimitive2D(const basegfx::B2DRange& rB2DRange,
+ const basegfx::BColor& rBColor)
+ : BasePrimitive2D()
+ , maB2DRange(rB2DRange)
+ , maBColor(rBColor)
+{
+}
+
+bool FilledRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const FilledRectanglePrimitive2D& rCompare(
+ static_cast<const FilledRectanglePrimitive2D&>(rPrimitive));
+
+ return (getB2DRange() == rCompare.getB2DRange() && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange FilledRectanglePrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ return getB2DRange();
+}
+
+sal_uInt32 FilledRectanglePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D;
+}
+
+void FilledRectanglePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(getB2DRange()));
+ Primitive2DContainer aSequence
+ = { new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), getBColor()) };
+ rVisitor.visit(aSequence);
+}
+
} // end drawinglayer::primitive2d namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
index 3e88b6c0ddb0..4fe3321e62f8 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx
@@ -23,13 +23,14 @@
#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <rtl/ref.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonGradientPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonGradientPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (!getFillGradient().isDefault())
{
@@ -40,25 +41,25 @@ void PolyPolygonGradientPrimitive2D::create2DDecomposition(
Primitive2DContainer aSubSequence{ pNewGradient };
// create mask primitive
- rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)));
+ return new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence));
}
+ return nullptr;
}
PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon,
- const attribute::FillGradientAttribute& rFillGradient)
+ const basegfx::B2DPolyPolygon& rPolyPolygon, attribute::FillGradientAttribute aFillGradient)
: maPolyPolygon(rPolyPolygon)
, maDefinitionRange(rPolyPolygon.getB2DRange())
- , maFillGradient(rFillGradient)
+ , maFillGradient(std::move(aFillGradient))
{
}
PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
- const attribute::FillGradientAttribute& rFillGradient)
- : maPolyPolygon(rPolyPolygon)
+ basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ attribute::FillGradientAttribute aFillGradient)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maDefinitionRange(rDefinitionRange)
- , maFillGradient(rFillGradient)
+ , maFillGradient(std::move(aFillGradient))
{
}
diff --git a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
index a65d1d08a0f0..31a7acb03d7c 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
@@ -24,30 +24,31 @@
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <utility>
#include <vcl/graph.hxx>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonGraphicPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonGraphicPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (getFillGraphic().isDefault())
- return;
+ return nullptr;
const Graphic& rGraphic = getFillGraphic().getGraphic();
const GraphicType aType(rGraphic.GetType());
// is there a bitmap or a metafile (do we have content)?
if (GraphicType::Bitmap != aType && GraphicType::GdiMetafile != aType)
- return;
+ return nullptr;
const Size aPrefSize(rGraphic.GetPrefSize());
// does content have a size?
if (!(aPrefSize.Width() && aPrefSize.Height()))
- return;
+ return nullptr;
// create SubSequence with FillGraphicPrimitive2D based on polygon range
const basegfx::B2DRange aOutRange(getB2DPolyPolygon().getB2DRange());
@@ -92,13 +93,13 @@ void PolyPolygonGraphicPrimitive2D::create2DDecomposition(
}
// embed to mask primitive
- rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), Primitive2DContainer{ xSubRef }));
+ return new MaskPrimitive2D(getB2DPolyPolygon(), Primitive2DContainer{ xSubRef });
}
PolyPolygonGraphicPrimitive2D::PolyPolygonGraphicPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
const attribute::FillGraphicAttribute& rFillGraphic)
- : maPolyPolygon(rPolyPolygon)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maDefinitionRange(rDefinitionRange)
, maFillGraphic(rFillGraphic)
{
diff --git a/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
index 5bc291676dbc..88b15160e2c0 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
@@ -18,34 +18,35 @@
*/
#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonHairlinePrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonHairlinePrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
const sal_uInt32 nCount(aPolyPolygon.count());
- if (nCount)
+ Primitive2DContainer aContainer;
+ for (sal_uInt32 a(0); a < nCount; a++)
{
- for (sal_uInt32 a(0); a < nCount; a++)
- {
- rContainer.push_back(
- new PolygonHairlinePrimitive2D(aPolyPolygon.getB2DPolygon(a), getBColor()));
- }
+ aContainer.push_back(
+ new PolygonHairlinePrimitive2D(aPolyPolygon.getB2DPolygon(a), getBColor()));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
-PolyPolygonHairlinePrimitive2D::PolyPolygonHairlinePrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor)
- : maPolyPolygon(rPolyPolygon)
+PolyPolygonHairlinePrimitive2D::PolyPolygonHairlinePrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maBColor(rBColor)
{
}
diff --git a/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
index 7fc0502c8f43..2379e54e3de8 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
@@ -23,13 +23,14 @@
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
#include <rtl/ref.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonHatchPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonHatchPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (!getFillHatch().isDefault())
{
@@ -40,27 +41,29 @@ void PolyPolygonHatchPrimitive2D::create2DDecomposition(
Primitive2DContainer aSubSequence{ pNewHatch };
// create mask primitive
- rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)));
+ return new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence));
}
+ return nullptr;
}
PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(
const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBackgroundColor,
- const attribute::FillHatchAttribute& rFillHatch)
+ attribute::FillHatchAttribute rFillHatch)
: maPolyPolygon(rPolyPolygon)
, maDefinitionRange(rPolyPolygon.getB2DRange())
, maBackgroundColor(rBackgroundColor)
- , maFillHatch(rFillHatch)
+ , maFillHatch(std::move(rFillHatch))
{
}
-PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
- const basegfx::BColor& rBackgroundColor, const attribute::FillHatchAttribute& rFillHatch)
- : maPolyPolygon(rPolyPolygon)
+PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon,
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rBackgroundColor,
+ attribute::FillHatchAttribute rFillHatch)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maDefinitionRange(rDefinitionRange)
, maBackgroundColor(rBackgroundColor)
- , maFillHatch(rFillHatch)
+ , maFillHatch(std::move(rFillHatch))
{
}
diff --git a/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx
index 2381ed8f866c..9c9e3b1ae81f 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx
@@ -18,36 +18,38 @@
*/
#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonMarkerPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonMarkerPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
const sal_uInt32 nCount(aPolyPolygon.count());
- if (nCount)
+ Primitive2DContainer aContainer;
+ for (sal_uInt32 a(0); a < nCount; a++)
{
- for (sal_uInt32 a(0); a < nCount; a++)
- {
- rContainer.push_back(new PolygonMarkerPrimitive2D(aPolyPolygon.getB2DPolygon(a),
- getRGBColorA(), getRGBColorB(),
- getDiscreteDashLength()));
- }
+ aContainer.push_back(new PolygonMarkerPrimitive2D(aPolyPolygon.getB2DPolygon(a),
+ getRGBColorA(), getRGBColorB(),
+ getDiscreteDashLength()));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
-PolyPolygonMarkerPrimitive2D::PolyPolygonMarkerPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rRGBColorA,
- const basegfx::BColor& rRGBColorB, double fDiscreteDashLength)
- : maPolyPolygon(rPolyPolygon)
+PolyPolygonMarkerPrimitive2D::PolyPolygonMarkerPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maRGBColorA(rRGBColorA)
, maRGBColorB(rRGBColorB)
, mfDiscreteDashLength(fDiscreteDashLength)
diff --git a/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
index 3ad4840bd5bd..a5595a4bcaeb 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
@@ -25,16 +25,17 @@
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonSelectionPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonSelectionPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (getTransparence() >= 1.0 || !getB2DPolyPolygon().count())
- return;
+ return nullptr;
Primitive2DContainer aRetval;
@@ -66,13 +67,13 @@ void PolyPolygonSelectionPrimitive2D::create2DDecomposition(
aRetval = Primitive2DContainer{ aTrans };
}
- rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
+ return new GroupPrimitive2D(std::move(aRetval));
}
PolyPolygonSelectionPrimitive2D::PolyPolygonSelectionPrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rColor,
- double fTransparence, double fDiscreteGrow, bool bFill)
- : maPolyPolygon(rPolyPolygon)
+ basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::BColor& rColor, double fTransparence,
+ double fDiscreteGrow, bool bFill)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maColor(rColor)
, mfTransparence(fTransparence)
, mfDiscreteGrow(fabs(fDiscreteGrow))
diff --git a/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
index 91093f37278d..9c32d8853d8d 100644
--- a/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
+++ b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
@@ -21,40 +21,41 @@
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-void PolyPolygonStrokePrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolyPolygonStrokePrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
const sal_uInt32 nCount(aPolyPolygon.count());
- if (nCount)
+ Primitive2DContainer aContainer;
+ for (sal_uInt32 a(0); a < nCount; a++)
{
- for (sal_uInt32 a(0); a < nCount; a++)
- {
- rContainer.push_back(new PolygonStrokePrimitive2D(
- aPolyPolygon.getB2DPolygon(a), getLineAttribute(), getStrokeAttribute()));
- }
+ aContainer.push_back(new PolygonStrokePrimitive2D(
+ aPolyPolygon.getB2DPolygon(a), getLineAttribute(), getStrokeAttribute()));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute,
- const attribute::StrokeAttribute& rStrokeAttribute)
- : maPolyPolygon(rPolyPolygon)
+ basegfx::B2DPolyPolygon aPolyPolygon, const attribute::LineAttribute& rLineAttribute,
+ attribute::StrokeAttribute aStrokeAttribute)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maLineAttribute(rLineAttribute)
- , maStrokeAttribute(rStrokeAttribute)
+ , maStrokeAttribute(std::move(aStrokeAttribute))
{
}
PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D(
- const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute)
- : maPolyPolygon(rPolyPolygon)
+ basegfx::B2DPolyPolygon aPolyPolygon, const attribute::LineAttribute& rLineAttribute)
+ : maPolyPolygon(std::move(aPolyPolygon))
, maLineAttribute(rLineAttribute)
{
}
diff --git a/drawinglayer/source/primitive2d/Primitive2DContainer.cxx b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
index 3ae4a9b3e3c4..48b0c625e1ba 100644
--- a/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
+++ b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
@@ -21,30 +21,44 @@
#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
using namespace css;
namespace drawinglayer::primitive2d
{
-Primitive2DContainer Primitive2DContainer::maybeInvert(bool bInvert) const
+Primitive2DContainer::Primitive2DContainer(
+ const css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource)
{
- const sal_uInt32 nSize(size());
- Primitive2DContainer aRetval;
-
- aRetval.resize(nSize);
+ for (const auto& rPrimitive : rSource)
+ append(static_cast<const UnoPrimitive2D*>(rPrimitive.get())->getBasePrimitive2D());
+}
+Primitive2DContainer::Primitive2DContainer(
+ const std::deque<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource)
+{
+ for (const auto& rPrimitive : rSource)
+ append(static_cast<const UnoPrimitive2D*>(rPrimitive.get())->getBasePrimitive2D());
+}
- for (sal_uInt32 a(0); a < nSize; a++)
+css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>>
+Primitive2DContainer::toSequence() const
+{
+ css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>> aVal(size());
+ auto p = aVal.getArray();
+ for (const auto& rPrimitive : *this)
{
- aRetval[bInvert ? nSize - 1 - a : a] = (*this)[a];
+ *p = new UnoPrimitive2D(rPrimitive);
+ ++p;
}
+ return aVal;
+}
- // all entries taken over to Uno References as owners. To avoid
- // errors with users of this mechanism to delete pointers to BasePrimitive2D
- // itself, clear given vector
- const_cast<Primitive2DContainer&>(*this).clear();
-
- return aRetval;
+Primitive2DContainer Primitive2DContainer::maybeInvert(bool bInvert)
+{
+ if (bInvert)
+ std::reverse(begin(), end());
+ return std::move(*this);
}
// get B2DRange from a given Primitive2DSequence
@@ -113,9 +127,27 @@ void Primitive2DContainer::append(Primitive2DContainer&& rSource)
std::make_move_iterator(rSource.end()));
}
-void Primitive2DContainer::append(const Primitive2DSequence& rSource)
+UnoPrimitive2D::~UnoPrimitive2D() {}
+
+css::uno::Sequence<::css::uno::Reference<::css::graphic::XPrimitive2D>>
+ SAL_CALL UnoPrimitive2D::getDecomposition(
+ const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters)
+{
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->getDecomposition(rViewParameters).toSequence();
+}
+
+css::geometry::RealRectangle2D SAL_CALL
+UnoPrimitive2D::getRange(const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters)
+{
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->getRange(rViewParameters);
+}
+
+sal_Int64 SAL_CALL UnoPrimitive2D::estimateUsage()
{
- this->insert(this->end(), rSource.begin(), rSource.end());
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->estimateUsage();
}
} // end of namespace drawinglayer::primitive2d
diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx
index 7be666a2cbd0..e20c34a6c4f7 100644
--- a/drawinglayer/source/primitive2d/Tools.cxx
+++ b/drawinglayer/source/primitive2d/Tools.cxx
@@ -21,7 +21,6 @@
#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
-#include <basegfx/utils/canvastools.hxx>
using namespace css;
@@ -35,9 +34,7 @@ getB2DRangeFromPrimitive2DReference(const Primitive2DReference& rCandidate,
if (!rCandidate)
return basegfx::B2DRange();
- // get C++ implementation base
- const BasePrimitive2D* pCandidate(static_cast<BasePrimitive2D*>(rCandidate.get()));
- return pCandidate->getB2DRange(aViewInformation);
+ return rCandidate->getB2DRange(aViewInformation);
}
bool arePrimitive2DReferencesEqual(const Primitive2DReference& rxA, const Primitive2DReference& rxB)
@@ -54,10 +51,28 @@ bool arePrimitive2DReferencesEqual(const Primitive2DReference& rxA, const Primit
return true;
}
- const BasePrimitive2D* pA(static_cast<const BasePrimitive2D*>(rxA.get()));
- const BasePrimitive2D* pB(static_cast<const BasePrimitive2D*>(rxB.get()));
+ return rxA->operator==(*rxB);
+}
+
+bool arePrimitive2DReferencesEqual(const css::uno::Reference<css::graphic::XPrimitive2D>& rxA,
+ const css::uno::Reference<css::graphic::XPrimitive2D>& rxB)
+{
+ const bool bAIs(rxA.is());
+
+ if (bAIs != rxB.is())
+ {
+ return false;
+ }
+
+ if (!bAIs)
+ {
+ return true;
+ }
+
+ auto pA = static_cast<const UnoPrimitive2D*>(rxA.get());
+ auto pB = static_cast<const UnoPrimitive2D*>(rxB.get());
- return pA->operator==(*pB);
+ return (*pA->getBasePrimitive2D()) == (*pB->getBasePrimitive2D());
}
OUString idToString(sal_uInt32 nId)
@@ -210,6 +225,14 @@ OUString idToString(sal_uInt32 nId)
return "GLOWPRIMITIVE";
case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
return "SOFTEDGEPRIMITIVE";
+ case PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D:
+ return "LINERECTANGLEPRIMITIVE";
+ case PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D:
+ return "FILLEDRECTANGLEPRIMITIVE";
+ case PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D:
+ return "SINGLELINEPRIMITIVE";
+ case PRIMITIVE2D_ID_EXCLUSIVEEDITVIEWPRIMITIVE2D:
+ return "EXCLUSIVEEDITVIEWPRIMITIVE2D";
default:
return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
}
diff --git a/drawinglayer/source/primitive2d/animatedprimitive2d.cxx b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
index adb66dddb36c..87f524180fbe 100644
--- a/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
@@ -76,8 +76,7 @@ namespace drawinglayer::primitive2d
nIndex = nLen - 1;
}
- const Primitive2DReference xRef(getChildren()[nIndex], uno::UNO_SET_THROW);
- rVisitor.append(xRef);
+ rVisitor.visit(getChildren()[nIndex]);
}
// provide unique ID
@@ -132,9 +131,9 @@ namespace drawinglayer::primitive2d
const sal_uInt32 nCount(rmMatrixStack.size());
maMatrixStack.reserve(nCount);
- for(sal_uInt32 a(0); a < nCount; a++)
+ for(const auto& a : rmMatrixStack)
{
- maMatrixStack.emplace_back(rmMatrixStack[a]);
+ maMatrixStack.emplace_back(a);
}
}
@@ -185,7 +184,7 @@ namespace drawinglayer::primitive2d
// create new transform primitive reference, return new sequence
Primitive2DReference xRef(new TransformPrimitive2D(aTargetTransform, Primitive2DContainer(getChildren())));
- rVisitor.append(xRef);
+ rVisitor.visit(xRef);
}
else
{
diff --git a/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
index 68f31cb4c67a..ea1b2a56942a 100644
--- a/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
@@ -21,6 +21,7 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
@@ -30,13 +31,31 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void BackgroundColorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference BackgroundColorPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
- if(!rViewInformation.getViewport().isEmpty())
+ // transparency invalid or completely transparent, done
+ if(getTransparency() < 0.0 || getTransparency() >= 1.0)
+ return nullptr;
+
+ // no viewport, not visible, done
+ if(rViewInformation.getViewport().isEmpty())
+ return nullptr;
+
+ // create decompose geometry
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rViewInformation.getViewport()));
+ Primitive2DReference aDecompose(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), getBColor()));
+
+ if(getTransparency() != 0.0)
{
- const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rViewInformation.getViewport()));
- rContainer.push_back(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), getBColor()));
+ // if used, embed decompose geometry to unified transparency
+ Primitive2DContainer aContent { aDecompose };
+ aDecompose =
+ new UnifiedTransparencePrimitive2D(
+ std::move(aContent),
+ getTransparency());
}
+
+ return aDecompose;
}
BackgroundColorPrimitive2D::BackgroundColorPrimitive2D(
@@ -67,22 +86,19 @@ namespace drawinglayer::primitive2d
void BackgroundColorPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
- if(!getBuffered2DDecomposition().empty() && (maLastViewport != rViewInformation.getViewport()))
+ if(getBuffered2DDecomposition() && (maLastViewport != rViewInformation.getViewport()))
{
// conditions of last local decomposition have changed, delete
- const_cast< BackgroundColorPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< BackgroundColorPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember ViewRange
const_cast< BackgroundColorPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/baseprimitive2d.cxx b/drawinglayer/source/primitive2d/baseprimitive2d.cxx
index 71a13346beff..a2e0eaf6b6ba 100644
--- a/drawinglayer/source/primitive2d/baseprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/baseprimitive2d.cxx
@@ -19,75 +19,14 @@
#include <sal/config.h>
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <drawinglayer/primitive2d/Tools.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <basegfx/utils/canvastools.hxx>
-#include <comphelper/sequence.hxx>
-#include <cppuhelper/queryinterface.hxx>
using namespace css;
-BasePrimitive2DImplBase::~BasePrimitive2DImplBase() {}
-
-css::uno::Any BasePrimitive2DImplBase::queryInterface(css::uno::Type const& rType)
-{
- css::uno::Any aReturn = ::cppu::queryInterface(
- rType, static_cast<uno::XWeak*>(this), static_cast<lang::XComponent*>(this),
- static_cast<lang::XTypeProvider*>(this), static_cast<graphic::XPrimitive2D*>(this),
- static_cast<util::XAccounting*>(this));
- if (aReturn.hasValue())
- return aReturn;
- return OWeakObject::queryInterface(rType);
-}
-
-void BasePrimitive2DImplBase::acquire() noexcept { OWeakObject::acquire(); }
-
-void BasePrimitive2DImplBase::release() noexcept
-{
- if (osl_atomic_decrement(&m_refCount) != 0)
- return;
-
- // ensure no other references are created, via the weak connection point, from now on
- disposeWeakConnectionPoint();
- // restore reference count:
- osl_atomic_increment(&m_refCount);
- // if (! rBHelper.bDisposed) {
- // try {
- // dispose();
- // }
- // catch (RuntimeException const& exc) { // don't break throw ()
- // SAL_WARN( "cppuhelper", exc );
- // }
- // OSL_ASSERT( rBHelper.bDisposed );
- // }
- OWeakObject::release();
-}
-
-void BasePrimitive2DImplBase::dispose() {}
-
-void BasePrimitive2DImplBase::addEventListener(
- css::uno::Reference<css::lang::XEventListener> const&)
-{
- assert(false);
-}
-void BasePrimitive2DImplBase::removeEventListener(
- css::uno::Reference<css::lang::XEventListener> const&)
-{
- assert(false);
-}
-
-css::uno::Sequence<css::uno::Type> BasePrimitive2DImplBase::getTypes()
-{
- static const css::uno::Sequence<uno::Type> aTypeList{
- cppu::UnoType<uno::XWeak>::get(), cppu::UnoType<lang::XComponent>::get(),
- cppu::UnoType<lang::XTypeProvider>::get(), cppu::UnoType<graphic::XPrimitive2D>::get(),
- cppu::UnoType<util::XAccounting>::get()
- };
-
- return aTypeList;
-}
-
namespace drawinglayer::primitive2d
{
BasePrimitive2D::BasePrimitive2D() {}
@@ -112,15 +51,15 @@ public:
: mrViewInformation(rViewInformation)
{
}
- virtual void append(const Primitive2DReference& r) override
+ virtual void visit(const Primitive2DReference& r) override
{
maRetval.expand(getB2DRangeFromPrimitive2DReference(r, mrViewInformation));
}
- virtual void append(const Primitive2DContainer& r) override
+ virtual void visit(const Primitive2DContainer& r) override
{
maRetval.expand(r.getB2DRange(mrViewInformation));
}
- virtual void append(Primitive2DContainer&& r) override
+ virtual void visit(Primitive2DContainer&& r) override
{
maRetval.expand(r.getB2DRange(mrViewInformation));
}
@@ -141,23 +80,23 @@ void BasePrimitive2D::get2DDecomposition(
{
}
-css::uno::Sequence<::css::uno::Reference<::css::graphic::XPrimitive2D>> SAL_CALL
+Primitive2DContainer
BasePrimitive2D::getDecomposition(const uno::Sequence<beans::PropertyValue>& rViewParameters)
{
const auto aViewInformation = geometry::createViewInformation2D(rViewParameters);
Primitive2DContainer aContainer;
get2DDecomposition(aContainer, aViewInformation);
- return comphelper::containerToSequence(aContainer);
+ return aContainer;
}
-css::geometry::RealRectangle2D SAL_CALL
+css::geometry::RealRectangle2D
BasePrimitive2D::getRange(const uno::Sequence<beans::PropertyValue>& rViewParameters)
{
const auto aViewInformation = geometry::createViewInformation2D(rViewParameters);
return basegfx::unotools::rectangle2DFromB2DRectangle(getB2DRange(aViewInformation));
}
-sal_Int64 SAL_CALL BasePrimitive2D::estimateUsage()
+sal_Int64 BasePrimitive2D::estimateUsage()
{
return 0; // for now ignore the objects themselves
}
diff --git a/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
index 0a06010d31e2..7dc58c3fe00a 100644
--- a/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
@@ -20,15 +20,15 @@
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <com/sun/star/awt/XBitmap.hpp>
+#include <utility>
using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
-BitmapPrimitive2D::BitmapPrimitive2D(const css::uno::Reference<css::awt::XBitmap>& rXBitmap,
- const basegfx::B2DHomMatrix& rTransform)
- : maXBitmap(rXBitmap)
- , maTransform(rTransform)
+BitmapPrimitive2D::BitmapPrimitive2D(BitmapEx xXBitmap, basegfx::B2DHomMatrix aTransform)
+ : maBitmap(std::move(xXBitmap))
+ , maTransform(std::move(aTransform))
{
}
@@ -38,7 +38,7 @@ bool BitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
const BitmapPrimitive2D& rCompare = static_cast<const BitmapPrimitive2D&>(rPrimitive);
- return (getXBitmap() == rCompare.getXBitmap() && getTransform() == rCompare.getTransform());
+ return (getBitmap() == rCompare.getBitmap() && getTransform() == rCompare.getTransform());
}
return false;
@@ -52,21 +52,13 @@ BitmapPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInforma
return aRetval;
}
-sal_Int64 SAL_CALL BitmapPrimitive2D::estimateUsage()
+sal_Int64 BitmapPrimitive2D::estimateUsage()
{
- if (!getXBitmap().is())
+ if (getBitmap().IsEmpty())
{
return 0;
}
-
- uno::Reference<util::XAccounting> const xAcc(getXBitmap(), uno::UNO_QUERY);
-
- if (!xAcc.is())
- {
- return 0;
- }
-
- return xAcc->estimateUsage();
+ return getBitmap().GetSizeBytes();
}
// provide unique ID
diff --git a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
index f26f690fb337..79e34ec972ce 100644
--- a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
@@ -21,11 +21,13 @@
#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <rtl/math.hxx>
#include <algorithm>
+#include <utility>
namespace drawinglayer::primitive2d
@@ -87,14 +89,14 @@ namespace drawinglayer::primitive2d
{
rContainer.push_back(
new PolygonStrokePrimitive2D(
- aPolygon,
+ std::move(aPolygon),
rLineAttribute));
}
else
{
rContainer.push_back(
new PolygonStrokePrimitive2D(
- aPolygon,
+ std::move(aPolygon),
rLineAttribute,
rStrokeAttribute));
}
@@ -112,10 +114,10 @@ namespace drawinglayer::primitive2d
return fRetval;
}
- void BorderLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference BorderLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (getStart().equal(getEnd()) || getBorderLines().empty())
- return;
+ return nullptr;
// get data and vectors
basegfx::B2DVector aVector(getEnd() - getStart());
@@ -124,6 +126,7 @@ namespace drawinglayer::primitive2d
const double fFullWidth(getFullWidth());
double fOffset(fFullWidth * -0.5);
+ Primitive2DContainer aContainer;
for(const auto& candidate : maBorderLines)
{
const double fWidth(candidate.getLineAttribute().getWidth());
@@ -141,7 +144,7 @@ namespace drawinglayer::primitive2d
// start and end extends lead to an edge perpendicular to the line, so we can just use
// a PolygonStrokePrimitive2D for representation
addPolygonStrokePrimitive2D(
- rContainer,
+ aContainer,
aStart - (aVector * candidate.getStartLeft()),
aEnd + (aVector * candidate.getEndLeft()),
candidate.getLineAttribute(),
@@ -162,7 +165,7 @@ namespace drawinglayer::primitive2d
aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
- rContainer.push_back(
+ aContainer.push_back(
new PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(aPolygon),
candidate.getLineAttribute().getColor()));
@@ -197,7 +200,7 @@ namespace drawinglayer::primitive2d
aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
}
- rContainer.push_back(
+ aContainer.push_back(
new PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(aPolygon),
candidate.getLineAttribute().getColor()));
@@ -226,7 +229,7 @@ namespace drawinglayer::primitive2d
aPolygon.append(aEnd + aHalfLineOffset + (aVector * fMin));
aPolygon.append(aEnd - aHalfLineOffset + (aVector * fMin));
- rContainer.push_back(
+ aContainer.push_back(
new PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(aPolygon),
candidate.getLineAttribute().getColor()));
@@ -236,7 +239,7 @@ namespace drawinglayer::primitive2d
}
addPolygonStrokePrimitive2D(
- rContainer,
+ aContainer,
aStrokeStart,
aStrokeEnd,
candidate.getLineAttribute(),
@@ -247,6 +250,7 @@ namespace drawinglayer::primitive2d
fOffset += fWidth;
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
bool BorderLinePrimitive2D::isHorizontalOrVertical(const geometry::ViewInformation2D& rViewInformation) const
@@ -266,38 +270,25 @@ namespace drawinglayer::primitive2d
const basegfx::B2DPoint& rStart,
const basegfx::B2DPoint& rEnd,
std::vector< BorderLine >&& rBorderLines,
- const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute)
+ drawinglayer::attribute::StrokeAttribute aStrokeAttribute)
: maStart(rStart),
maEnd(rEnd),
maBorderLines(std::move(rBorderLines)),
- maStrokeAttribute(rStrokeAttribute)
+ maStrokeAttribute(std::move(aStrokeAttribute))
{
}
bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
- if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
- {
- const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive);
+ if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ return false;
- if (getStart() == rCompare.getStart()
- && getEnd() == rCompare.getEnd()
- && getStrokeAttribute() == rCompare.getStrokeAttribute())
- {
- if (getBorderLines().size() == rCompare.getBorderLines().size())
- {
- for (size_t a(0); a < getBorderLines().size(); a++)
- {
- if (!(getBorderLines()[a] == rCompare.getBorderLines()[a]))
- {
- return false;
- }
- }
- }
- }
- }
+ const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive);
- return false;
+ return (getStart() == rCompare.getStart()
+ && getEnd() == rCompare.getEnd()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute()
+ && getBorderLines() == rCompare.getBorderLines());
}
// provide unique ID
@@ -340,7 +331,7 @@ namespace drawinglayer::primitive2d
// direction has to be equal -> cross product == 0.0
const basegfx::B2DVector aVT(pCandidateA->getEnd() - pCandidateA->getStart());
const basegfx::B2DVector aVC(pCandidateB->getEnd() - pCandidateB->getStart());
- if(!rtl::math::approxEqual(0.0, aVC.cross(aVT)))
+ if(aVC.cross(aVT) != 0)
{
return Primitive2DReference();
}
@@ -417,12 +408,12 @@ namespace drawinglayer::primitive2d
}
}
- return Primitive2DReference(
+ return
new BorderLinePrimitive2D(
pCandidateA->getStart(),
pCandidateB->getEnd(),
std::move(aMergedBorderLines),
- pCandidateA->getStrokeAttribute()));
+ pCandidateA->getStrokeAttribute());
}
} // end of namespace
diff --git a/drawinglayer/source/primitive2d/controlprimitive2d.cxx b/drawinglayer/source/primitive2d/controlprimitive2d.cxx
index 6eebc11a0fa5..730e522dc6f6 100644
--- a/drawinglayer/source/primitive2d/controlprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/controlprimitive2d.cxx
@@ -19,25 +19,28 @@
#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <utility>
+#include <rtl/ustrbuf.hxx>
#include <vcl/virdev.hxx>
#include <vcl/svapp.hxx>
#include <com/sun/star/awt/PosSize.hpp>
#include <vcl/bitmapex.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
-#include <tools/diagnose_ex.h>
+#include <comphelper/diagnose_ex.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <svtools/optionsdrawinglayer.hxx>
#include <vcl/window.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <toolkit/helper/vclunohelper.hxx>
+#include <officecfg/Office/Common.hxx>
using namespace com::sun::star;
@@ -96,7 +99,7 @@ namespace drawinglayer::primitive2d
basegfx::B2DVector aDiscreteSize(rViewInformation.getObjectToViewTransformation() * aScale);
// limit to a maximum square size, e.g. 300x150 pixels (45000)
- const double fDiscreteMax(SvtOptionsDrawinglayer::GetQuadraticFormControlRenderLimit());
+ const double fDiscreteMax(officecfg::Office::Common::Drawinglayer::QuadraticFormControlRenderLimit::get());
const double fDiscreteQuadratic(aDiscreteSize.getX() * aDiscreteSize.getY());
const bool bScaleUsed(fDiscreteQuadratic > fDiscreteMax);
double fFactor(1.0);
@@ -144,16 +147,11 @@ namespace drawinglayer::primitive2d
if(xControl.is())
{
uno::Reference<awt::XWindowPeer> xWindowPeer(xControl->getPeer());
-
- VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindowPeer);
- if (pWindow)
+ if (xWindowPeer)
{
- pWindow = pWindow->GetParent();
-
- if(pWindow && MapUnit::Map100thMM == pWindow->GetMapMode().GetMapUnit())
- {
- bUserIs100thmm = true;
- }
+ uno::Reference<awt::XVclWindowPeer> xPeerProps(xWindowPeer, uno::UNO_QUERY_THROW);
+ uno::Any aAny = xPeerProps->getProperty("ParentIs100thmm"); // see VCLXWindow::getProperty
+ aAny >>= bUserIs100thmm;
}
}
@@ -197,7 +195,7 @@ namespace drawinglayer::primitive2d
// create primitive
xRetval = new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(aContent),
+ aContent,
aBitmapTransform);
}
catch( const uno::Exception& )
@@ -217,16 +215,16 @@ namespace drawinglayer::primitive2d
// create a gray placeholder hairline polygon in object size
basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0);
aObjectRange.transform(getTransform());
- const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aObjectRange));
+ basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aObjectRange));
const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0);
// The replacement object may also get a text like 'empty group' here later
- Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(aOutline, aGrayTone));
+ Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(std::move(aOutline), aGrayTone));
return xRetval;
}
- void ControlPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference ControlPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
// try to create a bitmap decomposition. If that fails for some reason,
// at least create a replacement decomposition.
@@ -237,25 +235,28 @@ namespace drawinglayer::primitive2d
xReference = createPlaceholderDecomposition();
}
- rContainer.push_back(xReference);
- }
-
- ControlPrimitive2D::ControlPrimitive2D(
- const basegfx::B2DHomMatrix& rTransform,
- const uno::Reference< awt::XControlModel >& rxControlModel)
- : maTransform(rTransform),
- mxControlModel(rxControlModel)
- {
+ return xReference;
}
ControlPrimitive2D::ControlPrimitive2D(
- const basegfx::B2DHomMatrix& rTransform,
- const uno::Reference< awt::XControlModel >& rxControlModel,
- const uno::Reference< awt::XControl >& rxXControl)
- : maTransform(rTransform),
- mxControlModel(rxControlModel),
- mxXControl(rxXControl)
+ basegfx::B2DHomMatrix aTransform,
+ uno::Reference< awt::XControlModel > xControlModel,
+ uno::Reference<awt::XControl> xXControl,
+ ::std::u16string_view const rTitle,
+ ::std::u16string_view const rDescription,
+ void const*const pAnchorKey)
+ : maTransform(std::move(aTransform)),
+ mxControlModel(std::move(xControlModel)),
+ mxXControl(std::move(xXControl))
+ , m_pAnchorStructureElementKey(pAnchorKey)
{
+ ::rtl::OUStringBuffer buf(rTitle);
+ if (!rTitle.empty() && !rDescription.empty())
+ {
+ buf.append(" - ");
+ }
+ buf.append(rDescription);
+ m_AltText = buf.makeStringAndClear();
}
const uno::Reference< awt::XControl >& ControlPrimitive2D::getXControl() const
@@ -271,38 +272,37 @@ namespace drawinglayer::primitive2d
bool ControlPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
// use base class compare operator
- if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
- {
- const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive);
+ if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ return false;
- if(getTransform() == rCompare.getTransform())
- {
- // check if ControlModel references both are/are not
- bool bRetval(getControlModel().is() == rCompare.getControlModel().is());
+ const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive);
- if(bRetval && getControlModel().is())
- {
- // both exist, check for equality
- bRetval = (getControlModel() == rCompare.getControlModel());
- }
+ if(getTransform() != rCompare.getTransform())
+ return false;
- if(bRetval)
- {
- // check if XControl references both are/are not
- bRetval = (getXControl().is() == rCompare.getXControl().is());
- }
+ // check if ControlModel references both are/are not
+ if (getControlModel().is() != rCompare.getControlModel().is())
+ return false;
- if(bRetval && getXControl().is())
- {
- // both exist, check for equality
- bRetval = (getXControl() == rCompare.getXControl());
- }
+ if(getControlModel().is())
+ {
+ // both exist, check for equality
+ if (getControlModel() != rCompare.getControlModel())
+ return false;
+ }
- return bRetval;
- }
+ // check if XControl references both are/are not
+ if (getXControl().is() != rCompare.getXControl().is())
+ return false;
+
+ if(getXControl().is())
+ {
+ // both exist, check for equality
+ if (getXControl() != rCompare.getXControl())
+ return false;
}
- return false;
+ return true;
}
basegfx::B2DRange ControlPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
@@ -317,26 +317,24 @@ namespace drawinglayer::primitive2d
{
// this primitive is view-dependent related to the scaling. If scaling has changed,
// destroy existing decomposition. To detect change, use size of unit size in view coordinates
- std::unique_lock aGuard( m_aMutex );
const basegfx::B2DVector aNewScaling(rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
- if(!getBuffered2DDecomposition().empty())
+ if(getBuffered2DDecomposition())
{
if(!maLastViewScaling.equal(aNewScaling))
{
// conditions of last local decomposition have changed, delete
- const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember ViewTransformation
const_cast< ControlPrimitive2D* >(this)->maLastViewScaling = aNewScaling;
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/cropprimitive2d.cxx b/drawinglayer/source/primitive2d/cropprimitive2d.cxx
index 06a7e2726f04..da28d9e41663 100644
--- a/drawinglayer/source/primitive2d/cropprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/cropprimitive2d.cxx
@@ -24,6 +24,7 @@
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -33,13 +34,13 @@ namespace drawinglayer::primitive2d
{
CropPrimitive2D::CropPrimitive2D(
Primitive2DContainer&& aChildren,
- const basegfx::B2DHomMatrix& rTransformation,
+ basegfx::B2DHomMatrix aTransformation,
double fCropLeft,
double fCropTop,
double fCropRight,
double fCropBottom)
: GroupPrimitive2D(std::move(aChildren)),
- maTransformation(rTransformation),
+ maTransformation(std::move(aTransformation)),
mfCropLeft(fCropLeft),
mfCropTop(fCropTop),
mfCropRight(fCropRight),
@@ -126,7 +127,7 @@ namespace drawinglayer::primitive2d
{
// the new range is completely inside the old range (unit range),
// so no masking is needed
- rVisitor.append(xTransformPrimitive);
+ rVisitor.visit(xTransformPrimitive);
}
else
{
@@ -137,10 +138,10 @@ namespace drawinglayer::primitive2d
// create maskPrimitive with aMaskPolyPolygon and aMaskContentVector
const Primitive2DReference xMask(
new MaskPrimitive2D(
- aMaskPolyPolygon,
+ std::move(aMaskPolyPolygon),
Primitive2DContainer { xTransformPrimitive }));
- rVisitor.append(xMask);
+ rVisitor.visit(xMask);
}
}
diff --git a/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
index 8a86f83204bd..66dcfcb403e1 100644
--- a/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
@@ -25,14 +25,14 @@
namespace drawinglayer::primitive2d
{
- void DiscreteBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference DiscreteBitmapPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
// use getViewTransformation() and getObjectTransformation() from
// ObjectAndViewTransformationDependentPrimitive2D to create a BitmapPrimitive2D
// with the correct mapping
if(getBitmapEx().IsEmpty())
- return;
+ return nullptr;
// get discrete size
const Size& rSizePixel = getBitmapEx().GetSizePixel();
@@ -64,10 +64,10 @@ namespace drawinglayer::primitive2d
aObjectTransform = aInverseObjectTransformation * aObjectTransform;
// create BitmapPrimitive2D with now object-local coordinate data
- rContainer.push_back(
+ return
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
- aObjectTransform));
+ getBitmapEx(),
+ aObjectTransform);
}
DiscreteBitmapPrimitive2D::DiscreteBitmapPrimitive2D(
diff --git a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
index 5c562471a786..1ff96ce60886 100644
--- a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
@@ -23,6 +23,7 @@
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <osl/diagnose.h>
#include <toolkit/helper/vclunohelper.hxx>
@@ -31,6 +32,7 @@ namespace drawinglayer::primitive2d
DiscreteShadow::DiscreteShadow(const BitmapEx& rBitmapEx)
: maBitmapEx(rBitmapEx)
{
+ maBitmapEx.Invert(); // convert transparency to alpha
const Size& rBitmapSize = getBitmapEx().GetSizePixel();
if(rBitmapSize.Width() != rBitmapSize.Height() || rBitmapSize.Width() < 7)
@@ -149,12 +151,12 @@ namespace drawinglayer::primitive2d
namespace drawinglayer::primitive2d
{
- void DiscreteShadowPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference DiscreteShadowPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
Primitive2DContainer xRetval;
if(getDiscreteShadow().getBitmapEx().IsEmpty())
- return;
+ return nullptr;
const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2);
const basegfx::B2DVector aScale(getTransform() * basegfx::B2DVector(1.0, 1.0));
@@ -168,90 +170,90 @@ namespace drawinglayer::primitive2d
xRetval.resize(8);
// TopLeft
- xRetval[0] = Primitive2DReference(
+ xRetval[0] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopLeft()),
+ getDiscreteShadow().getTopLeft(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
fBigLenX,
fBigLenY,
-fBorderX,
- -fBorderY)));
+ -fBorderY));
// Top
- xRetval[1] = Primitive2DReference(
+ xRetval[1] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTop()),
+ getDiscreteShadow().getTop(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX,
fBorderY,
fBorderX + fSingleX,
- -fBorderY)));
+ -fBorderY));
// TopRight
- xRetval[2] = Primitive2DReference(
+ xRetval[2] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopRight()),
+ getDiscreteShadow().getTopRight(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
fBigLenX,
fBigLenY,
1.0 - fBorderX,
- -fBorderY)));
+ -fBorderY));
// Right
- xRetval[3] = Primitive2DReference(
+ xRetval[3] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getRight()),
+ getDiscreteShadow().getRight(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
fBorderX,
1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY,
1.0 + fSingleX,
- fBorderY + fSingleY)));
+ fBorderY + fSingleY));
// BottomRight
- xRetval[4] = Primitive2DReference(
+ xRetval[4] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomRight()),
+ getDiscreteShadow().getBottomRight(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
fBigLenX,
fBigLenY,
1.0 - (fBorderX + fSingleX) + fSingleX,
- 1.0 - (fBorderY + fSingleY) + fSingleY)));
+ 1.0 - (fBorderY + fSingleY) + fSingleY));
// Bottom
- xRetval[5] = Primitive2DReference(
+ xRetval[5] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottom()),
+ getDiscreteShadow().getBottom(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX,
fBorderY,
fBorderX + fSingleX,
- 1.0 + fSingleY)));
+ 1.0 + fSingleY));
// BottomLeft
- xRetval[6] = Primitive2DReference(
+ xRetval[6] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomLeft()),
+ getDiscreteShadow().getBottomLeft(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
fBigLenX,
fBigLenY,
-fBorderX,
- 1.0 - fBorderY)));
+ 1.0 - fBorderY));
// Left
- xRetval[7] = Primitive2DReference(
+ xRetval[7] =
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getLeft()),
+ getDiscreteShadow().getLeft(),
basegfx::utils::createScaleTranslateB2DHomMatrix(
fBorderX,
1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY,
-fBorderX,
- fBorderY + fSingleY)));
+ fBorderY + fSingleY));
// put all in object transformation to get to target positions
- rContainer.push_back(
+ return
new TransformPrimitive2D(
getTransform(),
- std::move(xRetval)));
+ std::move(xRetval));
}
DiscreteShadowPrimitive2D::DiscreteShadowPrimitive2D(
diff --git a/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
index 0b217adf6b49..6c7ebd113a43 100644
--- a/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
@@ -21,11 +21,12 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/color/bcolor.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/geometry/viewinformation3d.hxx>
#include <processor3d/shadow3dextractor.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -35,8 +36,6 @@ namespace drawinglayer::primitive2d
{
bool Embedded3DPrimitive2D::impGetShadow3D() const
{
- std::unique_lock aGuard( m_aMutex );
-
// create on demand
if(!mbShadow3DChecked && !getChildren3D().empty())
{
@@ -60,25 +59,25 @@ namespace drawinglayer::primitive2d
return !maShadowPrimitives.empty();
}
- void Embedded3DPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference Embedded3DPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
// use info to create a yellow 2d rectangle, similar to empty 3d scenes and/or groups
const basegfx::B2DRange aLocal2DRange(getB2DRange(rViewInformation));
- const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aLocal2DRange));
+ basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aLocal2DRange));
const basegfx::BColor aYellow(1.0, 1.0, 0.0);
- rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, aYellow));
+ return new PolygonHairlinePrimitive2D(std::move(aOutline), aYellow);
}
Embedded3DPrimitive2D::Embedded3DPrimitive2D(
- const primitive3d::Primitive3DContainer& rxChildren3D,
- const basegfx::B2DHomMatrix& rObjectTransformation,
- const geometry::ViewInformation3D& rViewInformation3D,
+ primitive3d::Primitive3DContainer aChildren3D,
+ basegfx::B2DHomMatrix aObjectTransformation,
+ geometry::ViewInformation3D aViewInformation3D,
const basegfx::B3DVector& rLightNormal,
double fShadowSlant,
const basegfx::B3DRange& rScene3DRange)
- : mxChildren3D(rxChildren3D),
- maObjectTransformation(rObjectTransformation),
- maViewInformation3D(rViewInformation3D),
+ : mxChildren3D(std::move(aChildren3D)),
+ maObjectTransformation(std::move(aObjectTransformation)),
+ maViewInformation3D(std::move(aViewInformation3D)),
maLightNormal(rLightNormal),
mfShadowSlant(fShadowSlant),
maScene3DRange(rScene3DRange),
diff --git a/drawinglayer/source/primitive2d/epsprimitive2d.cxx b/drawinglayer/source/primitive2d/epsprimitive2d.cxx
index 9e876dad5376..760d5d764c41 100644
--- a/drawinglayer/source/primitive2d/epsprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/epsprimitive2d.cxx
@@ -20,10 +20,11 @@
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <utility>
namespace drawinglayer::primitive2d
{
- void EpsPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference EpsPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const GDIMetaFile& rSubstituteContent = getMetaFile();
@@ -33,19 +34,20 @@ namespace drawinglayer::primitive2d
// To really use the Eps data, a renderer has to know and interpret this primitive
// directly.
- rContainer.push_back(
+ return
new MetafilePrimitive2D(
getEpsTransform(),
- rSubstituteContent));
+ rSubstituteContent);
}
+ return nullptr;
}
EpsPrimitive2D::EpsPrimitive2D(
- const basegfx::B2DHomMatrix& rEpsTransform,
- const GfxLink& rGfxLink,
+ basegfx::B2DHomMatrix aEpsTransform,
+ GfxLink aGfxLink,
const GDIMetaFile& rMetaFile)
- : maEpsTransform(rEpsTransform),
- maGfxLink(rGfxLink),
+ : maEpsTransform(std::move(aEpsTransform)),
+ maGfxLink(std::move(aGfxLink)),
maMetaFile(rMetaFile)
{
}
diff --git a/drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx b/drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx
new file mode 100644
index 000000000000..a510b97b5b02
--- /dev/null
+++ b/drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/primitive2d/exclusiveeditviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ExclusiveEditViewPrimitive2D::ExclusiveEditViewPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+{
+}
+
+basegfx::B2DRange
+ExclusiveEditViewPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ return getChildren().getB2DRange(rViewInformation);
+}
+
+void ExclusiveEditViewPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ // check if EditView is visualized. if yes, use content by calling parent class. if no, suppress it
+ if (rViewInformation.getEditViewActive())
+ GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+// provide unique ID
+sal_uInt32 ExclusiveEditViewPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_EXCLUSIVEEDITVIEWPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
index 7baf93bf5292..1166c8950881 100644
--- a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
@@ -23,6 +23,9 @@
#include <texture/texture.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <utility>
+#include <algorithm>
using namespace com::sun::star;
@@ -30,246 +33,232 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void FillGradientPrimitive2D::generateMatricesAndColors(
- std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor) const
+ // Get the OuterColor. Take into account that for css::awt::GradientStyle_AXIAL
+ // this is the last one due to inverted gradient usage (see constructor there)
+ basegfx::BColor FillGradientPrimitive2D::getOuterColor() const
{
- rEntries.clear();
-
- // make sure steps is not too high/low
- const basegfx::BColor aStart(getFillGradient().getStartColor());
- const basegfx::BColor aEnd(getFillGradient().getEndColor());
- const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
- sal_uInt32 nSteps(getFillGradient().getSteps());
+ if (getFillGradient().getColorStops().empty())
+ return basegfx::BColor();
- if(nSteps == 0)
- {
- nSteps = nMaxSteps;
- }
+ if (css::awt::GradientStyle_AXIAL == getFillGradient().getStyle())
+ return getFillGradient().getColorStops().back().getStopColor();
- if(nSteps < 2)
- {
- nSteps = 2;
- }
+ return getFillGradient().getColorStops().front().getStopColor();
+ }
- if(nSteps > nMaxSteps)
+ // Get the needed UnitPolygon dependent on the GradientStyle
+ basegfx::B2DPolygon FillGradientPrimitive2D::getUnitPolygon() const
+ {
+ if (css::awt::GradientStyle_RADIAL == getFillGradient().getStyle()
+ || css::awt::GradientStyle_ELLIPTICAL == getFillGradient().getStyle())
{
- nSteps = nMaxSteps;
+ return basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
}
- nSteps = std::max(sal_uInt32(1), nSteps);
+ return basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
+ }
+ void FillGradientPrimitive2D::generateMatricesAndColors(
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) const
+ {
switch(getFillGradient().getStyle())
{
- case attribute::GradientStyle::Linear:
+ default: // GradientStyle_MAKE_FIXED_SIZE
+ case css::awt::GradientStyle_LINEAR:
{
texture::GeoTexSvxGradientLinear aGradient(
getDefinitionRange(),
getOutputRange(),
- aStart,
- aEnd,
- nSteps,
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
getFillGradient().getBorder(),
getFillGradient().getAngle());
- aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ aGradient.appendTransformationsAndColors(aCallback);
break;
}
- case attribute::GradientStyle::Axial:
+ case css::awt::GradientStyle_AXIAL:
{
texture::GeoTexSvxGradientAxial aGradient(
getDefinitionRange(),
getOutputRange(),
- aStart,
- aEnd,
- nSteps,
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
getFillGradient().getBorder(),
getFillGradient().getAngle());
- aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ aGradient.appendTransformationsAndColors(aCallback);
break;
}
- case attribute::GradientStyle::Radial:
+ case css::awt::GradientStyle_RADIAL:
{
texture::GeoTexSvxGradientRadial aGradient(
getDefinitionRange(),
- aStart,
- aEnd,
- nSteps,
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
getFillGradient().getBorder(),
getFillGradient().getOffsetX(),
getFillGradient().getOffsetY());
- aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ aGradient.appendTransformationsAndColors(aCallback);
break;
}
- case attribute::GradientStyle::Elliptical:
+ case css::awt::GradientStyle_ELLIPTICAL:
{
texture::GeoTexSvxGradientElliptical aGradient(
getDefinitionRange(),
- aStart,
- aEnd,
- nSteps,
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
getFillGradient().getBorder(),
getFillGradient().getOffsetX(),
getFillGradient().getOffsetY(),
getFillGradient().getAngle());
- aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ aGradient.appendTransformationsAndColors(aCallback);
break;
}
- case attribute::GradientStyle::Square:
+ case css::awt::GradientStyle_SQUARE:
{
texture::GeoTexSvxGradientSquare aGradient(
getDefinitionRange(),
- aStart,
- aEnd,
- nSteps,
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
getFillGradient().getBorder(),
getFillGradient().getOffsetX(),
getFillGradient().getOffsetY(),
getFillGradient().getAngle());
- aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ aGradient.appendTransformationsAndColors(aCallback);
break;
}
- case attribute::GradientStyle::Rect:
+ case css::awt::GradientStyle_RECT:
{
texture::GeoTexSvxGradientRect aGradient(
getDefinitionRange(),
- aStart,
- aEnd,
- nSteps,
+ getFillGradient().getSteps(),
+ getFillGradient().getColorStops(),
getFillGradient().getBorder(),
getFillGradient().getOffsetX(),
getFillGradient().getOffsetY(),
getFillGradient().getAngle());
- aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ aGradient.appendTransformationsAndColors(aCallback);
break;
}
}
}
- void FillGradientPrimitive2D::createOverlappingFill(
- Primitive2DContainer& rContainer,
- const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
- const basegfx::BColor& rOuterColor,
- const basegfx::B2DPolygon& rUnitPolygon) const
- {
- // create solid fill with outmost color
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(
- basegfx::B2DPolyPolygon(
- basegfx::utils::createPolygonFromRect(getOutputRange())),
- rOuterColor));
-
- // create solid fill steps
- for(const auto &a : rEntries)
- {
- // create part polygon
- basegfx::B2DPolygon aNewPoly(rUnitPolygon);
-
- aNewPoly.transform(a.maB2DHomMatrix);
-
- // create solid fill
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(
- basegfx::B2DPolyPolygon(aNewPoly),
- a.maBColor));
- }
- }
-
- void FillGradientPrimitive2D::createNonOverlappingFill(
- Primitive2DContainer& rContainer,
- const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
- const basegfx::BColor& rOuterColor,
- const basegfx::B2DPolygon& rUnitPolygon) const
+ Primitive2DReference FillGradientPrimitive2D::createFill(bool bOverlapping) const
{
- // get outmost visible range from object
- basegfx::B2DRange aOutmostRange(getOutputRange());
- basegfx::B2DPolyPolygon aCombinedPolyPoly;
-
- if(!rEntries.empty())
- {
- // extend aOutmostRange with first polygon
- basegfx::B2DPolygon aFirstPoly(rUnitPolygon);
-
- aFirstPoly.transform(rEntries[0].maB2DHomMatrix);
- aCombinedPolyPoly.append(aFirstPoly);
- aOutmostRange.expand(aFirstPoly.getB2DRange());
- }
-
- // add outmost range to combined polypolygon (in 1st place), create first primitive
- aCombinedPolyPoly.insert(0, basegfx::utils::createPolygonFromRect(aOutmostRange));
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(
- aCombinedPolyPoly,
- rOuterColor));
-
- if(rEntries.empty())
- return;
-
- // reuse first polygon, it's the second one
- aCombinedPolyPoly.remove(0);
-
- for(size_t a(0); a < rEntries.size() - 1; a++)
+ Primitive2DContainer aContainer;
+ if (bOverlapping)
{
- // create next inner polygon, combined with last one
- basegfx::B2DPolygon aNextPoly(rUnitPolygon);
-
- aNextPoly.transform(rEntries[a + 1].maB2DHomMatrix);
- aCombinedPolyPoly.append(aNextPoly);
-
- // create primitive with correct color
- rContainer.push_back(
+ // OverlappingFill: create solid fill with outmost color
+ aContainer.push_back(
new PolyPolygonColorPrimitive2D(
- aCombinedPolyPoly,
- rEntries[a].maBColor));
-
- // reuse inner polygon, it's the 2nd one
- aCombinedPolyPoly.remove(0);
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getOutputRange())),
+ getOuterColor()));
+
+ // create solid fill steps by providing callback as lambda
+ auto aCallback([&aContainer,this](
+ const basegfx::B2DHomMatrix& rMatrix,
+ const basegfx::BColor& rColor)
+ {
+ // create part polygon
+ basegfx::B2DPolygon aNewPoly(getUnitPolygon());
+ aNewPoly.transform(rMatrix);
+
+ // create solid fill
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aNewPoly),
+ rColor));
+ });
+
+ // call value generator to trigger callbacks
+ generateMatricesAndColors(aCallback);
}
-
- // add last inner polygon with last color
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(
- aCombinedPolyPoly,
- rEntries[rEntries.size() - 1].maBColor));
- }
-
- void FillGradientPrimitive2D::createFill(Primitive2DContainer& rContainer, bool bOverlapping) const
- {
- // prepare shape of the Unit Polygon
- basegfx::B2DPolygon aUnitPolygon;
-
- switch(getFillGradient().getStyle())
+ else
{
- case attribute::GradientStyle::Radial:
- case attribute::GradientStyle::Elliptical:
+ // NonOverlappingFill
+ if (getFillGradient().getColorStops().size() < 2)
{
- aUnitPolygon = basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
- break;
+ // not really a gradient, we need to create a start primitive
+ // entry using the single color and the covered area
+ const basegfx::B2DRange aOutmostRange(getOutputRange());
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aOutmostRange)),
+ getOuterColor()));
}
- default: // GradientStyle::Linear, attribute::GradientStyle::Axial, attribute::GradientStyle::Square, attribute::GradientStyle::Rect
+ else
{
- aUnitPolygon = basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
- break;
+ // gradient with stops, prepare CombinedPolyPoly, use callback
+ basegfx::B2DPolyPolygon aCombinedPolyPoly;
+ basegfx::BColor aLastColor;
+
+ auto aCallback([&aContainer,&aCombinedPolyPoly,&aLastColor,this](
+ const basegfx::B2DHomMatrix& rMatrix,
+ const basegfx::BColor& rColor)
+ {
+ if (aContainer.empty())
+ {
+ // 1st callback, init CombinedPolyPoly & create 1st entry
+ basegfx::B2DRange aOutmostRange(getOutputRange());
+
+ // expand aOutmostRange with transformed first polygon
+ // to ensure confinement
+ basegfx::B2DPolygon aFirstPoly(getUnitPolygon());
+ aFirstPoly.transform(rMatrix);
+ aOutmostRange.expand(aFirstPoly.getB2DRange());
+
+ // build 1st combined polygon; outmost range 1st, then
+ // the shaped, transformed polygon
+ aCombinedPolyPoly.append(basegfx::utils::createPolygonFromRect(aOutmostRange));
+ aCombinedPolyPoly.append(aFirstPoly);
+
+ // create first primitive
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ getOuterColor()));
+
+ // save first polygon for re-use in next call, it's the second
+ // one, so remove 1st
+ aCombinedPolyPoly.remove(0);
+
+ // remember color for next primitive creation
+ aLastColor = rColor;
+ }
+ else
+ {
+ // regular n-th callback, create combined entry by re-using
+ // CombinedPolyPoly and aLastColor
+ basegfx::B2DPolygon aNextPoly(getUnitPolygon());
+ aNextPoly.transform(rMatrix);
+ aCombinedPolyPoly.append(aNextPoly);
+
+ // create primitive with correct color
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ aLastColor));
+
+ // prepare re-use of inner polygon, save color
+ aCombinedPolyPoly.remove(0);
+ aLastColor = rColor;
+ }
+ });
+
+ // call value generator to trigger callbacks
+ generateMatricesAndColors(aCallback);
+
+ // add last inner polygon with last color
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ aLastColor));
}
}
-
- // get the transform matrices and colors (where colors
- // will have one more entry that matrices)
- std::vector< drawinglayer::texture::B2DHomMatrixAndBColor > aEntries;
- basegfx::BColor aOuterColor;
-
- generateMatricesAndColors(aEntries, aOuterColor);
-
- if(bOverlapping)
- {
- createOverlappingFill(rContainer, aEntries, aOuterColor, aUnitPolygon);
- }
- else
- {
- createNonOverlappingFill(rContainer, aEntries, aOuterColor, aUnitPolygon);
- }
+ return new GroupPrimitive2D(std::move(aContainer));
}
- void FillGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
// default creates overlapping fill which works with AntiAliasing and without.
// The non-overlapping version does not create single filled polygons, but
@@ -280,26 +269,27 @@ namespace drawinglayer::primitive2d
if(!getFillGradient().isDefault())
{
- createFill(rContainer, /*bOverlapping*/true);
+ return createFill(/*bOverlapping*/true);
}
+ return nullptr;
}
FillGradientPrimitive2D::FillGradientPrimitive2D(
const basegfx::B2DRange& rOutputRange,
- const attribute::FillGradientAttribute& rFillGradient)
+ attribute::FillGradientAttribute aFillGradient)
: maOutputRange(rOutputRange),
maDefinitionRange(rOutputRange),
- maFillGradient(rFillGradient)
+ maFillGradient(std::move(aFillGradient))
{
}
FillGradientPrimitive2D::FillGradientPrimitive2D(
const basegfx::B2DRange& rOutputRange,
const basegfx::B2DRange& rDefinitionRange,
- const attribute::FillGradientAttribute& rFillGradient)
+ attribute::FillGradientAttribute aFillGradient)
: maOutputRange(rOutputRange),
maDefinitionRange(rDefinitionRange),
- maFillGradient(rFillGradient)
+ maFillGradient(std::move(aFillGradient))
{
}
diff --git a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
index 0f386eea29de..a553687e7787 100644
--- a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
@@ -25,6 +25,7 @@
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <utility>
#include <vcl/graph.hxx>
@@ -33,24 +34,25 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void FillGraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference FillGraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const attribute::FillGraphicAttribute& rAttribute = getFillGraphic();
if(rAttribute.isDefault())
- return;
+ return nullptr;
const Graphic& rGraphic = rAttribute.getGraphic();
if(GraphicType::Bitmap != rGraphic.GetType() && GraphicType::GdiMetafile != rGraphic.GetType())
- return;
+ return nullptr;
const Size aSize(rGraphic.GetPrefSize());
if(!(aSize.Width() && aSize.Height()))
- return;
+ return nullptr;
// we have a graphic (bitmap or metafile) with some size
+ Primitive2DContainer aContainer;
if(rAttribute.getTiling())
{
// get object range and create tiling matrices
@@ -69,11 +71,12 @@ namespace drawinglayer::primitive2d
rGraphic,
basegfx::B2DHomMatrix());
+ rtl::Reference<GroupPrimitive2D> xGroup = new GroupPrimitive2D(std::move(xSeq));
for(const auto &a : aMatrices)
{
- rContainer.push_back(new TransformPrimitive2D(
+ aContainer.push_back(new TransformPrimitive2D(
getTransformation() * a,
- Primitive2DContainer(xSeq)));
+ *xGroup));
}
}
else
@@ -84,17 +87,19 @@ namespace drawinglayer::primitive2d
rAttribute.getGraphicRange().getRange(),
rAttribute.getGraphicRange().getMinimum()));
- create2DDecompositionOfGraphic(rContainer,
+ create2DDecompositionOfGraphic(aContainer,
rGraphic,
aObjectTransform);
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
FillGraphicPrimitive2D::FillGraphicPrimitive2D(
- const basegfx::B2DHomMatrix& rTransformation,
+ basegfx::B2DHomMatrix aTransformation,
const attribute::FillGraphicAttribute& rFillGraphic)
- : maTransformation(rTransformation),
- maFillGraphic(rFillGraphic)
+ : maTransformation(std::move(aTransformation)),
+ maFillGraphic(rFillGraphic),
+ maOffsetXYCreatedBitmap()
{
}
diff --git a/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
index 60d47c3ed559..c855460824e7 100644
--- a/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
@@ -19,12 +19,14 @@
#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -32,10 +34,10 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void FillHatchPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference FillHatchPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if(getFillHatch().isDefault())
- return;
+ return nullptr;
// create hatch
const basegfx::BColor aHatchColor(getFillHatch().getColor());
@@ -99,12 +101,12 @@ namespace drawinglayer::primitive2d
// prepare return value
const bool bFillBackground(getFillHatch().isFillBackground());
-
+ Primitive2DContainer aContainer;
// evtl. create filled background
if(bFillBackground)
{
// create primitive for background
- rContainer.push_back(
+ aContainer.push_back(
new PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(
basegfx::utils::createPolygonFromRect(getOutputRange())), getBColor()));
@@ -123,17 +125,18 @@ namespace drawinglayer::primitive2d
aNewLine.append(rMatrix * aEnd);
// create hairline
- rContainer.push_back(new PolygonHairlinePrimitive2D(aNewLine, aHatchColor));
+ aContainer.push_back(new PolygonHairlinePrimitive2D(std::move(aNewLine), aHatchColor));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
FillHatchPrimitive2D::FillHatchPrimitive2D(
const basegfx::B2DRange& rOutputRange,
const basegfx::BColor& rBColor,
- const attribute::FillHatchAttribute& rFillHatch)
+ attribute::FillHatchAttribute aFillHatch)
: maOutputRange(rOutputRange),
maDefinitionRange(rOutputRange),
- maFillHatch(rFillHatch),
+ maFillHatch(std::move(aFillHatch)),
maBColor(rBColor)
{
}
@@ -142,10 +145,10 @@ namespace drawinglayer::primitive2d
const basegfx::B2DRange& rOutputRange,
const basegfx::B2DRange& rDefinitionRange,
const basegfx::BColor& rBColor,
- const attribute::FillHatchAttribute& rFillHatch)
+ attribute::FillHatchAttribute aFillHatch)
: maOutputRange(rOutputRange),
maDefinitionRange(rDefinitionRange),
- maFillHatch(rFillHatch),
+ maFillHatch(std::move(aFillHatch)),
maBColor(rBColor)
{
}
@@ -173,10 +176,8 @@ namespace drawinglayer::primitive2d
void FillHatchPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
bool bAdaptDistance(0 != getFillHatch().getMinimalDiscreteDistance());
- aGuard.unlock();
if(bAdaptDistance)
{
// behave view-dependent
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
index 6fe14c2222c1..6bf9dea8af83 100644
--- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -18,7 +18,18 @@
*/
#include <drawinglayer/primitive2d/glowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <drawinglayer/converters.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
using namespace com::sun::star;
@@ -26,15 +37,19 @@ namespace drawinglayer::primitive2d
{
GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, double fRadius,
Primitive2DContainer&& rChildren)
- : GroupPrimitive2D(std::move(rChildren))
+ : BufferedDecompositionGroupPrimitive2D(std::move(rChildren))
, maGlowColor(rGlowColor)
, mfGlowRadius(fRadius)
+ , mfLastDiscreteGlowRadius(0.0)
+ , maLastClippedRange()
{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(15);
}
bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
- if (BasePrimitive2D::operator==(rPrimitive))
+ if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive))
{
const GlowPrimitive2D& rCompare = static_cast<const GlowPrimitive2D&>(rPrimitive);
@@ -45,12 +60,294 @@ bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
return false;
}
+bool GlowPrimitive2D::prepareValuesAndcheckValidity(
+ basegfx::B2DRange& rGlowRange, basegfx::B2DRange& rClippedRange,
+ basegfx::B2DVector& rDiscreteGlowSize, double& rfDiscreteGlowRadius,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ // no GlowRadius defined, done
+ if (getGlowRadius() <= 0.0)
+ return false;
+
+ // no geometry, done
+ if (getChildren().empty())
+ return false;
+
+ // no pixel target, done
+ if (rViewInformation.getObjectToViewTransformation().isIdentity())
+ return false;
+
+ // get geometry range that defines area that needs to be pixelated
+ rGlowRange = getChildren().getB2DRange(rViewInformation);
+
+ // no range of geometry, done
+ if (rGlowRange.isEmpty())
+ return false;
+
+ // extend range by GlowRadius in all directions
+ rGlowRange.grow(getGlowRadius());
+
+ // initialize ClippedRange to full GlowRange -> all is visible
+ rClippedRange = rGlowRange;
+
+ // get Viewport and check if used. If empty, all is visible (see
+ // ViewInformation2D definition in viewinformation2d.hxx)
+ if (!rViewInformation.getViewport().isEmpty())
+ {
+ // if used, extend by GlowRadius to ensure needed parts are included
+ basegfx::B2DRange aVisibleArea(rViewInformation.getViewport());
+ aVisibleArea.grow(getGlowRadius());
+
+ // To do this correctly, it needs to be done in discrete coordinates.
+ // The object may be transformed relative to the original#
+ // ObjectTransformation, e.g. when re-used in shadow
+ aVisibleArea.transform(rViewInformation.getViewTransformation());
+ rClippedRange.transform(rViewInformation.getObjectToViewTransformation());
+
+ // calculate ClippedRange
+ rClippedRange.intersect(aVisibleArea);
+
+ // if GlowRange is completely outside of VisibleArea, ClippedRange
+ // will be empty and we are done
+ if (rClippedRange.isEmpty())
+ return false;
+
+ // convert result back to object coordinates
+ rClippedRange.transform(rViewInformation.getInverseObjectToViewTransformation());
+ }
+
+ // calculate discrete pixel size of GlowRange. If it's too small to visualize, we are done
+ rDiscreteGlowSize = rViewInformation.getObjectToViewTransformation() * rGlowRange.getRange();
+ if (ceil(rDiscreteGlowSize.getX()) < 2.0 || ceil(rDiscreteGlowSize.getY()) < 2.0)
+ return false;
+
+ // calculate discrete pixel size of GlowRadius. If it's too small to visualize, we are done
+ rfDiscreteGlowRadius = ceil(
+ (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getGlowRadius(), 0))
+ .getLength());
+ if (rfDiscreteGlowRadius < 1.0)
+ return false;
+
+ return true;
+}
+
+void GlowPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aGlowRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteGlowSize;
+ double fDiscreteGlowRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aGlowRange, aClippedRange, aDiscreteGlowSize,
+ fDiscreteGlowRadius, rViewInformation))
+ return;
+
+ // Create embedding transformation from object to top-left zero-aligned
+ // target pixel geometry (discrete form of ClippedRange)
+ // First, move to top-left of GlowRange
+ const sal_uInt32 nDiscreteGlowWidth(ceil(aDiscreteGlowSize.getX()));
+ const sal_uInt32 nDiscreteGlowHeight(ceil(aDiscreteGlowSize.getY()));
+ basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+ -aClippedRange.getMinX(), -aClippedRange.getMinY()));
+ // Second, scale to discrete bitmap size
+ // Even when using the offset from ClippedRange, we need to use the
+ // scaling from the full representation, thus from GlowRange
+ aEmbedding.scale(nDiscreteGlowWidth / aGlowRange.getWidth(),
+ nDiscreteGlowHeight / aGlowRange.getHeight());
+
+ // Embed content graphics to TransformPrimitive2D
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(aEmbedding, Primitive2DContainer(getChildren())));
+ primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+ // Create BitmapEx using drawinglayer tooling, including a MaximumQuadraticPixel
+ // limitation to be safe and not go runtime/memory havoc. Use a pretty small
+ // limit due to this is glow functionality and will look good with bitmap scaling
+ // anyways. The value of 250.000 square pixels below maybe adapted as needed.
+ const basegfx::B2DVector aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation()
+ * aClippedRange.getRange());
+ const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX()));
+ const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY()));
+ const geometry::ViewInformation2D aViewInformation2D;
+ const sal_uInt32 nMaximumQuadraticPixels(250000);
+
+ // I have now added a helper that just creates the mask without having
+ // to render the content, use it, it's faster
+ const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
+ std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
+ nMaximumQuadraticPixels));
+
+ if (aAlpha.IsEmpty())
+ return;
+
+ const Size& rBitmapExSizePixel(aAlpha.GetSizePixel());
+
+ if (rBitmapExSizePixel.Width() <= 0 || rBitmapExSizePixel.Height() <= 0)
+ return;
+
+ // We may have to take a corrective scaling into account when the
+ // MaximumQuadraticPixel limit was used/triggered
+ double fScale(1.0);
+
+ if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
+ || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
+ {
+ // scale in X and Y should be the same (see fReduceFactor in createAlphaMask),
+ // so adapt numerically to a single scale value, they are integer rounded values
+ const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
+ / static_cast<double>(nDiscreteClippedWidth));
+ const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+ / static_cast<double>(nDiscreteClippedHeight));
+
+ fScale = (fScaleX + fScaleY) * 0.5;
+ }
+
+ // fDiscreteGlowRadius is the size of the halo from each side of the object. The halo is the
+ // border of glow color that fades from glow transparency level to fully transparent
+ // When blurring a sharp boundary (our case), it gets 50% of original intensity, and
+ // fades to both sides by the blur radius; thus blur radius is half of glow radius.
+ // Consider glow transparency (initial transparency near the object edge)
+ AlphaMask mask(ProcessAndBlurAlphaMask(aAlpha, fDiscreteGlowRadius * fScale / 2.0,
+ fDiscreteGlowRadius * fScale / 2.0,
+ 255 - getGlowColor().GetAlpha()));
+
+ // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask
+ Bitmap bmp(aAlpha.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ bmp.Erase(getGlowColor());
+ BitmapEx result(bmp, mask);
+
+#ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_glow.png", StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(result);
+ }
+ }
+#endif
+
+ // Independent from discrete sizes of glow alpha creation, always
+ // map and project glow result to geometry range extended by glow
+ // radius, but to the eventually clipped instance (ClippedRange)
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new BitmapPrimitive2D(result, basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aClippedRange.getWidth(), aClippedRange.getHeight(),
+ aClippedRange.getMinX(), aClippedRange.getMinY())));
+
+ rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap };
+}
+
+// Using tooling class BufferedDecompositionGroupPrimitive2D now, so
+// no more need to locally do the buffered get2DDecomposition here,
+// see BufferedDecompositionGroupPrimitive2D::get2DDecomposition
+void GlowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aGlowRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteGlowSize;
+ double fDiscreteGlowRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aGlowRange, aClippedRange, aDiscreteGlowSize,
+ fDiscreteGlowRadius, rViewInformation))
+ return;
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // First check is to detect if the last created decompose is capable
+ // to represent the now requested visualization.
+ // ClippedRange is the needed visualizationArea for the current glow
+ // effect, LastClippedRange is the one from the existing/last rendering.
+ // Check if last created area is sufficient and can be re-used
+ if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange))
+ {
+ // To avoid unnecessary invalidations due to being *very* correct
+ // with HairLines (which are view-dependent and thus change the
+ // result(s) here slightly when changing zoom), add a slight unsharp
+ // component if we have a ViewTransform. The derivation is inside
+ // the range of half a pixel (due to one pixel hairline)
+ basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange);
+
+ if (!rViewInformation.getObjectToViewTransformation().isIdentity())
+ {
+ // Grow by view-dependent size of 1/2 pixel
+ const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(0.5, 0))
+ .getLength());
+ aLastClippedRangeAndHairline.grow(fHalfPixel);
+ }
+
+ if (!aLastClippedRangeAndHairline.isInside(aClippedRange))
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<GlowPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+ }
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // Second check is to react on changes of the DiscreteGlowRadius when
+ // zooming in/out.
+ // Use the known last and current DiscreteGlowRadius to decide
+ // if the visualization can be re-used. Be a little 'creative' here
+ // and make it dependent on a *relative* change - it is not necessary
+ // to re-create everytime if the exact value is missed since zooming
+ // pixel-based glow effect is pretty good due to it's smooth nature
+ bool bFree(mfLastDiscreteGlowRadius <= 0.0 || fDiscreteGlowRadius <= 0.0);
+
+ if (!bFree)
+ {
+ const double fDiff(fabs(mfLastDiscreteGlowRadius - fDiscreteGlowRadius));
+ const double fLen(fabs(mfLastDiscreteGlowRadius) + fabs(fDiscreteGlowRadius));
+ const double fRelativeChange(fDiff / fLen);
+
+ // Use lower fixed values here to change more often, higher to change less often.
+ // Value is in the range of ]0.0 .. 1.0]
+ bFree = fRelativeChange >= 0.15;
+ }
+
+ if (bFree)
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<GlowPrimitive2D*>(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // refresh last used DiscreteGlowRadius and ClippedRange to new remembered values
+ const_cast<GlowPrimitive2D*>(this)->mfLastDiscreteGlowRadius = fDiscreteGlowRadius;
+ const_cast<GlowPrimitive2D*>(this)->maLastClippedRange = aClippedRange;
+ }
+
+ // call parent, that will check for empty, call create2DDecomposition and
+ // set as decomposition
+ BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
basegfx::B2DRange
GlowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
{
- basegfx::B2DRange aRetval(GroupPrimitive2D::getB2DRange(rViewInformation));
+ // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily)
+ // use the decompose - what works, but is not needed here.
+ // We know the to-be-visualized geometry and the radius it needs to be extended,
+ // so simply calculate the exact needed range.
+ basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+
// We need additional space for the glow from all sides
aRetval.grow(getGlowRadius());
+
return aRetval;
}
diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
index 0d57512f2f86..8219d25d59bc 100644
--- a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
@@ -27,16 +27,17 @@
#include <primitive2d/graphicprimitivehelper2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <utility>
namespace drawinglayer::primitive2d
{
-void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
- const geometry::ViewInformation2D&) const
+Primitive2DReference
+GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D&) const
{
if (0 == getGraphicAttr().GetAlpha())
{
// content is invisible, done
- return;
+ return nullptr;
}
// do not apply mirroring from GraphicAttr to the Metafile by calling
@@ -106,7 +107,7 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
if (aRetval.empty())
{
// content is invisible, done
- return;
+ return nullptr;
}
if (isAdjusted || isDrawMode)
@@ -126,7 +127,7 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
if (aRetval.empty())
{
// content is invisible, done
- return;
+ return nullptr;
}
}
@@ -138,10 +139,8 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
if (!basegfx::fTools::equalZero(fTransparency))
{
- Primitive2DReference aUnifiedTransparence(
- new UnifiedTransparencePrimitive2D(std::move(aRetval), fTransparency));
-
- aRetval = Primitive2DContainer{ aUnifiedTransparence };
+ aRetval = Primitive2DContainer{ new UnifiedTransparencePrimitive2D(std::move(aRetval),
+ fTransparency) };
}
}
@@ -157,33 +156,35 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
getGraphicAttr().GetBottomCrop()));
// embed content in cropPrimitive
- Primitive2DReference xPrimitive(
- new CropPrimitive2D(std::move(aRetval), aTransform,
- getGraphicAttr().GetLeftCrop() * aCropScaleFactor.getX(),
- getGraphicAttr().GetTopCrop() * aCropScaleFactor.getY(),
- getGraphicAttr().GetRightCrop() * aCropScaleFactor.getX(),
- getGraphicAttr().GetBottomCrop() * aCropScaleFactor.getY()));
-
- aRetval = Primitive2DContainer{ xPrimitive };
+ aRetval = Primitive2DContainer{ new CropPrimitive2D(
+ std::move(aRetval), aTransform,
+ getGraphicAttr().GetLeftCrop() * aCropScaleFactor.getX(),
+ getGraphicAttr().GetTopCrop() * aCropScaleFactor.getY(),
+ getGraphicAttr().GetRightCrop() * aCropScaleFactor.getX(),
+ getGraphicAttr().GetBottomCrop() * aCropScaleFactor.getY()) };
}
- rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
+ return new GroupPrimitive2D(std::move(aRetval));
}
-GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform,
+GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
const GraphicObject& rGraphicObject,
const GraphicAttr& rGraphicAttr)
- : maTransform(rTransform)
+ : maTransform(std::move(aTransform))
, maGraphicObject(rGraphicObject)
, maGraphicAttr(rGraphicAttr)
{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(20);
}
-GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform,
+GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform,
const GraphicObject& rGraphicObject)
- : maTransform(rTransform)
+ : maTransform(std::move(aTransform))
, maGraphicObject(rGraphicObject)
{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(20);
}
bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
index c91dd683106b..d756e6e3b74f 100644
--- a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
+++ b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
@@ -36,6 +36,7 @@
// helper class for animated graphics
+#include <utility>
#include <vcl/animate/Animation.hxx>
#include <vcl/graph.hxx>
#include <vcl/virdev.hxx>
@@ -74,7 +75,7 @@ namespace drawinglayer::primitive2d
Primitive2DReference maBufferedFirstFrame;
/// buffering of all frames
- Primitive2DContainer maBufferedPrimitives;
+ std::vector<Primitive2DReference> maBufferedPrimitives;
bool mbBufferingAllowed;
/// set if the animation is huge so that just always the next frame
@@ -103,6 +104,10 @@ namespace drawinglayer::primitive2d
maVirtualDeviceMask->EnableMapMode(false);
maVirtualDevice->SetOutputSizePixel(aTarget);
maVirtualDeviceMask->SetOutputSizePixel(aTarget);
+
+ // tdf#156630 make erase calls fill with transparency
+ maVirtualDevice->SetBackground(COL_BLACK);
+ maVirtualDeviceMask->SetBackground(COL_ALPHA_TRANSPARENT);
}
maVirtualDevice->Erase();
@@ -115,13 +120,13 @@ namespace drawinglayer::primitive2d
sal_uInt32 generateStepTime(sal_uInt32 nIndex) const
{
- const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(nIndex));
- sal_uInt32 nWaitTime(rAnimationBitmap.mnWait * 10);
+ const AnimationFrame& rAnimationFrame = maAnimation.Get(sal_uInt16(nIndex));
+ sal_uInt32 nWaitTime(rAnimationFrame.mnWait * 10);
// Take care of special value for MultiPage TIFFs. ATM these shall just
// show their first page. Later we will offer some switching when object
// is selected.
- if (ANIMATION_TIMEOUT_ON_CLICK == rAnimationBitmap.mnWait)
+ if (ANIMATION_TIMEOUT_ON_CLICK == rAnimationFrame.mnWait)
{
// ATM the huge value would block the timer, so
// use a long time to show first page (whole day)
@@ -180,14 +185,13 @@ namespace drawinglayer::primitive2d
}
else
{
- const Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
+ Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
+ // tdf#156630 invert the alpha mask
+ aMaskBitmap.Invert(); // convert from transparency to alpha
bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
}
- return Primitive2DReference(
- new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(bitmap),
- getTransform()));
+ return new BitmapPrimitive2D(bitmap, getTransform());
}
void checkSafeToBuffer(sal_uInt32 nIndex)
@@ -249,14 +253,23 @@ namespace drawinglayer::primitive2d
while (mnNextFrameToPrepare <= nTarget)
{
// prepare step
- const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(mnNextFrameToPrepare));
+ const AnimationFrame& rAnimationFrame = maAnimation.Get(sal_uInt16(mnNextFrameToPrepare));
+
+ bool bSourceBlending = rAnimationFrame.meBlend == Blend::Source;
+
+ if (bSourceBlending)
+ {
+ tools::Rectangle aArea(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmapEx.GetSizePixel());
+ maVirtualDevice->Erase(aArea);
+ maVirtualDeviceMask->Erase(aArea);
+ }
- switch (rAnimationBitmap.meDisposal)
+ switch (rAnimationFrame.meDisposal)
{
case Disposal::Not:
{
- maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx);
- Bitmap aAlphaMask = rAnimationBitmap.maBitmapEx.GetAlpha();
+ maVirtualDevice->DrawBitmapEx(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmapEx);
+ AlphaMask aAlphaMask = rAnimationFrame.maBitmapEx.GetAlphaMask();
if (aAlphaMask.IsEmpty())
{
@@ -267,8 +280,8 @@ namespace drawinglayer::primitive2d
}
else
{
- BitmapEx aExpandVisibilityMask(aAlphaMask, aAlphaMask);
- maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ BitmapEx aExpandVisibilityMask(aAlphaMask.GetBitmap(), aAlphaMask);
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aExpandVisibilityMask);
}
break;
@@ -276,32 +289,31 @@ namespace drawinglayer::primitive2d
case Disposal::Back:
{
// #i70772# react on no mask, for primitives, too.
- const Bitmap & rMask(rAnimationBitmap.maBitmapEx.GetAlpha());
- const Bitmap & rContent(rAnimationBitmap.maBitmapEx.GetBitmap());
+ const AlphaMask & rMask(rAnimationFrame.maBitmapEx.GetAlphaMask());
maVirtualDeviceMask->Erase();
- maVirtualDevice->DrawBitmap(rAnimationBitmap.maPositionPixel, rContent);
+ maVirtualDevice->DrawBitmapEx(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmapEx);
if (rMask.IsEmpty())
{
- const ::tools::Rectangle aRect(rAnimationBitmap.maPositionPixel, rContent.GetSizePixel());
+ const ::tools::Rectangle aRect(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmapEx.GetSizePixel());
maVirtualDeviceMask->SetFillColor(COL_BLACK);
maVirtualDeviceMask->SetLineColor();
maVirtualDeviceMask->DrawRect(aRect);
}
else
{
- BitmapEx aExpandVisibilityMask(rMask, rMask);
- maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ BitmapEx aExpandVisibilityMask(rMask.GetBitmap(), rMask);
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aExpandVisibilityMask);
}
break;
}
case Disposal::Previous:
{
- maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx);
- BitmapEx aExpandVisibilityMask(rAnimationBitmap.maBitmapEx.GetAlpha(), rAnimationBitmap.maBitmapEx.GetAlpha());
- maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ maVirtualDevice->DrawBitmapEx(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmapEx);
+ BitmapEx aExpandVisibilityMask(rAnimationFrame.maBitmapEx.GetAlphaMask().GetBitmap(), rAnimationFrame.maBitmapEx.GetAlphaMask());
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aExpandVisibilityMask);
break;
}
}
@@ -342,7 +354,8 @@ namespace drawinglayer::primitive2d
/// constructor
AnimatedGraphicPrimitive2D(
const Graphic& rGraphic,
- const basegfx::B2DHomMatrix& rTransform);
+ basegfx::B2DHomMatrix aTransform);
+ virtual ~AnimatedGraphicPrimitive2D();
/// data read access
const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
@@ -358,12 +371,12 @@ namespace drawinglayer::primitive2d
AnimatedGraphicPrimitive2D::AnimatedGraphicPrimitive2D(
const Graphic& rGraphic,
- const basegfx::B2DHomMatrix& rTransform)
+ basegfx::B2DHomMatrix aTransform)
: AnimatedSwitchPrimitive2D(
animation::AnimationEntryList(),
Primitive2DContainer(),
false),
- maTransform(rTransform),
+ maTransform(std::move(aTransform)),
maGraphic(rGraphic),
maAnimation(rGraphic.GetAnimation()),
maVirtualDevice(*Application::GetDefaultDevice()),
@@ -402,10 +415,27 @@ namespace drawinglayer::primitive2d
// prepare buffer space
if (mbBufferingAllowed && isValidData())
{
- maBufferedPrimitives = Primitive2DContainer(maAnimation.Count());
+ maBufferedPrimitives.resize(maAnimation.Count());
}
}
+ AnimatedGraphicPrimitive2D::~AnimatedGraphicPrimitive2D()
+ {
+ // Related: tdf#158807 mutex must be locked when disposing a VirtualDevice
+ // If the following .ppt document is opened in a debug build
+ // and the document is left open for a minute or two without
+ // changing any content, this destructor will be called on a
+ // non-main thread with the mutex unlocked:
+ // https://bugs.documentfoundation.org/attachment.cgi?id=46801
+ // This hits an assert in VirtualDevice::ReleaseGraphics() so
+ // explicitly lock the mutex and explicitly dispose and clear
+ // the VirtualDevice instances variables.
+ const SolarMutexGuard aSolarGuard;
+
+ maVirtualDevice.disposeAndClear();
+ maVirtualDeviceMask.disposeAndClear();
+ }
+
bool AnimatedGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
// do not use 'GroupPrimitive2D::operator==' here, that would compare
@@ -442,7 +472,7 @@ namespace drawinglayer::primitive2d
if (aRetval.is())
{
- rVisitor.append(aRetval);
+ rVisitor.visit(aRetval);
return;
}
@@ -461,14 +491,14 @@ namespace drawinglayer::primitive2d
if (aRetval.is())
{
- rVisitor.append(aRetval);
+ rVisitor.visit(aRetval);
return;
}
// did not work (not buffered and not 1st frame), create from buffer
aRetval = createFromBuffer();
- rVisitor.append(aRetval);
+ rVisitor.visit(aRetval);
}
} // end of namespace
@@ -489,17 +519,16 @@ namespace drawinglayer::primitive2d
if(rGraphic.IsAnimated())
{
// prepare specialized AnimatedGraphicPrimitive2D
- aRetval.resize(1);
- aRetval[0] = new AnimatedGraphicPrimitive2D(
+ aRetval = Primitive2DContainer { new AnimatedGraphicPrimitive2D(
rGraphic,
- rTransform);
+ rTransform) };
}
else if(rGraphic.getVectorGraphicData())
{
// embedded Vector Graphic Data fill, create embed transform
const basegfx::B2DRange& rSvgRange(rGraphic.getVectorGraphicData()->getRange());
- if(basegfx::fTools::more(rSvgRange.getWidth(), 0.0) && basegfx::fTools::more(rSvgRange.getHeight(), 0.0))
+ if(rSvgRange.getWidth() > 0.0 && rSvgRange.getHeight() > 0.0)
{
// translate back to origin, scale to unit coordinates
basegfx::B2DHomMatrix aEmbedVectorGraphic(
@@ -515,18 +544,16 @@ namespace drawinglayer::primitive2d
aEmbedVectorGraphic = rTransform * aEmbedVectorGraphic;
// add Vector Graphic Data primitives embedded
- aRetval.resize(1);
- aRetval[0] = new TransformPrimitive2D(
+ aRetval = Primitive2DContainer { new TransformPrimitive2D(
aEmbedVectorGraphic,
- rGraphic.getVectorGraphicData()->getPrimitive2DSequence());
+ Primitive2DContainer(rGraphic.getVectorGraphicData()->getPrimitive2DSequence()))};
}
}
else
{
- aRetval.resize(1);
- aRetval[0] = new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(rGraphic.GetBitmapEx()),
- rTransform);
+ aRetval = Primitive2DContainer { new BitmapPrimitive2D(
+ rGraphic.GetBitmapEx(),
+ rTransform) };
}
break;
@@ -537,10 +564,9 @@ namespace drawinglayer::primitive2d
// create MetafilePrimitive2D
const GDIMetaFile& rMetafile = rGraphic.GetGDIMetaFile();
- aRetval.resize(1);
- aRetval[0] = new MetafilePrimitive2D(
+ aRetval = Primitive2DContainer { new MetafilePrimitive2D(
rTransform,
- rMetafile);
+ rMetafile) };
// #i100357# find out if clipping is needed for this primitive. Unfortunately,
// there exist Metafiles who's content is bigger than the proposed PrefSize set
@@ -557,10 +583,11 @@ namespace drawinglayer::primitive2d
basegfx::B2DPolygon aMaskPolygon(basegfx::utils::createUnitPolygon());
aMaskPolygon.transform(rTransform);
- Primitive2DReference mask = new MaskPrimitive2D(
- basegfx::B2DPolyPolygon(aMaskPolygon),
- std::move(aRetval));
- aRetval = Primitive2DContainer { mask };
+ aRetval = Primitive2DContainer {
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(aMaskPolygon),
+ std::move(aRetval))
+ };
}
break;
}
@@ -572,7 +599,7 @@ namespace drawinglayer::primitive2d
}
}
- rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
+ rContainer.append(std::move(aRetval));
}
Primitive2DContainer create2DColorModifierEmbeddingsAsNeeded(
diff --git a/drawinglayer/source/primitive2d/gridprimitive2d.cxx b/drawinglayer/source/primitive2d/gridprimitive2d.cxx
index 515263e0d475..1a996188f03c 100644
--- a/drawinglayer/source/primitive2d/gridprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/gridprimitive2d.cxx
@@ -20,9 +20,11 @@
#include <drawinglayer/primitive2d/gridprimitive2d.hxx>
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -30,10 +32,10 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void GridPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference GridPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
if(!(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0))
- return;
+ return nullptr;
// decompose grid matrix to get logic size
basegfx::B2DVector aScale, aTranslate;
@@ -158,7 +160,7 @@ namespace drawinglayer::primitive2d
}
if(aExtendedViewport.isEmpty())
- return;
+ return nullptr;
// prepare point vectors for point and cross markers
std::vector< basegfx::B2DPoint > aPositionsPoint;
@@ -228,29 +230,31 @@ namespace drawinglayer::primitive2d
const sal_uInt32 nCountCross(aPositionsCross.size());
// add PointArrayPrimitive2D if point markers were added
+ Primitive2DContainer aContainer;
if(nCountPoint)
{
- rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsPoint), getBColor()));
+ aContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsPoint), getBColor()));
}
// add MarkerArrayPrimitive2D if cross markers were added
if(!nCountCross)
- return;
+ return new GroupPrimitive2D(std::move(aContainer));
if(!getSubdivisionsX() && !getSubdivisionsY())
{
// no subdivisions, so fall back to points at grid positions, no need to
// visualize a difference between divisions and sub-divisions
- rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsCross), getBColor()));
+ aContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsCross), getBColor()));
}
else
{
- rContainer.push_back(new MarkerArrayPrimitive2D(std::move(aPositionsCross), getCrossMarker()));
+ aContainer.push_back(new MarkerArrayPrimitive2D(std::move(aPositionsCross), getCrossMarker()));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
GridPrimitive2D::GridPrimitive2D(
- const basegfx::B2DHomMatrix& rTransform,
+ basegfx::B2DHomMatrix aTransform,
double fWidth,
double fHeight,
double fSmallestViewDistance,
@@ -259,7 +263,7 @@ namespace drawinglayer::primitive2d
sal_uInt32 nSubdivisionsY,
const basegfx::BColor& rBColor,
const BitmapEx& rCrossMarker)
- : maTransform(rTransform),
+ : maTransform(std::move(aTransform)),
mfWidth(fWidth),
mfHeight(fHeight),
mfSmallestViewDistance(fSmallestViewDistance),
@@ -305,18 +309,16 @@ namespace drawinglayer::primitive2d
void GridPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
- if(!getBuffered2DDecomposition().empty())
+ if(getBuffered2DDecomposition())
{
if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
{
// conditions of last local decomposition have changed, delete
- const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember ViewRange and ViewTransformation
const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
@@ -324,7 +326,6 @@ namespace drawinglayer::primitive2d
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/groupprimitive2d.cxx b/drawinglayer/source/primitive2d/groupprimitive2d.cxx
index 8c16d848fdff..e6428c09e8cd 100644
--- a/drawinglayer/source/primitive2d/groupprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/groupprimitive2d.cxx
@@ -31,7 +31,7 @@ namespace drawinglayer::primitive2d
{
}
- /** The compare opertator uses the Sequence::==operator, so only checking if
+ /** The compare operator uses the Sequence::==operator, so only checking if
the references are equal. All non-equal references are interpreted as
non-equal.
*/
@@ -53,16 +53,13 @@ namespace drawinglayer::primitive2d
getChildren(rVisitor);
}
- sal_Int64 SAL_CALL GroupPrimitive2D::estimateUsage()
+ sal_Int64 GroupPrimitive2D::estimateUsage()
{
size_t nRet(0);
for (auto& it : getChildren())
{
- uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
- if (xAcc.is())
- {
- nRet += xAcc->estimateUsage();
- }
+ if (it)
+ nRet += it->estimateUsage();
}
return nRet;
}
diff --git a/drawinglayer/source/primitive2d/helplineprimitive2d.cxx b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
index ba1a680560bb..047084eb1469 100644
--- a/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
@@ -19,10 +19,11 @@
#include <drawinglayer/primitive2d/helplineprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
using namespace com::sun::star;
@@ -30,14 +31,14 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void HelplinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference HelplinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
if(rViewInformation.getViewport().isEmpty() || getDirection().equalZero())
- return;
+ return nullptr;
// position to view coordinates, DashLen and DashLen in logic
const basegfx::B2DPoint aViewPosition(rViewInformation.getObjectToViewTransformation() * getPosition());
-
+ Primitive2DReference xRet;
switch(getStyle())
{
default : // HelplineStyle2D::Point
@@ -52,7 +53,7 @@ namespace drawinglayer::primitive2d
aLineA.append(aStartA);
aLineA.append(aEndA);
aLineA.transform(rViewInformation.getInverseObjectToViewTransformation());
- rContainer.push_back(new PolygonMarkerPrimitive2D(aLineA, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+ auto xMarker1 = new PolygonMarkerPrimitive2D(std::move(aLineA), getRGBColA(), getRGBColB(), getDiscreteDashLength());
const basegfx::B2DVector aPerpendicularNormalizedDirection(basegfx::getPerpendicular(aNormalizedDirection));
const basegfx::B2DPoint aStartB(aViewPosition - aPerpendicularNormalizedDirection);
@@ -61,8 +62,9 @@ namespace drawinglayer::primitive2d
aLineB.append(aStartB);
aLineB.append(aEndB);
aLineB.transform(rViewInformation.getInverseObjectToViewTransformation());
- rContainer.push_back(new PolygonMarkerPrimitive2D(aLineB, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+ auto xMarker2 = new PolygonMarkerPrimitive2D(std::move(aLineB), getRGBColA(), getRGBColB(), getDiscreteDashLength());
+ xRet = new GroupPrimitive2D(Primitive2DContainer{xMarker1, xMarker2});
break;
}
case HelplineStyle2D::Line :
@@ -106,18 +108,20 @@ namespace drawinglayer::primitive2d
{
// clip against visible area
const basegfx::B2DPolyPolygon aResult(basegfx::utils::clipPolygonOnRange(aLine, rViewInformation.getDiscreteViewport(), true, true));
-
+ Primitive2DContainer aContainer;
for(sal_uInt32 a(0); a < aResult.count(); a++)
{
basegfx::B2DPolygon aPart(aResult.getB2DPolygon(a));
aPart.transform(rViewInformation.getInverseObjectToViewTransformation());
- rContainer.push_back(new PolygonMarkerPrimitive2D(aPart, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+ aContainer.push_back(new PolygonMarkerPrimitive2D(std::move(aPart), getRGBColA(), getRGBColB(), getDiscreteDashLength()));
}
+ xRet = new GroupPrimitive2D(std::move(aContainer));
}
break;
}
}
+ return xRet;
}
HelplinePrimitive2D::HelplinePrimitive2D(
@@ -155,18 +159,16 @@ namespace drawinglayer::primitive2d
void HelplinePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
- if(!getBuffered2DDecomposition().empty())
+ if(getBuffered2DDecomposition())
{
if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
{
// conditions of last local decomposition have changed, delete
- const_cast< HelplinePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< HelplinePrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember ViewRange and ViewTransformation
const_cast< HelplinePrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
@@ -174,7 +176,6 @@ namespace drawinglayer::primitive2d
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
index ab66cb223a66..1702b16496d8 100644
--- a/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
@@ -22,6 +22,7 @@
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <toolkit/helper/vclunohelper.hxx>
@@ -30,19 +31,19 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void MarkerArrayPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference MarkerArrayPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
const std::vector< basegfx::B2DPoint >& rPositions = getPositions();
const sal_uInt32 nMarkerCount(rPositions.size());
if(!nMarkerCount || getMarker().IsEmpty())
- return;
+ return nullptr;
// get pixel size
Size aBitmapSize(getMarker().GetSizePixel());
if(!(aBitmapSize.Width() && aBitmapSize.Height()))
- return;
+ return nullptr;
// get logic half pixel size
basegfx::B2DVector aLogicHalfSize(rViewInformation.getInverseObjectToViewTransformation() *
@@ -51,9 +52,9 @@ namespace drawinglayer::primitive2d
// use half size for expand
aLogicHalfSize *= 0.5;
- for(sal_uInt32 a(0); a < nMarkerCount; a++)
+ Primitive2DContainer aContainer;
+ for(const auto& rPosition : rPositions)
{
- const basegfx::B2DPoint& rPosition(rPositions[a]);
const basegfx::B2DRange aRange(rPosition - aLogicHalfSize, rPosition + aLogicHalfSize);
basegfx::B2DHomMatrix aTransform;
@@ -62,11 +63,12 @@ namespace drawinglayer::primitive2d
aTransform.set(0, 2, aRange.getMinX());
aTransform.set(1, 2, aRange.getMinY());
- rContainer.push_back(
+ aContainer.push_back(
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getMarker()),
+ getMarker(),
aTransform));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
MarkerArrayPrimitive2D::MarkerArrayPrimitive2D(
diff --git a/drawinglayer/source/primitive2d/maskprimitive2d.cxx b/drawinglayer/source/primitive2d/maskprimitive2d.cxx
index 842085168827..630548861a90 100644
--- a/drawinglayer/source/primitive2d/maskprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/maskprimitive2d.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -27,10 +28,10 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
MaskPrimitive2D::MaskPrimitive2D(
- const basegfx::B2DPolyPolygon& rMask,
+ basegfx::B2DPolyPolygon aMask,
Primitive2DContainer&& aChildren)
: GroupPrimitive2D(std::move(aChildren)),
- maMask(rMask)
+ maMask(std::move(aMask))
{
}
diff --git a/drawinglayer/source/primitive2d/mediaprimitive2d.cxx b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
index 108f53bf1431..eb70c7602c8c 100644
--- a/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
@@ -21,6 +21,7 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <utility>
#include <vcl/GraphicObject.hxx>
#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
@@ -31,7 +32,7 @@
namespace drawinglayer::primitive2d
{
- void MediaPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference MediaPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
Primitive2DContainer xRetval;
xRetval.resize(1);
@@ -82,25 +83,26 @@ namespace drawinglayer::primitive2d
aTransform.translate(aDestRange.getMinX(), aDestRange.getMinY());
// add transform primitive
- Primitive2DReference aScaled(new TransformPrimitive2D(aTransform, std::move(xRetval)));
- xRetval = Primitive2DContainer { aScaled };
+ xRetval = Primitive2DContainer {
+ new TransformPrimitive2D(aTransform, std::move(xRetval)) // Scaled
+ };
}
}
- rContainer.insert(rContainer.end(), xRetval.begin(), xRetval.end());
+ return new GroupPrimitive2D(std::move(xRetval));
}
MediaPrimitive2D::MediaPrimitive2D(
- const basegfx::B2DHomMatrix& rTransform,
- const OUString& rURL,
+ basegfx::B2DHomMatrix aTransform,
+ OUString aURL,
const basegfx::BColor& rBackgroundColor,
sal_uInt32 nDiscreteBorder,
- const Graphic &rSnapshot)
- : maTransform(rTransform),
- maURL(rURL),
+ Graphic aSnapshot)
+ : maTransform(std::move(aTransform)),
+ maURL(std::move(aURL)),
maBackgroundColor(rBackgroundColor),
mnDiscreteBorder(nDiscreteBorder),
- maSnapshot(rSnapshot)
+ maSnapshot(std::move(aSnapshot))
{
}
@@ -113,7 +115,8 @@ namespace drawinglayer::primitive2d
return (getTransform() == rCompare.getTransform()
&& maURL == rCompare.maURL
&& getBackgroundColor() == rCompare.getBackgroundColor()
- && getDiscreteBorder() == rCompare.getDiscreteBorder());
+ && getDiscreteBorder() == rCompare.getDiscreteBorder()
+ && maSnapshot.IsNone() == rCompare.maSnapshot.IsNone());
}
return false;
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
index eddb02375d0a..46ddf6582571 100644
--- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -18,6 +18,7 @@
*/
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <utility>
#include <wmfemfhelper.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
@@ -30,67 +31,67 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void MetafilePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
// Interpret the Metafile and get the content. There should be only one target, as in the start condition,
// but iterating will be the right thing to do when some push/pop is not closed
Primitive2DContainer xRetval(wmfemfhelper::interpretMetafile(getMetaFile(), rViewInformation));
- if(!xRetval.empty())
+ if(xRetval.empty())
+ return nullptr;
+
+ // get target size
+ const ::tools::Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
+ const basegfx::B2DRange aMtfRange(vcl::unotools::b2DRectangleFromRectangle(aMtfTarget));
+
+ // tdf#113197 get content range and check if we have an overlap with
+ // defined target range (aMtfRange)
+ if (!aMtfRange.isEmpty())
{
- // get target size
- const ::tools::Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
- const basegfx::B2DRange aMtfRange(vcl::unotools::b2DRectangleFromRectangle(aMtfTarget));
+ const basegfx::B2DRange aContentRange(xRetval.getB2DRange(rViewInformation));
- // tdf#113197 get content range and check if we have an overlap with
- // defined target range (aMtfRange)
- if (!aMtfRange.isEmpty())
+ // also test equal since isInside gives also true for equal
+ if (!aMtfRange.equal(aContentRange) && !aMtfRange.isInside(aContentRange))
{
- const basegfx::B2DRange aContentRange(xRetval.getB2DRange(rViewInformation));
-
- // also test equal since isInside gives also true for equal
- if (!aMtfRange.equal(aContentRange) && !aMtfRange.isInside(aContentRange))
- {
- // contentRange is partly larger than aMtfRange (stuff sticks
- // outside), clipping is needed
- const drawinglayer::primitive2d::Primitive2DReference xMask(
- new drawinglayer::primitive2d::MaskPrimitive2D(
- basegfx::B2DPolyPolygon(
- basegfx::utils::createPolygonFromRect(
- aMtfRange)),
- std::move(xRetval)));
-
- xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask };
- }
+ // contentRange is partly larger than aMtfRange (stuff sticks
+ // outside), clipping is needed
+ const drawinglayer::primitive2d::Primitive2DReference xMask(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(
+ aMtfRange)),
+ std::move(xRetval)));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask };
}
+ }
- // create transformation
- basegfx::B2DHomMatrix aAdaptedTransform;
-
- aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
- aAdaptedTransform.scale(
- aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0,
- aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0);
- aAdaptedTransform = getTransform() * aAdaptedTransform;
+ // create transformation
+ basegfx::B2DHomMatrix aAdaptedTransform;
- // embed to target transformation
- const Primitive2DReference aEmbeddedTransform(
- new TransformPrimitive2D(
- aAdaptedTransform,
- std::move(xRetval)));
+ aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
+ aAdaptedTransform.scale(
+ aMtfTarget.getOpenWidth() ? 1.0 / aMtfTarget.getOpenWidth() : 1.0,
+ aMtfTarget.getOpenHeight() ? 1.0 / aMtfTarget.getOpenHeight() : 1.0);
+ aAdaptedTransform = getTransform() * aAdaptedTransform;
- xRetval = Primitive2DContainer { aEmbeddedTransform };
- }
+ // embed to target transformation
+ const Primitive2DReference aEmbeddedTransform(
+ new TransformPrimitive2D(
+ aAdaptedTransform,
+ std::move(xRetval)));
- rContainer.insert(rContainer.end(), xRetval.begin(), xRetval.end());
+ return aEmbeddedTransform;
}
MetafilePrimitive2D::MetafilePrimitive2D(
- const basegfx::B2DHomMatrix& rMetaFileTransform,
+ basegfx::B2DHomMatrix aMetaFileTransform,
const GDIMetaFile& rMetaFile)
- : maMetaFileTransform(rMetaFileTransform),
+ : maMetaFileTransform(std::move(aMetaFileTransform)),
maMetaFile(rMetaFile)
{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(20);
}
bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
diff --git a/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
index 2d0f572dad39..9786f9164e7f 100644
--- a/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -28,9 +29,9 @@ namespace drawinglayer::primitive2d
{
ModifiedColorPrimitive2D::ModifiedColorPrimitive2D(
Primitive2DContainer&& aChildren,
- const basegfx::BColorModifierSharedPtr& rColorModifier)
+ basegfx::BColorModifierSharedPtr xColorModifier)
: GroupPrimitive2D(std::move(aChildren)),
- maColorModifier(rColorModifier)
+ maColorModifier(std::move(xColorModifier))
{
}
diff --git a/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
index 577f3171a3dd..0c91957766a4 100644
--- a/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
@@ -18,6 +18,7 @@
*/
#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -25,13 +26,13 @@ namespace drawinglayer::primitive2d
{
ObjectInfoPrimitive2D::ObjectInfoPrimitive2D(
Primitive2DContainer&& aChildren,
- const OUString& rName,
- const OUString& rTitle,
- const OUString& rDesc)
+ OUString aName,
+ OUString aTitle,
+ OUString aDesc)
: GroupPrimitive2D(std::move(aChildren)),
- maName(rName),
- maTitle(rTitle),
- maDesc(rDesc)
+ maName(std::move(aName)),
+ maTitle(std::move(aTitle)),
+ maDesc(std::move(aDesc))
{
}
diff --git a/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
index 7aa1a440555e..a4280ea1aba0 100644
--- a/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
@@ -24,6 +24,7 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -31,22 +32,22 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
- void PagePreviewPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference PagePreviewPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
Primitive2DContainer aContent(getPageContent());
if(!(!aContent.empty()
- && basegfx::fTools::more(getContentWidth(), 0.0)
- && basegfx::fTools::more(getContentHeight(), 0.0)))
- return;
+ && getContentWidth() > 0.0)
+ && getContentHeight() > 0.0)
+ return nullptr;
// the decomposed matrix will be needed
basegfx::B2DVector aScale, aTranslate;
double fRotate, fShearX;
getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
- if(!(basegfx::fTools::more(aScale.getX(), 0.0) && basegfx::fTools::more(aScale.getY(), 0.0)))
- return;
+ if(!(aScale.getX() > 0.0 && aScale.getY() > 0.0))
+ return nullptr;
// check if content overlaps with target size and needs to be embedded with a
// clipping primitive
@@ -97,18 +98,18 @@ namespace drawinglayer::primitive2d
aPageTrans = aCombined * aPageTrans;
// embed in necessary transformation to map from SdrPage to SdrPageObject
- rContainer.push_back(new TransformPrimitive2D(aPageTrans, std::move(aContent)));
+ return new TransformPrimitive2D(aPageTrans, std::move(aContent));
}
PagePreviewPrimitive2D::PagePreviewPrimitive2D(
- const css::uno::Reference< css::drawing::XDrawPage >& rxDrawPage,
- const basegfx::B2DHomMatrix& rTransform,
+ css::uno::Reference< css::drawing::XDrawPage > xDrawPage,
+ basegfx::B2DHomMatrix aTransform,
double fContentWidth,
double fContentHeight,
Primitive2DContainer&& rPageContent)
- : mxDrawPage(rxDrawPage),
+ : mxDrawPage(std::move(xDrawPage)),
maPageContent(std::move(rPageContent)),
- maTransform(rTransform),
+ maTransform(std::move(aTransform)),
mfContentWidth(fContentWidth),
mfContentHeight(fContentHeight)
{
diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
index cd4c58d11127..516b0042d960 100644
--- a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
@@ -29,6 +29,7 @@
#include <toolkit/helper/vclunohelper.hxx>
#include <drawinglayer/converters.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -125,11 +126,11 @@ namespace drawinglayer::primitive2d
if(0 != mnDiscreteWidth && 0 != mnDiscreteHeight)
{
const geometry::ViewInformation2D aViewInformation2D;
- primitive2d::Primitive2DReference xEmbedRef(
- new primitive2d::TransformPrimitive2D(
- basegfx::utils::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight),
- Primitive2DContainer(getChildren())));
- primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+ primitive2d::Primitive2DContainer xEmbedSeq {
+ new primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight),
+ Primitive2DContainer(getChildren()))
+ };
const BitmapEx aBitmapEx(
convertToBitmapEx(
@@ -147,7 +148,7 @@ namespace drawinglayer::primitive2d
{
const primitive2d::Primitive2DReference xEmbedRefBitmap(
new primitive2d::BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(aBitmapEx),
+ aBitmapEx,
basegfx::B2DHomMatrix()));
aContent = primitive2d::Primitive2DContainer { xEmbedRefBitmap };
}
@@ -201,20 +202,20 @@ namespace drawinglayer::primitive2d
nWidth * nHeight);
}
- void PatternFillPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference PatternFillPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
Primitive2DContainer aRetval;
if(getChildren().empty())
- return;
+ return nullptr;
if(!(!getReferenceRange().isEmpty() && getReferenceRange().getWidth() > 0.0 && getReferenceRange().getHeight() > 0.0))
- return;
+ return nullptr;
const basegfx::B2DRange aMaskRange(getMask().getB2DRange());
if(!(!aMaskRange.isEmpty() && aMaskRange.getWidth() > 0.0 && aMaskRange.getHeight() > 0.0))
- return;
+ return nullptr;
// create tiling matrices
std::vector< basegfx::B2DHomMatrix > aMatrices;
@@ -243,28 +244,25 @@ namespace drawinglayer::primitive2d
aMaskRange.getRange(),
aMaskRange.getMinimum()));
- Primitive2DReference xRef(
- new TransformPrimitive2D(
- aMaskTransform,
- std::move(aRetval)));
-
- aRetval = Primitive2DContainer { xRef };
+ aRetval = Primitive2DContainer {
+ new TransformPrimitive2D(
+ aMaskTransform,
+ std::move(aRetval))
+ };
}
// embed result in mask
- {
- rContainer.push_back(
- new MaskPrimitive2D(
- getMask(),
- std::move(aRetval)));
- }
+ return
+ new MaskPrimitive2D(
+ getMask(),
+ std::move(aRetval));
}
PatternFillPrimitive2D::PatternFillPrimitive2D(
- const basegfx::B2DPolyPolygon& rMask,
+ basegfx::B2DPolyPolygon aMask,
Primitive2DContainer&& rChildren,
const basegfx::B2DRange& rReferenceRange)
- : maMask(rMask),
+ : maMask(std::move(aMask)),
maChildren(std::move(rChildren)),
maReferenceRange(rReferenceRange),
mnDiscreteWidth(0),
@@ -343,24 +341,19 @@ namespace drawinglayer::primitive2d
PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
pThat->mnDiscreteWidth = nW;
pThat->mnDiscreteHeight = nH;
- pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ pThat->setBuffered2DDecomposition(nullptr);
}
// call parent
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
- sal_Int64 SAL_CALL PatternFillPrimitive2D::estimateUsage()
+ sal_Int64 PatternFillPrimitive2D::estimateUsage()
{
size_t nRet(0);
for (auto& it : getChildren())
- {
- uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY);
- if (xAcc.is())
- {
- nRet += xAcc->estimateUsage();
- }
- }
+ if (it)
+ nRet += it->estimateUsage();
return nRet;
}
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
index 25e78edffff4..ab6833a44ffa 100644
--- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -17,22 +17,48 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <basegfx/polygon/b2dlinegeometry.hxx>
#include <com/sun/star/drawing/LineCap.hpp>
+#include <utility>
using namespace com::sun::star;
+namespace
+{
+void implGrowHairline(basegfx::B2DRange& rRange,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+{
+ if (!rRange.isEmpty())
+ {
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
+
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
+ {
+ rRange.grow(fDiscreteHalfLineWidth);
+ }
+ }
+}
+}
+
namespace drawinglayer::primitive2d
{
-PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(basegfx::B2DPolygon aPolygon,
const basegfx::BColor& rBColor)
- : maPolygon(rPolygon)
+ : maPolygon(std::move(aPolygon))
, maBColor(rBColor)
{
}
@@ -57,18 +83,8 @@ PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rView
// as base size
basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange());
- if (!aRetval.isEmpty())
- {
- // Calculate view-dependent hairline width
- const basegfx::B2DVector aDiscreteSize(
- rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
- const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
-
- if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
- {
- aRetval.grow(fDiscreteHalfLineWidth);
- }
- }
+ // Calculate and grow by view-dependent hairline width
+ implGrowHairline(aRetval, rViewInformation);
// return range
return aRetval;
@@ -80,8 +96,120 @@ sal_uInt32 PolygonHairlinePrimitive2D::getPrimitive2DID() const
return PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D;
}
-void PolygonMarkerPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+SingleLinePrimitive2D::SingleLinePrimitive2D(const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ const basegfx::BColor& rBColor)
+ : BasePrimitive2D()
+ , maStart(rStart)
+ , maEnd(rEnd)
+ , maBColor(rBColor)
+{
+}
+
+bool SingleLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const SingleLinePrimitive2D& rCompare(
+ static_cast<const SingleLinePrimitive2D&>(rPrimitive));
+
+ return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+SingleLinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(getStart(), getEnd());
+
+ // Calculate and grow by view-dependent hairline width
+ implGrowHairline(aRetval, rViewInformation);
+
+ return aRetval;
+}
+
+sal_uInt32 SingleLinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D;
+}
+
+void SingleLinePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getStart() == getEnd())
+ {
+ // single point
+ Primitive2DContainer aSequence = { new PointArrayPrimitive2D(
+ std::vector<basegfx::B2DPoint>{ getStart() }, getBColor()) };
+ rVisitor.visit(aSequence);
+ }
+ else
+ {
+ // line
+ Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(
+ basegfx::B2DPolygon{ getStart(), getEnd() }, getBColor()) };
+ rVisitor.visit(aSequence);
+ }
+}
+
+LineRectanglePrimitive2D::LineRectanglePrimitive2D(const basegfx::B2DRange& rB2DRange,
+ const basegfx::BColor& rBColor)
+ : BasePrimitive2D()
+ , maB2DRange(rB2DRange)
+ , maBColor(rBColor)
+{
+}
+
+bool LineRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const LineRectanglePrimitive2D& rCompare(
+ static_cast<const LineRectanglePrimitive2D&>(rPrimitive));
+
+ return (getB2DRange() == rCompare.getB2DRange() && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+LineRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(getB2DRange());
+
+ // Calculate and grow by view-dependent hairline width
+ implGrowHairline(aRetval, rViewInformation);
+
+ return aRetval;
+}
+
+sal_uInt32 LineRectanglePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D;
+}
+
+void LineRectanglePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(getB2DRange()));
+ Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(aPolygon, getBColor()) };
+ rVisitor.visit(aSequence);
+}
+
+Primitive2DReference PolygonMarkerPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& rViewInformation) const
{
// calculate logic DashLength
const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation()
@@ -100,20 +228,24 @@ void PolygonMarkerPrimitive2D::create2DDecomposition(
basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA,
&aDashedPolyPolyB, 2.0 * fLogicDashLength);
- rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA, getRGBColorA()));
- rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB, getRGBColorB()));
+ Primitive2DContainer aContainer;
+ aContainer.push_back(
+ new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyA), getRGBColorA()));
+ aContainer.push_back(
+ new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyB), getRGBColorB()));
+ return new GroupPrimitive2D(std::move(aContainer));
}
else
{
- rContainer.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
+ return new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA());
}
}
-PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(const basegfx::B2DPolygon& rPolygon,
+PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(basegfx::B2DPolygon aPolygon,
const basegfx::BColor& rRGBColorA,
const basegfx::BColor& rRGBColorB,
double fDiscreteDashLength)
- : maPolygon(rPolygon)
+ : maPolygon(std::move(aPolygon))
, maRGBColorA(rRGBColorA)
, maRGBColorB(rRGBColorB)
, mfDiscreteDashLength(fDiscreteDashLength)
@@ -164,10 +296,9 @@ void PolygonMarkerPrimitive2D::get2DDecomposition(
Primitive2DDecompositionVisitor& rVisitor,
const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard(m_aMutex);
bool bNeedNewDecomposition(false);
- if (!getBuffered2DDecomposition().empty())
+ if (getBuffered2DDecomposition())
{
if (rViewInformation.getInverseObjectToViewTransformation()
!= maLastInverseObjectToViewTransformation)
@@ -179,11 +310,10 @@ void PolygonMarkerPrimitive2D::get2DDecomposition(
if (bNeedNewDecomposition)
{
// conditions of last local decomposition have changed, delete
- const_cast<PolygonMarkerPrimitive2D*>(this)->setBuffered2DDecomposition(
- Primitive2DContainer());
+ const_cast<PolygonMarkerPrimitive2D*>(this)->setBuffered2DDecomposition(nullptr);
}
- if (getBuffered2DDecomposition().empty())
+ if (!getBuffered2DDecomposition())
{
// remember last used InverseObjectToViewTransformation
PolygonMarkerPrimitive2D* pThat = const_cast<PolygonMarkerPrimitive2D*>(this);
@@ -192,7 +322,6 @@ void PolygonMarkerPrimitive2D::get2DDecomposition(
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
@@ -206,11 +335,11 @@ sal_uInt32 PolygonMarkerPrimitive2D::getPrimitive2DID() const
namespace drawinglayer::primitive2d
{
-void PolygonStrokePrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolygonStrokePrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (!getB2DPolygon().count())
- return;
+ return nullptr;
// #i102241# try to simplify before usage
const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon()));
@@ -250,29 +379,33 @@ void PolygonStrokePrimitive2D::create2DDecomposition(
}
// create primitive
+ Primitive2DContainer aContainer;
for (sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++)
{
// put into single polyPolygon primitives to make clear that this is NOT meant
// to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a
// melting process may be used here one day.
- const basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
+ basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b));
const basegfx::BColor aColor(getLineAttribute().getColor());
- rContainer.push_back(new PolyPolygonColorPrimitive2D(aNewPolyPolygon, aColor));
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(std::move(aNewPolyPolygon), aColor));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
else
{
- rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aHairLinePolyPolygon,
- getLineAttribute().getColor()));
+ return new PolyPolygonHairlinePrimitive2D(std::move(aHairLinePolyPolygon),
+ getLineAttribute().getColor());
}
}
-PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
- const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
- const attribute::StrokeAttribute& rStrokeAttribute)
- : maPolygon(rPolygon)
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ attribute::StrokeAttribute aStrokeAttribute)
+ : maPolygon(std::move(aPolygon))
, maLineAttribute(rLineAttribute)
- , maStrokeAttribute(rStrokeAttribute)
+ , maStrokeAttribute(std::move(aStrokeAttribute))
+ , maBufferedRange()
{
// MM01: keep these - these are no curve-decompposers but just checks
// simplify curve segments: moved here to not need to use it
@@ -280,10 +413,11 @@ PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
}
-PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon,
const attribute::LineAttribute& rLineAttribute)
- : maPolygon(rPolygon)
+ : maPolygon(std::move(aPolygon))
, maLineAttribute(rLineAttribute)
+ , maBufferedRange()
{
// MM01: keep these - these are no curve-decompposers but just checks
// simplify curve segments: moved here to not need to use it
@@ -309,7 +443,11 @@ bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) con
basegfx::B2DRange
PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
{
- basegfx::B2DRange aRetval;
+ if (!maBufferedRange.isEmpty())
+ {
+ // use the view-independent, buffered B2DRange
+ return maBufferedRange;
+ }
if (getLineAttribute().getWidth())
{
@@ -329,6 +467,20 @@ PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewIn
// the grow method below works perfectly for LineCap_ROUND since the grow is in
// all directions and the rounded cap needs the same grow in all directions independent
// from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE
+
+ // NOTE: I thought about using [sqrt(2) * 0.5] a a factor for LineCap_SQUARE and not
+ // set bUseDecomposition. I even tried that it works. Then an auto-test failing showed
+ // not only that view-dependent stuff needs to be considered (what is done for the
+ // hairline case below), *BUT* also e.g. conversions to PNG/exports use the B2DRange
+ // of the geometry, so:
+ // - expanding by 1/2 LineWidth is OK for rounded
+ // - expanding by more (like sqrt(2) * 0.5 * LineWidth) immediately extends the size
+ // of e.g. geometry converted to PNG, plus many similar cases that cannot be thought
+ // of in advance.
+ // This means that converting those thought-experiment examples in (4) will and do lead
+ // to bigger e.g. Bitmap conversion(s), not avoiding but painting the free space. That
+ // could only be done by correctly and fully decomposing the geometry, including
+ // stroke, and accepting the cost...
bUseDecomposition = true;
}
@@ -336,44 +488,108 @@ PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewIn
{
// get correct range by using the decomposition fallback, reasons see above cases
- // ofz#947 to optimize calculating the range, turn any slow dashes into a solid line
- // when just calculating bounds
- attribute::StrokeAttribute aOrigStrokeAttribute = maStrokeAttribute;
- const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute
- = attribute::StrokeAttribute();
- aRetval = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
- const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute = aOrigStrokeAttribute;
+ // It is not a good idea to temporarily (re)set the PolygonStrokePrimitive2D
+ // to default. While it is understandable to use the speed advantage, it is
+ // bad for quite some reasons:
+ //
+ // (1) As described in include/drawinglayer/primitive2d/baseprimitive2d.hxx
+ // a Primitive is "noncopyable to make clear that a primitive is a read-only
+ // instance and copying or changing values is not intended". This is the base
+ // assumption for many decisions for Primitive handling.
+ // (2) For example, that the decomposition is *always* re-usable. It cannot change
+ // and is correct when it already exists since the values the decomposition is
+ // based on cannot change.
+ // (3) If this *is* done (like it was here) and the Primitive is derived from
+ // BufferedDecompositionPrimitive2D and thus buffers it's decomposition,
+ // the risk is that in this case the *wrong* decomposition will be used by
+ // other PrimitiveProcessors. Maybe not by the VclPixelProcessor2D/VclProcessor2D
+ // since it handles this primitive directly - not even sure for all cases.
+ // Sooner or later another PrimitiveProcessor will re-use this wrong temporary
+ // decomposition, and as an error, a non-stroked line will be painted/used.
+ // (4) The B2DRange is not strictly defined as minimal bound for the geometry,
+ // but it should be as small/tight as possible. Making it larger risks more
+ // area to be invalidated (repaint) and processed (all geometric stuff,l may
+ // include future and existing exports to other formats which are or will be
+ // implemented as PrimitiveProcessor). It is easy to imagine cases with much
+ // too large B2DRange - a line with a pattern that would solve to a single
+ // small start-rectangle and rest is empty, or a circle with a stroke that
+ // makes only a quarter of it visible.
+ //
+ // The reason to do this is speed, what is a good argument. But speed should
+ // only be used if the pair of [correctness/speed] does not sacrifice the correctness
+ // over the speed.
+ // Luckily there are alternatives to solve this and to keep [correctness/speed]
+ // valid:
+ //
+ // (a) Reset the temporary decomposition after having const-casted and
+ // changed maStrokeAttribute.
+ // Disadvantage: More const-cast hacks, plus this temporary decomposition
+ // will be potentially done repeatedly (every time
+ // PolygonStrokePrimitive2D::getB2DRange is called)
+ // (b) Use a temporary, local PolygonStrokePrimitive2D here, with neutral
+ // PolygonStrokePrimitive2D and call ::getB2DRange() at it. That way
+ // the buffered decomposition will not be harmed.
+ // Disadvantage: Same as (a), decomposition will be potentially done repeatedly
+ // (c) Use a temporary, local PolygonStrokePrimitive2D and buffer B2DRange
+ // locally for this Primitive. Due to (1)/(2) this cannot change, so
+ // when calculated once it is totally legal to use it.
+ //
+ // Thus here I would use (c): It accepts the disadvantages of (4) over speed, but
+ // avoids the errors/problems from (1-4).
+ // Additional argument for this: The hairline case below *also* uses the full
+ // B2DRange of the polygon, ignoring an evtl. stroke, so (4) applies
+ if (!getStrokeAttribute().isDefault())
+ {
+ // only do this if StrokeAttribute is used, else recursion may happen (!)
+ const rtl::Reference<primitive2d::PolygonStrokePrimitive2D>
+ aTemporaryPrimitiveWithoutStroke(new primitive2d::PolygonStrokePrimitive2D(
+ getB2DPolygon(), getLineAttribute()));
+ maBufferedRange
+ = aTemporaryPrimitiveWithoutStroke
+ ->BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // fallback to normal decompose, that result can be used for visualization
+ // later, too. Still buffer B2DRange in maBufferedRange, so it needs to be
+ // merged into one B2DRange only once
+ maBufferedRange = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
}
else
{
// for all other B2DLINEJOIN_* get the range from the base geometry
- // and expand by half the line width
- aRetval = getB2DPolygon().getB2DRange();
- aRetval.grow(getLineAttribute().getWidth() * 0.5);
+ // and expand by half the line width.
+ maBufferedRange = getB2DPolygon().getB2DRange();
+ maBufferedRange.grow(getLineAttribute().getWidth() * 0.5);
}
+
+ return maBufferedRange;
}
- else
+
+ // It is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size.
+ // CAUTION: Since a hairline *is* view-dependent,
+ // - either use maBufferedRange, additionally remember view-dependent
+ // factor & reset if that changes
+ // - or do not buffer for hairline -> not really needed, the range is buffered
+ // in the B2DPolygon, no decomposition is needed and a simple grow is cheap
+ basegfx::B2DRange aHairlineRange = getB2DPolygon().getB2DRange();
+
+ if (!aHairlineRange.isEmpty())
{
- // this is a hairline, thus the line width is view-dependent. Get range of polygon
- // as base size
- aRetval = getB2DPolygon().getB2DRange();
+ // Calculate view-dependent hairline width
+ const basegfx::B2DVector aDiscreteSize(
+ rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
- if (!aRetval.isEmpty())
+ if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
{
- // Calculate view-dependent hairline width
- const basegfx::B2DVector aDiscreteSize(
- rViewInformation.getInverseObjectToViewTransformation()
- * basegfx::B2DVector(1.0, 0.0));
- const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5);
-
- if (basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0))
- {
- aRetval.grow(fDiscreteHalfLineWidth);
- }
+ aHairlineRange.grow(fDiscreteHalfLineWidth);
}
}
- return aRetval;
+ return aHairlineRange;
}
// provide unique ID
@@ -382,11 +598,11 @@ sal_uInt32 PolygonStrokePrimitive2D::getPrimitive2DID() const
return PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D;
}
-void PolygonWavePrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolygonWavePrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (!getB2DPolygon().count())
- return;
+ return nullptr;
const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
@@ -394,16 +610,16 @@ void PolygonWavePrimitive2D::create2DDecomposition(
if (bHasWidth && bHasHeight)
{
// create waveline curve
- const basegfx::B2DPolygon aWaveline(
+ basegfx::B2DPolygon aWaveline(
basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
- rContainer.push_back(
- new PolygonStrokePrimitive2D(aWaveline, getLineAttribute(), getStrokeAttribute()));
+ return new PolygonStrokePrimitive2D(std::move(aWaveline), getLineAttribute(),
+ getStrokeAttribute());
}
else
{
// flat waveline, decompose to simple line primitive
- rContainer.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
- getStrokeAttribute()));
+ return new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
+ getStrokeAttribute());
}
}
@@ -485,8 +701,8 @@ sal_uInt32 PolygonWavePrimitive2D::getPrimitive2DID() const
return PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D;
}
-void PolygonStrokeArrowPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference PolygonStrokeArrowPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
// copy local polygon, it may be changed
basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
@@ -536,20 +752,22 @@ void PolygonStrokeArrowPrimitive2D::create2DDecomposition(
}
// add shaft
- rContainer.push_back(
- new PolygonStrokePrimitive2D(aLocalPolygon, getLineAttribute(), getStrokeAttribute()));
+ Primitive2DContainer aContainer;
+ aContainer.push_back(new PolygonStrokePrimitive2D(std::move(aLocalPolygon), getLineAttribute(),
+ getStrokeAttribute()));
if (aArrowA.count())
{
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(aArrowA, getLineAttribute().getColor()));
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(std::move(aArrowA), getLineAttribute().getColor()));
}
if (aArrowB.count())
{
- rContainer.push_back(
- new PolyPolygonColorPrimitive2D(aArrowB, getLineAttribute().getColor()));
+ aContainer.push_back(
+ new PolyPolygonColorPrimitive2D(std::move(aArrowB), getLineAttribute().getColor()));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
diff --git a/drawinglayer/source/primitive2d/primitivetools2d.cxx b/drawinglayer/source/primitive2d/primitivetools2d.cxx
index c4ab4f5c800b..04a91fe9b55b 100644
--- a/drawinglayer/source/primitive2d/primitivetools2d.cxx
+++ b/drawinglayer/source/primitive2d/primitivetools2d.cxx
@@ -26,26 +26,23 @@ namespace drawinglayer::primitive2d
{
void DiscreteMetricDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
// get the current DiscreteUnit, look at X and Y and use the maximum
const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
const double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
- if(!getBuffered2DDecomposition().empty() && !basegfx::fTools::equal(fDiscreteUnit, getDiscreteUnit()))
+ if(getBuffered2DDecomposition() && !basegfx::fTools::equal(fDiscreteUnit, getDiscreteUnit()))
{
// conditions of last local decomposition have changed, delete
- const_cast< DiscreteMetricDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< DiscreteMetricDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember new valid DiscreteUnit
const_cast< DiscreteMetricDependentPrimitive2D* >(this)->mfDiscreteUnit = fDiscreteUnit;
}
// call base implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
@@ -54,75 +51,67 @@ namespace drawinglayer::primitive2d
void ViewportDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
// get the current Viewport
const basegfx::B2DRange& rViewport = rViewInformation.getViewport();
- if(!getBuffered2DDecomposition().empty() && !rViewport.equal(getViewport()))
+ if(getBuffered2DDecomposition() && !rViewport.equal(getViewport()))
{
// conditions of last local decomposition have changed, delete
- const_cast< ViewportDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< ViewportDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember new valid DiscreteUnit
const_cast< ViewportDependentPrimitive2D* >(this)->maViewport = rViewport;
}
// call base implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
void ViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
// get the current ViewTransformation
const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation();
- if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation())
+ if(getBuffered2DDecomposition() && rViewTransformation != getViewTransformation())
{
// conditions of last local decomposition have changed, delete
- const_cast< ViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< ViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember new valid ViewTransformation
const_cast< ViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation;
}
// call base implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
void ObjectAndViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
// get the current ViewTransformation
const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation();
- if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation())
+ if(getBuffered2DDecomposition() && rViewTransformation != getViewTransformation())
{
// conditions of last local decomposition have changed, delete
- const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
// get the current ObjectTransformation
const basegfx::B2DHomMatrix& rObjectTransformation = rViewInformation.getObjectTransformation();
- if(!getBuffered2DDecomposition().empty() && rObjectTransformation != getObjectTransformation())
+ if(getBuffered2DDecomposition() && rObjectTransformation != getObjectTransformation())
{
// conditions of last local decomposition have changed, delete
- const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
// remember new valid ViewTransformation, and ObjectTransformation
const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation;
@@ -130,7 +119,6 @@ namespace drawinglayer::primitive2d
}
// call base implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
index 870d187b222c..11807e459b7f 100644
--- a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -23,17 +23,21 @@
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <processor3d/zbufferprocessor3d.hxx>
#include <processor3d/shadow3dextractor.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <processor3d/geometry2dextractor.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <basegfx/raster/bzpixelraster.hxx>
+#include <utility>
#include <vcl/BitmapTools.hxx>
#include <comphelper/threadpool.hxx>
+#include <comphelper/lok.hxx>
#include <toolkit/helper/vclunohelper.hxx>
+#include <officecfg/Office/Common.hxx>
using namespace com::sun::star;
@@ -209,8 +213,9 @@ namespace drawinglayer::primitive2d
}
}
- void ScenePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ Primitive2DReference ScenePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
+ Primitive2DContainer aContainer;
// create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
// there are some or not. Do this at start, the shadow might still be visible even when the scene is not
if(impGetShadow3D())
@@ -223,7 +228,7 @@ namespace drawinglayer::primitive2d
if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
{
// add extracted 2d shadows (before 3d scene creations itself)
- rContainer.insert(rContainer.end(), maShadowPrimitives.begin(), maShadowPrimitives.end());
+ aContainer.append(maShadowPrimitives);
}
}
@@ -235,13 +240,13 @@ namespace drawinglayer::primitive2d
calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
if(aVisibleDiscreteRange.isEmpty())
- return;
+ return new GroupPrimitive2D(std::move(aContainer));
// test if discrete view size (pixel) maybe too big and limit it
double fViewSizeX(aVisibleDiscreteRange.getWidth());
double fViewSizeY(aVisibleDiscreteRange.getHeight());
const double fViewVisibleArea(fViewSizeX * fViewSizeY);
- const double fMaximumVisibleArea(SvtOptionsDrawinglayer::GetQuadratic3DRenderLimit());
+ const double fMaximumVisibleArea(officecfg::Office::Common::Drawinglayer::Quadratic3DRenderLimit::get());
double fReduceFactor(1.0);
if(fViewVisibleArea > fMaximumVisibleArea)
@@ -279,7 +284,7 @@ namespace drawinglayer::primitive2d
// determine the oversample value
static const sal_uInt16 nDefaultOversampleValue(3);
- const sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0);
+ sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0);
geometry::ViewInformation3D aViewInformation3D(getViewInformation3D());
{
@@ -351,15 +356,65 @@ namespace drawinglayer::primitive2d
const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength());
// generate ViewSizes
- const double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength());
- const double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength());
+ double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength());
+ double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength());
// generate RasterWidth and RasterHeight for visible part
- const sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1);
- const sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1);
+ sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1);
+ sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1);
+
+ if(!rViewInformation.getReducedDisplayQuality() && comphelper::LibreOfficeKit::isActive())
+ {
+ // for this purpose allow reduced 3D quality and make a compromise
+ // between quality and speed. This is balanced between those two
+ // targets, fine-tuning/experimenting can be done with the values
+ // below.
+
+ // define some values which allow fine-tuning this feature
+ static const double fMin(80.0);
+ static const double fSqareMin(fMin * fMin);
+ static const double fMax(800.0);
+ static const double fSqareMax(fMax * fMax);
+ static const double fMaxReduction(0.65);
+
+ // get the square pixels (work on pixel numbers to get same
+ // behaviour independent of width/height relations)
+ const double fSquarePixels(nRasterWidth * nRasterHeight);
+
+ if (fSquarePixels > fSqareMin)
+ {
+ // only reduce at all when more than fSqareMin pixels needed
+ double fReduction(fMaxReduction);
+
+ if (fSquarePixels < fSqareMax)
+ {
+ // range between fSqareMin and fSqareMax, calculate a
+ // linear interpolated reduction based on square root
+ fReduction = sqrt(fSquarePixels); // [fMin .. fMax]
+ fReduction = fReduction - fMin; // [0 .. (fMax - fMin)]
+ fReduction = fReduction / (fMax - fMin); // [0 .. 1]
+ fReduction = 1.0 - (fReduction * (1.0 - fMaxReduction)); // [1 .. fMaxReduction]
+
+ // reduce oversampling for this range
+ if(nOversampleValue > 2)
+ nOversampleValue--;
+ }
+ else
+ {
+ // more than fSqareMax pixels, disable oversampling
+ nOversampleValue = 0;
+ }
+
+ // adapt needed values to reduction
+ nRasterWidth = basegfx::fround(fReduction * nRasterWidth);
+ nRasterHeight = basegfx::fround(fReduction * nRasterHeight);
+ fFullViewSizeX *= fReduction;
+ fFullViewSizeY *= fReduction;
+ }
+ }
if(!(nRasterWidth && nRasterHeight))
- return;
+ return new GroupPrimitive2D(std::move(aContainer));
// create view unit buffer
basegfx::BZPixelRaster aBZPixelRaster(
@@ -367,7 +422,7 @@ namespace drawinglayer::primitive2d
nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight);
// check for parallel execution possibilities
- static bool bMultithreadAllowed = false; // loplugin:constvars:ignore
+ static bool bMultithreadAllowed = true; // loplugin:constvars:ignore
sal_Int32 nThreadCount(0);
comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
@@ -455,7 +510,7 @@ namespace drawinglayer::primitive2d
const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight()))
- return;
+ return new GroupPrimitive2D(std::move(aContainer));
// create transform for the created bitmap in discrete coordinates first.
basegfx::B2DHomMatrix aNew2DTransform;
@@ -469,9 +524,9 @@ namespace drawinglayer::primitive2d
aNew2DTransform *= aInverseOToV;
// create bitmap primitive and add
- rContainer.push_back(
+ aContainer.push_back(
new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(maOldRenderedBitmap),
+ maOldRenderedBitmap,
aNew2DTransform));
// test: Allow to add an outline in the debugger when tests are needed
@@ -481,8 +536,9 @@ namespace drawinglayer::primitive2d
{
basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
aOutline.transform(aNew2DTransform);
- rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0)));
+ aContainer.push_back(new PolygonHairlinePrimitive2D(std::move(aOutline), basegfx::BColor(1.0, 0.0, 0.0)));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
Primitive2DContainer ScenePrimitive2D::getGeometry2D() const
@@ -509,8 +565,6 @@ namespace drawinglayer::primitive2d
Primitive2DContainer ScenePrimitive2D::getShadow2D() const
{
- std::unique_lock aGuard( m_aMutex );
-
Primitive2DContainer aRetval;
// create 2D shadows from contained 3D primitives
@@ -525,60 +579,61 @@ namespace drawinglayer::primitive2d
bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
{
- if(!maOldRenderedBitmap.IsEmpty() && !maOldUnitVisiblePart.isEmpty())
- {
- basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
- aInverseSceneTransform.invert();
- const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
-
- if(maOldUnitVisiblePart.isInside(aRelativePoint))
- {
- // calculate coordinates relative to visualized part
- double fDivisorX(maOldUnitVisiblePart.getWidth());
- double fDivisorY(maOldUnitVisiblePart.getHeight());
+ if(maOldRenderedBitmap.IsEmpty() || maOldUnitVisiblePart.isEmpty())
+ return false;
- if(basegfx::fTools::equalZero(fDivisorX))
- {
- fDivisorX = 1.0;
- }
+ basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
- if(basegfx::fTools::equalZero(fDivisorY))
- {
- fDivisorY = 1.0;
- }
+ if(!maOldUnitVisiblePart.isInside(aRelativePoint))
+ return false;
- const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
- const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
+ // calculate coordinates relative to visualized part
+ double fDivisorX(maOldUnitVisiblePart.getWidth());
+ double fDivisorY(maOldUnitVisiblePart.getHeight());
- // combine with real BitmapSizePixel to get bitmap coordinates
- const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
- const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
- const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
+ if(basegfx::fTools::equalZero(fDivisorX))
+ {
+ fDivisorX = 1.0;
+ }
- // try to get a statement about transparency in that pixel
- o_rResult = (0 != maOldRenderedBitmap.GetAlpha(nX, nY));
- return true;
- }
+ if(basegfx::fTools::equalZero(fDivisorY))
+ {
+ fDivisorY = 1.0;
}
- return false;
+ const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
+ const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
+
+ // combine with real BitmapSizePixel to get bitmap coordinates
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
+ const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
+ const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
+
+ // try to get a statement about transparency in that pixel
+ o_rResult = (0 != maOldRenderedBitmap.GetAlpha(nX, nY));
+ return true;
}
ScenePrimitive2D::ScenePrimitive2D(
- const primitive3d::Primitive3DContainer& rxChildren3D,
- const attribute::SdrSceneAttribute& rSdrSceneAttribute,
- const attribute::SdrLightingAttribute& rSdrLightingAttribute,
- const basegfx::B2DHomMatrix& rObjectTransformation,
- const geometry::ViewInformation3D& rViewInformation3D)
- : mxChildren3D(rxChildren3D),
- maSdrSceneAttribute(rSdrSceneAttribute),
- maSdrLightingAttribute(rSdrLightingAttribute),
- maObjectTransformation(rObjectTransformation),
- maViewInformation3D(rViewInformation3D),
+ primitive3d::Primitive3DContainer aChildren3D,
+ attribute::SdrSceneAttribute aSdrSceneAttribute,
+ attribute::SdrLightingAttribute aSdrLightingAttribute,
+ basegfx::B2DHomMatrix aObjectTransformation,
+ geometry::ViewInformation3D aViewInformation3D)
+ : BufferedDecompositionPrimitive2D(),
+ mxChildren3D(std::move(aChildren3D)),
+ maSdrSceneAttribute(std::move(aSdrSceneAttribute)),
+ maSdrLightingAttribute(std::move(aSdrLightingAttribute)),
+ maObjectTransformation(std::move(aObjectTransformation)),
+ maViewInformation3D(std::move(aViewInformation3D)),
mbShadow3DChecked(false),
mfOldDiscreteSizeX(0.0),
mfOldDiscreteSizeY(0.0)
{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(45);
}
bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -626,15 +681,13 @@ namespace drawinglayer::primitive2d
void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard( m_aMutex );
-
// get the involved ranges (see helper method calculateDiscreteSizes for details)
basegfx::B2DRange aDiscreteRange;
basegfx::B2DRange aUnitVisibleRange;
bool bNeedNewDecomposition(false);
bool bDiscreteSizesAreCalculated(false);
- if(!getBuffered2DDecomposition().empty())
+ if(getBuffered2DDecomposition())
{
basegfx::B2DRange aVisibleDiscreteRange;
calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
@@ -662,10 +715,10 @@ namespace drawinglayer::primitive2d
if(bNeedNewDecomposition)
{
// conditions of last local decomposition have changed, delete
- const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(nullptr);
}
- if(getBuffered2DDecomposition().empty())
+ if(!getBuffered2DDecomposition())
{
if(!bDiscreteSizesAreCalculated)
{
@@ -681,7 +734,6 @@ namespace drawinglayer::primitive2d
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
index 5080a92a8cd6..7768c1f6af2b 100644
--- a/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
+++ b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
@@ -84,7 +84,7 @@ namespace drawinglayer::primitive2d
if(bFilled)
{
xReference = new PolyPolygonColorPrimitive2D(
- aScaledOutline,
+ std::move(aScaledOutline),
basegfx::BColor(0.0, 0.0, 0.0));
}
else
@@ -92,13 +92,12 @@ namespace drawinglayer::primitive2d
const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0);
xReference = new PolyPolygonHairlinePrimitive2D(
- aScaledOutline,
+ std::move(aScaledOutline),
aGrayTone);
}
// create HiddenGeometryPrimitive2D
- return Primitive2DReference(
- new HiddenGeometryPrimitive2D(Primitive2DContainer { xReference }));
+ return new HiddenGeometryPrimitive2D(Primitive2DContainer { xReference });
}
} // end of namespace
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
index 6ea066b35754..5de34c5440b6 100644
--- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -22,72 +22,382 @@
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <drawinglayer/converters.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
#include <memory>
+#include <utility>
using namespace com::sun::star;
-
namespace drawinglayer::primitive2d
{
- ShadowPrimitive2D::ShadowPrimitive2D(
- const basegfx::B2DHomMatrix& rShadowTransform,
- const basegfx::BColor& rShadowColor,
- double fShadowBlur,
- Primitive2DContainer&& aChildren)
- : GroupPrimitive2D(std::move(aChildren)),
- maShadowTransform(rShadowTransform),
- maShadowColor(rShadowColor),
- mfShadowBlur(fShadowBlur)
+ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix aShadowTransform,
+ const basegfx::BColor& rShadowColor, double fShadowBlur,
+ Primitive2DContainer&& aChildren)
+ : BufferedDecompositionGroupPrimitive2D(std::move(aChildren))
+ , maShadowTransform(std::move(aShadowTransform))
+ , maShadowColor(rShadowColor)
+ , mfShadowBlur(fShadowBlur)
+ , mfLastDiscreteBlurRadius(0.0)
+ , maLastClippedRange()
+{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(15);
+}
+
+bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive))
+ {
+ const ShadowPrimitive2D& rCompare = static_cast<const ShadowPrimitive2D&>(rPrimitive);
+
+ return (getShadowTransform() == rCompare.getShadowTransform()
+ && getShadowColor() == rCompare.getShadowColor()
+ && getShadowBlur() == rCompare.getShadowBlur());
+ }
+
+ return false;
+}
+
+// Helper to get the to-be-shadowed geometry completely embedded to
+// a ModifiedColorPrimitive2D (change to ShadowColor) and TransformPrimitive2D
+// (direction/offset/transformation of shadow). Since this is used pretty
+// often, pack into a helper
+void ShadowPrimitive2D::getFullyEmbeddedShadowPrimitives(Primitive2DContainer& rContainer) const
+{
+ if (getChildren().empty())
+ return;
+
+ // create a modifiedColorPrimitive containing the shadow color and the content
+ const basegfx::BColorModifierSharedPtr aBColorModifier
+ = std::make_shared<basegfx::BColorModifier_replace>(getShadowColor());
+ const Primitive2DReference xRefA(
+ new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), aBColorModifier));
+ Primitive2DContainer aSequenceB{ xRefA };
+
+ // build transformed primitiveVector with shadow offset and add to target
+ rContainer.visit(new TransformPrimitive2D(getShadowTransform(), std::move(aSequenceB)));
+}
+
+bool ShadowPrimitive2D::prepareValuesAndcheckValidity(
+ basegfx::B2DRange& rBlurRange, basegfx::B2DRange& rClippedRange,
+ basegfx::B2DVector& rDiscreteBlurSize, double& rfDiscreteBlurRadius,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ // no BlurRadius defined, done
+ if (getShadowBlur() <= 0.0)
+ return false;
+
+ // no geometry, done
+ if (getChildren().empty())
+ return false;
+
+ // no pixel target, done
+ if (rViewInformation.getObjectToViewTransformation().isIdentity())
+ return false;
+
+ // get fully embedded ShadowPrimitive
+ Primitive2DContainer aEmbedded;
+ getFullyEmbeddedShadowPrimitives(aEmbedded);
+
+ // get geometry range that defines area that needs to be pixelated
+ rBlurRange = aEmbedded.getB2DRange(rViewInformation);
+
+ // no range of geometry, done
+ if (rBlurRange.isEmpty())
+ return false;
+
+ // extend range by BlurRadius in all directions
+ rBlurRange.grow(getShadowBlur());
+
+ // initialize ClippedRange to full BlurRange -> all is visible
+ rClippedRange = rBlurRange;
+
+ // get Viewport and check if used. If empty, all is visible (see
+ // ViewInformation2D definition in viewinformation2d.hxx)
+ if (!rViewInformation.getViewport().isEmpty())
+ {
+ // if used, extend by BlurRadius to ensure needed parts are included
+ basegfx::B2DRange aVisibleArea(rViewInformation.getViewport());
+ aVisibleArea.grow(getShadowBlur());
+
+ // calculate ClippedRange
+ rClippedRange.intersect(aVisibleArea);
+
+ // if BlurRange is completely outside of VisibleArea, ClippedRange
+ // will be empty and we are done
+ if (rClippedRange.isEmpty())
+ return false;
+ }
+
+ // calculate discrete pixel size of BlurRange. If it's too small to visualize, we are done
+ rDiscreteBlurSize = rViewInformation.getObjectToViewTransformation() * rBlurRange.getRange();
+ if (ceil(rDiscreteBlurSize.getX()) < 2.0 || ceil(rDiscreteBlurSize.getY()) < 2.0)
+ return false;
+
+ // calculate discrete pixel size of BlurRadius. If it's too small to visualize, we are done
+ rfDiscreteBlurRadius = ceil(
+ (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getShadowBlur(), 0))
+ .getLength());
+ if (rfDiscreteBlurRadius < 1.0)
+ return false;
+
+ return true;
+}
+
+void ShadowPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getShadowBlur() <= 0.0)
+ {
+ // Normal (non-blurred) shadow is already completely
+ // handled by get2DDecomposition and not buffered. It
+ // does not need to be since it's a simple embedding
+ // to a ModifiedColorPrimitive2D and TransformPrimitive2D
+ return;
+ }
+
+ // from here on we process a blurred shadow
+ basegfx::B2DRange aBlurRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteBlurSize;
+ double fDiscreteBlurRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aBlurRange, aClippedRange, aDiscreteBlurSize,
+ fDiscreteBlurRadius, rViewInformation))
+ return;
+
+ // Create embedding transformation from object to top-left zero-aligned
+ // target pixel geometry (discrete form of ClippedRange)
+ // First, move to top-left of BlurRange
+ const sal_uInt32 nDiscreteBlurWidth(ceil(aDiscreteBlurSize.getX()));
+ const sal_uInt32 nDiscreteBlurHeight(ceil(aDiscreteBlurSize.getY()));
+ basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+ -aClippedRange.getMinX(), -aClippedRange.getMinY()));
+ // Second, scale to discrete bitmap size
+ // Even when using the offset from ClippedRange, we need to use the
+ // scaling from the full representation, thus from BlurRange
+ aEmbedding.scale(nDiscreteBlurWidth / aBlurRange.getWidth(),
+ nDiscreteBlurHeight / aBlurRange.getHeight());
+
+ // Get fully embedded ShadowPrimitives. This will also embed to
+ // ModifiedColorPrimitive2D (what is not urgently needed) to create
+ // the alpha channel, but a paint with all colors set to a single
+ // one (like shadowColor here) is often less expensive due to possible
+ // simplifications painting the primitives (e.g. gradient)
+ Primitive2DContainer aEmbedded;
+ getFullyEmbeddedShadowPrimitives(aEmbedded);
+
+ // Embed content graphics to TransformPrimitive2D
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(aEmbedding, std::move(aEmbedded)));
+ primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+ // Create BitmapEx using drawinglayer tooling, including a MaximumQuadraticPixel
+ // limitation to be safe and not go runtime/memory havoc. Use a pretty small
+ // limit due to this is Blurred Shadow functionality and will look good with bitmap
+ // scaling anyways. The value of 250.000 square pixels below maybe adapted as needed.
+ const basegfx::B2DVector aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation()
+ * aClippedRange.getRange());
+ const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX()));
+ const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY()));
+ const geometry::ViewInformation2D aViewInformation2D;
+ const sal_uInt32 nMaximumQuadraticPixels(250000);
+
+ // I have now added a helper that just creates the mask without having
+ // to render the content, use it, it's faster
+ const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
+ std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
+ nMaximumQuadraticPixels));
+
+ // if we have no shadow, we are done
+ if (aAlpha.IsEmpty())
+ return;
+
+ const Size& rBitmapExSizePixel(aAlpha.GetSizePixel());
+ if (!(rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 0))
+ return;
+
+ // We may have to take a corrective scaling into account when the
+ // MaximumQuadraticPixel limit was used/triggered
+ double fScale(1.0);
+
+ if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
+ || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
+ {
+ // scale in X and Y should be the same (see fReduceFactor in createAlphaMask),
+ // so adapt numerically to a single scale value, they are integer rounded values
+ const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
+ / static_cast<double>(nDiscreteClippedWidth));
+ const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+ / static_cast<double>(nDiscreteClippedHeight));
+
+ fScale = (fScaleX + fScaleY) * 0.5;
+ }
+
+ // Use the Alpha as base to blur and apply the effect
+ const AlphaMask mask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
+ aAlpha, 0, fDiscreteBlurRadius * fScale, 0, false));
+
+ // The end result is the bitmap filled with blur color and blurred 8-bit alpha mask
+ Bitmap bmp(aAlpha.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ bmp.Erase(Color(getShadowColor()));
+ BitmapEx result(bmp, mask);
+
+#ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
{
+ SvFileStream aNew(sDumpPath + "test_shadowblur.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(result);
}
+ }
+#endif
+
+ // Independent from discrete sizes of blur alpha creation, always
+ // map and project blur result to geometry range extended by blur
+ // radius, but to the eventually clipped instance (ClippedRange)
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new BitmapPrimitive2D(result, basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aClippedRange.getWidth(), aClippedRange.getHeight(),
+ aClippedRange.getMinX(), aClippedRange.getMinY())));
+
+ rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap };
+}
+
+void ShadowPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getShadowBlur() <= 0.0)
+ {
+ // normal (non-blurred) shadow
+ if (getChildren().empty())
+ return;
+
+ // get fully embedded ShadowPrimitives
+ Primitive2DContainer aEmbedded;
+ getFullyEmbeddedShadowPrimitives(aEmbedded);
+
+ rVisitor.visit(aEmbedded);
+ return;
+ }
+
+ // here we have a blurred shadow, check conditions of last
+ // buffered decompose and decide re-use or re-create by using
+ // setBuffered2DDecomposition to reset local buffered version
+ basegfx::B2DRange aBlurRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteBlurSize;
+ double fDiscreteBlurRadius(0.0);
- bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aBlurRange, aClippedRange, aDiscreteBlurSize,
+ fDiscreteBlurRadius, rViewInformation))
+ return;
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // First check is to detect if the last created decompose is capable
+ // to represent the now requested visualization (see similar
+ // implementation at GlowPrimitive2D).
+ if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange))
{
- if(BasePrimitive2D::operator==(rPrimitive))
- {
- const ShadowPrimitive2D& rCompare = static_cast< const ShadowPrimitive2D& >(rPrimitive);
+ basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange);
- return (getShadowTransform() == rCompare.getShadowTransform()
- && getShadowColor() == rCompare.getShadowColor()
- && getShadowBlur() == rCompare.getShadowBlur());
+ if (!rViewInformation.getObjectToViewTransformation().isIdentity())
+ {
+ // Grow by view-dependent size of 1/2 pixel
+ const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(0.5, 0))
+ .getLength());
+ aLastClippedRangeAndHairline.grow(fHalfPixel);
}
- return false;
+ if (!aLastClippedRangeAndHairline.isInside(aClippedRange))
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<ShadowPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
}
+ }
- basegfx::B2DRange ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
- {
- basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
- aRetval.grow(getShadowBlur());
- aRetval.transform(getShadowTransform());
- return aRetval;
- }
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // Second check is to react on changes of the DiscreteSoftRadius when
+ // zooming in/out (see similar implementation at ShadowPrimitive2D).
+ bool bFree(mfLastDiscreteBlurRadius <= 0.0 || fDiscreteBlurRadius <= 0.0);
- void ShadowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ if (!bFree)
{
- if(getChildren().empty())
- return;
-
- // create a modifiedColorPrimitive containing the shadow color and the content
- const basegfx::BColorModifierSharedPtr aBColorModifier =
- std::make_shared<basegfx::BColorModifier_replace>(
- getShadowColor());
- const Primitive2DReference xRefA(
- new ModifiedColorPrimitive2D(
- Primitive2DContainer(getChildren()),
- aBColorModifier));
- Primitive2DContainer aSequenceB { xRefA };
-
- // build transformed primitiveVector with shadow offset and add to target
- rVisitor.append(new TransformPrimitive2D(getShadowTransform(), std::move(aSequenceB)));
+ const double fDiff(fabs(mfLastDiscreteBlurRadius - fDiscreteBlurRadius));
+ const double fLen(fabs(mfLastDiscreteBlurRadius) + fabs(fDiscreteBlurRadius));
+ const double fRelativeChange(fDiff / fLen);
+
+ // Use lower fixed values here to change more often, higher to change less often.
+ // Value is in the range of ]0.0 .. 1.0]
+ bFree = fRelativeChange >= 0.15;
}
- // provide unique ID
- sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const
+ if (bFree)
{
- return PRIMITIVE2D_ID_SHADOWPRIMITIVE2D;
+ // Conditions of last local decomposition have changed, delete
+ const_cast<ShadowPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
}
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // refresh last used DiscreteBlurRadius and ClippedRange to new remembered values
+ const_cast<ShadowPrimitive2D*>(this)->mfLastDiscreteBlurRadius = fDiscreteBlurRadius;
+ const_cast<ShadowPrimitive2D*>(this)->maLastClippedRange = aClippedRange;
+ }
+
+ // call parent, that will check for empty, call create2DDecomposition and
+ // set as decomposition
+ BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+basegfx::B2DRange
+ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily)
+ // use the decompose - what works, but is not needed here.
+ // We know the to-be-visualized geometry and the radius it needs to be extended,
+ // so simply calculate the exact needed range.
+ basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+
+ if (getShadowBlur() > 0.0)
+ {
+ // blurred shadow, that extends the geometry
+ aRetval.grow(getShadowBlur());
+ }
+
+ aRetval.transform(getShadowTransform());
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_SHADOWPRIMITIVE2D; }
} // end of namespace
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
index 98a3bea752eb..e6f92f312f59 100644
--- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -18,20 +18,34 @@
*/
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <drawinglayer/converters.hxx>
+#include "GlowSoftEgdeShadowTools.hxx"
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
namespace drawinglayer::primitive2d
{
SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& aChildren)
- : GroupPrimitive2D(std::move(aChildren))
+ : BufferedDecompositionGroupPrimitive2D(std::move(aChildren))
, mfRadius(fRadius)
+ , mfLastDiscreteSoftRadius(0.0)
+ , maLastClippedRange()
{
+ // activate callback to flush buffered decomposition content
+ setCallbackSeconds(15);
}
bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
- if (GroupPrimitive2D::operator==(rPrimitive))
+ if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive))
{
auto& rCompare = static_cast<const SoftEdgePrimitive2D&>(rPrimitive);
return getRadius() == rCompare.getRadius();
@@ -40,27 +54,300 @@ bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
return false;
}
-void SoftEdgePrimitive2D::get2DDecomposition(
- Primitive2DDecompositionVisitor& rVisitor,
+bool SoftEdgePrimitive2D::prepareValuesAndcheckValidity(
+ basegfx::B2DRange& rSoftRange, basegfx::B2DRange& rClippedRange,
+ basegfx::B2DVector& rDiscreteSoftSize, double& rfDiscreteSoftRadius,
const geometry::ViewInformation2D& rViewInformation) const
{
+ // no SoftRadius defined, done
+ if (getRadius() <= 0.0)
+ return false;
+
+ // no geometry, done
if (getChildren().empty())
+ return false;
+
+ // no pixel target, done
+ if (rViewInformation.getObjectToViewTransformation().isIdentity())
+ return false;
+
+ // get geometry range that defines area that needs to be pixelated
+ rSoftRange = getChildren().getB2DRange(rViewInformation);
+
+ // no range of geometry, done
+ if (rSoftRange.isEmpty())
+ return false;
+
+ // initialize ClippedRange to full SoftRange -> all is visible
+ rClippedRange = rSoftRange;
+
+ // get Viewport and check if used. If empty, all is visible (see
+ // ViewInformation2D definition in viewinformation2d.hxx)
+ if (!rViewInformation.getViewport().isEmpty())
+ {
+ // if used, extend by SoftRadius to ensure needed parts are included
+ // that are not visible, but influence the visible parts
+ basegfx::B2DRange aVisibleArea(rViewInformation.getViewport());
+ aVisibleArea.grow(getRadius() * 2);
+
+ // To do this correctly, it needs to be done in discrete coordinates.
+ // The object may be transformed relative to the original#
+ // ObjectTransformation, e.g. when re-used in shadow
+ aVisibleArea.transform(rViewInformation.getViewTransformation());
+ rClippedRange.transform(rViewInformation.getObjectToViewTransformation());
+
+ // calculate ClippedRange
+ rClippedRange.intersect(aVisibleArea);
+
+ // if SoftRange is completely outside of VisibleArea, ClippedRange
+ // will be empty and we are done
+ if (rClippedRange.isEmpty())
+ return false;
+
+ // convert result back to object coordinates
+ rClippedRange.transform(rViewInformation.getInverseObjectToViewTransformation());
+ }
+
+ // calculate discrete pixel size of SoftRange. If it's too small to visualize, we are done
+ rDiscreteSoftSize = rViewInformation.getObjectToViewTransformation() * rSoftRange.getRange();
+ if (ceil(rDiscreteSoftSize.getX()) < 2.0 || ceil(rDiscreteSoftSize.getY()) < 2.0)
+ return false;
+
+ // calculate discrete pixel size of SoftRadius. If it's too small to visualize, we are done
+ rfDiscreteSoftRadius = ceil(
+ (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getRadius(), 0))
+ .getLength());
+ if (rfDiscreteSoftRadius < 1.0)
+ return false;
+
+ return true;
+}
+
+void SoftEdgePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Use endless while-loop-and-break mechanism due to having multiple
+ // exit scenarios that all have to do the same thing when exiting
+ while (true)
+ {
+ basegfx::B2DRange aSoftRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteSoftSize;
+ double fDiscreteSoftRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aSoftRange, aClippedRange, aDiscreteSoftSize,
+ fDiscreteSoftRadius, rViewInformation))
+ break;
+
+ // Create embedding transformation from object to top-left zero-aligned
+ // target pixel geometry (discrete form of ClippedRange)
+ // First, move to top-left of SoftRange
+ const sal_uInt32 nDiscreteSoftWidth(ceil(aDiscreteSoftSize.getX()));
+ const sal_uInt32 nDiscreteSoftHeight(ceil(aDiscreteSoftSize.getY()));
+ basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+ -aClippedRange.getMinX(), -aClippedRange.getMinY()));
+ // Second, scale to discrete bitmap size
+ // Even when using the offset from ClippedRange, we need to use the
+ // scaling from the full representation, thus from SoftRange
+ aEmbedding.scale(nDiscreteSoftWidth / aSoftRange.getWidth(),
+ nDiscreteSoftHeight / aSoftRange.getHeight());
+
+ // Embed content graphics to TransformPrimitive2D
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(aEmbedding, Primitive2DContainer(getChildren())));
+ primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+ // Create BitmapEx using drawinglayer tooling, including a MaximumQuadraticPixel
+ // limitation to be safe and not go runtime/memory havoc. Use a pretty small
+ // limit due to this is softEdge functionality and will look good with bitmap scaling
+ // anyways. The value of 250.000 square pixels below maybe adapted as needed.
+ const basegfx::B2DVector aDiscreteClippedSize(
+ rViewInformation.getObjectToViewTransformation() * aClippedRange.getRange());
+ const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX()));
+ const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY()));
+ const geometry::ViewInformation2D aViewInformation2D;
+ const sal_uInt32 nMaximumQuadraticPixels(250000);
+ // tdf#156808 force an alpha mask to be created even if it has no alpha
+ // We need an alpha mask, even if it is totally opaque, so that
+ // drawinglayer::primitive2d::ProcessAndBlurAlphaMask() can be called.
+ // Otherwise, blurring of edges will fail in cases like running in a
+ // slideshow or exporting to PDF.
+ const BitmapEx aBitmapEx(::drawinglayer::convertToBitmapEx(
+ std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight,
+ nMaximumQuadraticPixels, true));
+
+ if (aBitmapEx.IsEmpty())
+ break;
+
+ // Get BitmapEx and check size. If no content, we are done
+ const Size& rBitmapExSizePixel(aBitmapEx.GetSizePixel());
+ if (!(rBitmapExSizePixel.Width() > 0 && rBitmapExSizePixel.Height() > 0))
+ break;
+
+ // We may have to take a corrective scaling into account when the
+ // MaximumQuadraticPixel limit was used/triggered
+ double fScale(1.0);
+
+ if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
+ || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
+ {
+ // scale in X and Y should be the same (see fReduceFactor in convertToBitmapEx),
+ // so adapt numerically to a single scale value, they are integer rounded values
+ const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
+ / static_cast<double>(nDiscreteClippedWidth));
+ const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+ / static_cast<double>(nDiscreteClippedHeight));
+
+ fScale = (fScaleX + fScaleY) * 0.5;
+ }
+
+ // Get the Alpha and use as base to blur and apply the effect
+ AlphaMask aMask(aBitmapEx.GetAlphaMask());
+ if (aMask.IsEmpty()) // There is no mask, fully opaque
+ break;
+ AlphaMask blurMask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask(
+ aMask, -fDiscreteSoftRadius * fScale, fDiscreteSoftRadius * fScale, 0));
+ aMask.BlendWith(blurMask);
+
+ // The end result is the original bitmap with blurred 8-bit alpha mask
+ BitmapEx result(aBitmapEx.GetBitmap(), aMask);
+
+#ifdef DBG_UTIL
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_softedge.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(result);
+ }
+ }
+#endif
+
+ // Independent from discrete sizes of soft alpha creation, always
+ // map and project soft result to geometry range extended by soft
+ // radius, but to the eventually clipped instance (ClippedRange)
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new BitmapPrimitive2D(result, basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aClippedRange.getWidth(), aClippedRange.getHeight(),
+ aClippedRange.getMinX(), aClippedRange.getMinY())));
+
+ rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap };
+
+ // we made it, return
return;
+ }
+
+ // creation failed for some of many possible reasons, use original
+ // content, so the unmodified original geometry will be the result,
+ // just without any softEdge effect
+ rContainer = getChildren();
+}
- if (!mbInMaskGeneration)
+void SoftEdgePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Use endless while-loop-and-break mechanism due to having multiple
+ // exit scenarios that all have to do the same thing when exiting
+ while (true)
{
- GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ basegfx::B2DRange aSoftRange;
+ basegfx::B2DRange aClippedRange;
+ basegfx::B2DVector aDiscreteSoftSize;
+ double fDiscreteSoftRadius(0.0);
+
+ // Check various validity details and calculate/prepare values. If false, we are done
+ if (!prepareValuesAndcheckValidity(aSoftRange, aClippedRange, aDiscreteSoftSize,
+ fDiscreteSoftRadius, rViewInformation))
+ break;
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // First check is to detect if the last created decompose is capable
+ // to represent the now requested visualization (see similar
+ // implementation at GlowPrimitive2D).
+ if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange))
+ {
+ basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange);
+
+ if (!rViewInformation.getObjectToViewTransformation().isIdentity())
+ {
+ // Grow by view-dependent size of 1/2 pixel
+ const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(0.5, 0))
+ .getLength());
+ aLastClippedRangeAndHairline.grow(fHalfPixel);
+ }
+
+ if (!aLastClippedRangeAndHairline.isInside(aClippedRange))
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<SoftEdgePrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+ }
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ // Second check is to react on changes of the DiscreteSoftRadius when
+ // zooming in/out (see similar implementation at GlowPrimitive2D).
+ bool bFree(mfLastDiscreteSoftRadius <= 0.0 || fDiscreteSoftRadius <= 0.0);
+
+ if (!bFree)
+ {
+ const double fDiff(fabs(mfLastDiscreteSoftRadius - fDiscreteSoftRadius));
+ const double fLen(fabs(mfLastDiscreteSoftRadius) + fabs(fDiscreteSoftRadius));
+ const double fRelativeChange(fDiff / fLen);
+
+ // Use a lower value here, soft edge keeps it's content so avoid that it gets too
+ // unsharp in the pixel visualization
+ // Value is in the range of ]0.0 .. 1.0]
+ bFree = fRelativeChange >= 0.075;
+ }
+
+ if (bFree)
+ {
+ // Conditions of last local decomposition have changed, delete
+ const_cast<SoftEdgePrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // refresh last used DiscreteSoftRadius and ClippedRange to new remembered values
+ const_cast<SoftEdgePrimitive2D*>(this)->mfLastDiscreteSoftRadius = fDiscreteSoftRadius;
+ const_cast<SoftEdgePrimitive2D*>(this)->maLastClippedRange = aClippedRange;
+ }
+
+ // call parent, that will check for empty, call create2DDecomposition and
+ // set as decomposition
+ BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+
+ // we made it, return
return;
}
- // create a modifiedColorPrimitive containing the *black* color and the content. Using black
- // on white allows creating useful mask in VclPixelProcessor2D::processSoftEdgePrimitive2D.
- basegfx::BColorModifierSharedPtr aBColorModifier
- = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor());
+ // No soft edge needed for some of many possible reasons, use original content
+ rVisitor.visit(getChildren());
+}
- const Primitive2DReference xRef(
- new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), aBColorModifier));
- rVisitor.append(xRef);
+basegfx::B2DRange
+SoftEdgePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily)
+ // use the decompose - what works, but is not needed here.
+ // We know the to-be-visualized geometry and the radius it needs to be extended,
+ // so simply calculate the exact needed range.
+ return getChildren().getB2DRange(rViewInformation);
}
sal_uInt32 SoftEdgePrimitive2D::getPrimitive2DID() const
diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
index c82b0088e29d..783a54a4c409 100644
--- a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
@@ -30,12 +30,21 @@ namespace drawinglayer::primitive2d
const vcl::PDFWriter::StructElement& rStructureElement,
bool bBackground,
bool bIsImage,
- Primitive2DContainer&& aChildren)
+ bool bIsDecorative,
+ Primitive2DContainer&& aChildren,
+ void const*const pAnchorStructureElementKey,
+ ::std::vector<sal_Int32> const*const pAnnotIds)
: GroupPrimitive2D(std::move(aChildren)),
maStructureElement(rStructureElement),
mbBackground(bBackground),
- mbIsImage(bIsImage)
+ mbIsImage(bIsImage),
+ mbIsDecorative(bIsDecorative)
+ , m_pAnchorStructureElementKey(pAnchorStructureElementKey)
{
+ if (pAnnotIds)
+ {
+ m_AnnotIds = *pAnnotIds;
+ }
}
bool StructureTagPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -57,6 +66,13 @@ namespace drawinglayer::primitive2d
return PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D;
}
+ bool StructureTagPrimitive2D::isTaggedSdrObject() const
+ {
+ // note at the moment *all* StructureTagPrimitive2D are created for
+ // SdrObjects - if that ever changes, need another condition here
+ return !isBackground() || isImage();
+ }
+
} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
index 503d07688ecd..24f979ce2c9d 100644
--- a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
@@ -28,8 +28,10 @@
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <cmath>
+#include <utility>
#include <vcl/skia/SkiaHelper.hxx>
using namespace com::sun::star;
@@ -61,7 +63,7 @@ namespace
namespace drawinglayer::primitive2d
{
- void SvgGradientHelper::createSingleGradientEntryFill(Primitive2DContainer& rContainer) const
+ Primitive2DReference SvgGradientHelper::createSingleGradientEntryFill() const
{
const SvgGradientEntryVector& rEntries = getGradientEntries();
const sal_uInt32 nCount(rEntries.size());
@@ -82,19 +84,20 @@ namespace drawinglayer::primitive2d
{
Primitive2DContainer aContent { xRef };
- xRef = Primitive2DReference(
+ xRef =
new UnifiedTransparencePrimitive2D(
std::move(aContent),
- 1.0 - fOpacity));
+ 1.0 - fOpacity);
}
- rContainer.push_back(xRef);
+ return xRef;
}
}
else
{
OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
}
+ return nullptr;
}
void SvgGradientHelper::checkPreconditions()
@@ -303,18 +306,17 @@ namespace drawinglayer::primitive2d
}
}
- void SvgGradientHelper::createResult(
- Primitive2DContainer& rContainer,
- const Primitive2DContainer& rTargetColor,
- const Primitive2DContainer& rTargetOpacity,
+ Primitive2DReference SvgGradientHelper::createResult(
+ Primitive2DContainer aTargetColor,
+ Primitive2DContainer aTargetOpacity,
const basegfx::B2DHomMatrix& rUnitGradientToObject,
bool bInvert) const
{
- Primitive2DContainer aTargetColorEntries(rTargetColor.maybeInvert(bInvert));
- Primitive2DContainer aTargetOpacityEntries(rTargetOpacity.maybeInvert(bInvert));
+ Primitive2DContainer aTargetColorEntries(aTargetColor.maybeInvert(bInvert));
+ Primitive2DContainer aTargetOpacityEntries(aTargetOpacity.maybeInvert(bInvert));
if(aTargetColorEntries.empty())
- return;
+ return nullptr;
Primitive2DReference xRefContent;
@@ -335,20 +337,20 @@ namespace drawinglayer::primitive2d
std::move(aTargetColorEntries));
}
- rContainer.push_back(new MaskPrimitive2D(
+ return new MaskPrimitive2D(
getPolyPolygon(),
- Primitive2DContainer { xRefContent }));
+ Primitive2DContainer { xRefContent });
}
SvgGradientHelper::SvgGradientHelper(
- const basegfx::B2DHomMatrix& rGradientTransform,
- const basegfx::B2DPolyPolygon& rPolyPolygon,
+ basegfx::B2DHomMatrix aGradientTransform,
+ basegfx::B2DPolyPolygon aPolyPolygon,
SvgGradientEntryVector&& rGradientEntries,
const basegfx::B2DPoint& rStart,
bool bUseUnitCoordinates,
SpreadMethod aSpreadMethod)
- : maGradientTransform(rGradientTransform),
- maPolyPolygon(rPolyPolygon),
+ : maGradientTransform(std::move(aGradientTransform)),
+ maPolyPolygon(std::move(aPolyPolygon)),
maGradientEntries(std::move(rGradientEntries)),
maStart(rStart),
maSpreadMethod(aSpreadMethod),
@@ -464,7 +466,7 @@ namespace drawinglayer::primitive2d
}
}
- void SvgLinearGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if(!getPreconditionsChecked())
{
@@ -474,7 +476,7 @@ namespace drawinglayer::primitive2d
if(getSingleEntry())
{
// fill with last existing color
- createSingleGradientEntryFill(rContainer);
+ return createSingleGradientEntryFill();
}
else if(getCreatesContent())
{
@@ -537,7 +539,7 @@ namespace drawinglayer::primitive2d
Primitive2DContainer aTargetColor;
Primitive2DContainer aTargetOpacity;
- if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0))
+ if(aUnitRange.getWidth() > 0.0)
{
// add a pre-multiply to aUnitGradientToObject to allow
// multiplication of the polygon(xl, 0.0, xr, 1.0)
@@ -554,8 +556,9 @@ namespace drawinglayer::primitive2d
aUnitRange.getMaxX());
}
- createResult(rContainer, aTargetColor, aTargetOpacity, aUnitGradientToObject);
+ return createResult(std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject);
}
+ return nullptr;
}
SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
@@ -686,7 +689,7 @@ namespace drawinglayer::primitive2d
}
}
- void SvgRadialGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if(!getPreconditionsChecked())
{
@@ -696,7 +699,7 @@ namespace drawinglayer::primitive2d
if(getSingleEntry())
{
// fill with last existing color
- createSingleGradientEntryFill(rContainer);
+ return createSingleGradientEntryFill();
}
else if(getCreatesContent())
{
@@ -780,8 +783,9 @@ namespace drawinglayer::primitive2d
fMax);
}
- createResult(rContainer, aTargetColor, aTargetOpacity, aUnitGradientToObject, true);
+ return createResult(std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject, true);
}
+ return nullptr;
}
SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
@@ -816,22 +820,22 @@ namespace drawinglayer::primitive2d
{
const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
- if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
- {
- const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
+ if(!pSvgGradientHelper || !SvgGradientHelper::operator==(*pSvgGradientHelper))
+ return false;
+
+ const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
- if(getRadius() == rCompare.getRadius())
+ if(getRadius() == rCompare.getRadius())
+ {
+ if(isFocalSet() == rCompare.isFocalSet())
{
- if(isFocalSet() == rCompare.isFocalSet())
+ if(isFocalSet())
+ {
+ return getFocal() == rCompare.getFocal();
+ }
+ else
{
- if(isFocalSet())
- {
- return getFocal() == rCompare.getFocal();
- }
- else
- {
- return true;
- }
+ return true;
}
}
}
@@ -858,12 +862,12 @@ namespace drawinglayer::primitive2d
namespace drawinglayer::primitive2d
{
- void SvgLinearAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const double fDelta(getOffsetB() - getOffsetA());
if(basegfx::fTools::equalZero(fDelta))
- return;
+ return nullptr;
// use one discrete unit for overlap (one pixel)
const double fDiscreteUnit(getDiscreteUnit());
@@ -897,15 +901,18 @@ namespace drawinglayer::primitive2d
double fUnitScale(0.0);
const double fUnitStep(1.0 / nSteps);
+ Primitive2DContainer aContainer;
+ aContainer.resize(nSteps);
for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
{
basegfx::B2DPolygon aNew(aPolygon);
aNew.transform(basegfx::utils::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
- rContainer.push_back(new PolyPolygonColorPrimitive2D(
+ aContainer[a] = new PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(aNew),
- basegfx::interpolate(getColorA(), getColorB(), fUnitScale)));
+ basegfx::interpolate(getColorA(), getColorB(), fUnitScale));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
@@ -951,12 +958,12 @@ namespace drawinglayer::primitive2d
namespace drawinglayer::primitive2d
{
- void SvgRadialAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
const double fDeltaScale(getScaleB() - getScaleA());
if(basegfx::fTools::equalZero(fDeltaScale))
- return;
+ return nullptr;
// use one discrete unit for overlap (one pixel)
const double fDiscreteUnit(getDiscreteUnit());
@@ -968,6 +975,8 @@ namespace drawinglayer::primitive2d
double fUnitScale(0.0);
const double fUnitStep(1.0 / nSteps);
+ Primitive2DContainer aContainer;
+ aContainer.resize(nSteps);
for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
{
basegfx::B2DHomMatrix aTransform;
@@ -997,10 +1006,11 @@ namespace drawinglayer::primitive2d
basegfx::B2DPolygon aNew(basegfx::utils::createPolygonFromUnitCircle());
aNew.transform(aTransform);
- rContainer.push_back(new PolyPolygonColorPrimitive2D(
+ aContainer[a] = new PolyPolygonColorPrimitive2D(
basegfx::B2DPolyPolygon(aNew),
- basegfx::interpolate(getColorB(), getColorA(), fUnitScale)));
+ basegfx::interpolate(getColorB(), getColorA(), fUnitScale));
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
@@ -1060,24 +1070,24 @@ namespace drawinglayer::primitive2d
bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
- if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
- {
- const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive);
+ if(!DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ return false;
- if(getColorA() == rCompare.getColorA()
- && getColorB() == rCompare.getColorB()
- && getScaleA() == rCompare.getScaleA()
- && getScaleB() == rCompare.getScaleB())
+ const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive);
+
+ if(getColorA() == rCompare.getColorA()
+ && getColorB() == rCompare.getColorB()
+ && getScaleA() == rCompare.getScaleA()
+ && getScaleB() == rCompare.getScaleB())
+ {
+ if(isTranslateSet() && rCompare.isTranslateSet())
{
- if(isTranslateSet() && rCompare.isTranslateSet())
- {
- return (getTranslateA() == rCompare.getTranslateA()
- && getTranslateB() == rCompare.getTranslateB());
- }
- else if(!isTranslateSet() && !rCompare.isTranslateSet())
- {
- return true;
- }
+ return (getTranslateA() == rCompare.getTranslateA()
+ && getTranslateB() == rCompare.getTranslateB());
+ }
+ else if(!isTranslateSet() && !rCompare.isTranslateSet())
+ {
+ return true;
}
}
diff --git a/drawinglayer/source/primitive2d/textbreakuphelper.cxx b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
index 9c4424b8d01e..8f92d9817a0e 100644
--- a/drawinglayer/source/primitive2d/textbreakuphelper.cxx
+++ b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
@@ -58,6 +58,7 @@ namespace drawinglayer::primitive2d
// prepare values for new portion
basegfx::B2DHomMatrix aNewTransform;
std::vector< double > aNewDXArray;
+ std::vector< sal_Bool > aNewKashidaArray;
const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition());
if(!mbNoDXArray)
@@ -68,6 +69,13 @@ namespace drawinglayer::primitive2d
mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition()));
}
+ if(!mbNoDXArray && !mrSource.getKashidaArray().empty())
+ {
+ aNewKashidaArray = std::vector< sal_Bool >(
+ mrSource.getKashidaArray().begin() + (nIndex - mrSource.getTextPosition()),
+ mrSource.getKashidaArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition()));
+ }
+
if(bNewStartIsNotOldStart)
{
// needs to be moved to a new start position
@@ -137,6 +145,7 @@ namespace drawinglayer::primitive2d
nIndex,
nLength,
std::move(aNewDXArray),
+ std::move(aNewKashidaArray),
mrSource.getFontAttribute(),
mrSource.getLocale(),
mrSource.getFontColor(),
@@ -168,6 +177,7 @@ namespace drawinglayer::primitive2d
nIndex,
nLength,
std::move(aNewDXArray),
+ std::move(aNewKashidaArray),
mrSource.getFontAttribute(),
mrSource.getLocale(),
mrSource.getFontColor()));
@@ -261,14 +271,14 @@ namespace drawinglayer::primitive2d
mxResult = aTempResult;
}
- const Primitive2DContainer& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const
+ Primitive2DContainer TextBreakupHelper::extractResult(BreakupUnit aBreakupUnit)
{
if(mxResult.empty())
{
- const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit);
+ breakup(aBreakupUnit);
}
- return mxResult;
+ return std::move(mxResult);
}
} // end of namespace
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
index 10cf07b4a8c0..07181bbf2a17 100644
--- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -30,26 +30,41 @@
namespace drawinglayer::primitive2d
{
void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
- std::vector< Primitive2DReference >& rTarget,
+ Primitive2DContainer& rTarget,
basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans,
const OUString& rText,
sal_Int32 nTextPosition,
sal_Int32 nTextLength,
const std::vector< double >& rDXArray,
+ const std::vector< sal_Bool >& rKashidaArray,
const attribute::FontAttribute& rFontAttribute) const
{
// create the SimpleTextPrimitive needed in any case
- rTarget.push_back(Primitive2DReference(
+ rTarget.push_back(
new TextSimplePortionPrimitive2D(
rDecTrans.getB2DHomMatrix(),
rText,
nTextPosition,
nTextLength,
std::vector(rDXArray),
+ std::vector(rKashidaArray),
rFontAttribute,
getLocale(),
- getFontColor())));
+ getFontColor()));
+ CreateDecorationGeometryContent(rTarget, rDecTrans, rText,
+ nTextPosition, nTextLength,
+ rDXArray);
+ }
+
+ void TextDecoratedPortionPrimitive2D::CreateDecorationGeometryContent(
+ Primitive2DContainer& rTarget,
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans,
+ const OUString& rText,
+ sal_Int32 nTextPosition,
+ sal_Int32 nTextLength,
+ const std::vector< double >& rDXArray) const
+ {
// see if something else needs to be done
const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
@@ -92,27 +107,27 @@ namespace drawinglayer::primitive2d
if(bOverlineUsed)
{
// create primitive geometry for overline
- rTarget.push_back(Primitive2DReference(
+ rTarget.push_back(
new TextLinePrimitive2D(
rDecTrans.getB2DHomMatrix(),
fTextWidth,
aTextLayouter.getOverlineOffset(),
aTextLayouter.getOverlineHeight(),
getFontOverline(),
- getOverlineColor())));
+ getOverlineColor()));
}
if(bUnderlineUsed)
{
// create primitive geometry for underline
- rTarget.push_back(Primitive2DReference(
+ rTarget.push_back(
new TextLinePrimitive2D(
rDecTrans.getB2DHomMatrix(),
fTextWidth,
aTextLayouter.getUnderlineOffset(),
aTextLayouter.getUnderlineHeight(),
getFontUnderline(),
- getTextlineColor())));
+ getTextlineColor()));
}
if(!bStrikeoutUsed)
@@ -124,45 +139,44 @@ namespace drawinglayer::primitive2d
// strikeout with character
const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
- rTarget.push_back(Primitive2DReference(
+ rTarget.push_back(
new TextCharacterStrikeoutPrimitive2D(
rDecTrans.getB2DHomMatrix(),
fTextWidth,
getFontColor(),
aStrikeoutChar,
getFontAttribute(),
- getLocale())));
+ getLocale()));
}
else
{
// strikeout with geometry
- rTarget.push_back(Primitive2DReference(
+ rTarget.push_back(
new TextGeometryStrikeoutPrimitive2D(
rDecTrans.getB2DHomMatrix(),
fTextWidth,
getFontColor(),
aTextLayouter.getUnderlineHeight(),
aTextLayouter.getStrikeoutOffset(),
- getTextStrikeout())));
+ getTextStrikeout()));
}
// TODO: Handle Font Emphasis Above/Below
}
- void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if(getWordLineMode())
{
// support for single word mode; split to single word primitives
// using TextBreakupHelper
- const TextBreakupHelper aTextBreakupHelper(*this);
- const Primitive2DContainer& aBroken(aTextBreakupHelper.getResult(BreakupUnit::Word));
+ TextBreakupHelper aTextBreakupHelper(*this);
+ Primitive2DContainer aBroken(aTextBreakupHelper.extractResult(BreakupUnit::Word));
if(!aBroken.empty())
{
// was indeed split to several words, use as result
- rContainer.insert(rContainer.end(), aBroken.begin(), aBroken.end());
- return;
+ return new GroupPrimitive2D(std::move(aBroken));
}
else
{
@@ -170,7 +184,6 @@ namespace drawinglayer::primitive2d
// decompose local entity
}
}
- std::vector< Primitive2DReference > aNewPrimitives;
basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
Primitive2DContainer aRetval;
@@ -190,115 +203,113 @@ namespace drawinglayer::primitive2d
getFontAttribute().getBiDiStrong());
// handle as one word
- impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
+ impCreateGeometryContent(aRetval, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), getKashidaArray(), aNewFontAttribute);
+
+ // Handle Shadow, Outline and TextRelief
+ if(aRetval.empty())
+ return nullptr;
- // convert to Primitive2DSequence
- const sal_uInt32 nMemberCount(aNewPrimitives.size());
+ // outline AND shadow depend on NO TextRelief (see dialog)
+ const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
+ const bool bHasShadow(!bHasTextRelief && getShadow());
+ const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
- if(nMemberCount)
+ if(bHasShadow || bHasTextRelief || bHasOutline)
{
- aRetval.resize(nMemberCount);
+ Primitive2DReference aShadow;
- for(sal_uInt32 a(0); a < nMemberCount; a++)
+ if(bHasShadow)
{
- aRetval[a] = aNewPrimitives[a];
+ // create shadow with current content (in aRetval). Text shadow
+ // is constant, relative to font size, rotated with the text and has a
+ // constant color.
+ // shadow parameter values
+ static const double fFactor(1.0 / 24.0);
+ const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
+ static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
+
+ // prepare shadow transform matrix
+ const basegfx::B2DHomMatrix aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ fTextShadowOffset, fTextShadowOffset));
+
+ // create shadow primitive
+ aShadow = new ShadowPrimitive2D(
+ aShadowTransform,
+ aShadowColor,
+ 0, // fShadowBlur = 0, there's no blur for text shadow yet.
+ Primitive2DContainer(aRetval));
}
- }
-
- // Handle Shadow, Outline and TextRelief
- if(!aRetval.empty())
- {
- // outline AND shadow depend on NO TextRelief (see dialog)
- const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
- const bool bHasShadow(!bHasTextRelief && getShadow());
- const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
- if(bHasShadow || bHasTextRelief || bHasOutline)
+ if(bHasTextRelief)
{
- Primitive2DReference aShadow;
+ // create emboss using an own helper primitive since this will
+ // be view-dependent
+ const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
+ const bool bDefaultTextColor(aBBlack == getFontColor());
+ TextEffectStyle2D aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed);
- if(bHasShadow)
+ if(bDefaultTextColor)
{
- // create shadow with current content (in aRetval). Text shadow
- // is constant, relative to font size, rotated with the text and has a
- // constant color.
- // shadow parameter values
- static const double fFactor(1.0 / 24.0);
- const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
- static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
-
- // prepare shadow transform matrix
- const basegfx::B2DHomMatrix aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix(
- fTextShadowOffset, fTextShadowOffset));
-
- // create shadow primitive
- aShadow = new ShadowPrimitive2D(
- aShadowTransform,
- aShadowColor,
- 0, // fShadowBlur = 0, there's no blur for text shadow yet.
- Primitive2DContainer(aRetval));
- }
-
- if(bHasTextRelief)
- {
- // create emboss using an own helper primitive since this will
- // be view-dependent
- const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
- const bool bDefaultTextColor(aBBlack == getFontColor());
- TextEffectStyle2D aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed);
-
- if(bDefaultTextColor)
+ if(TEXT_RELIEF_ENGRAVED == getTextRelief())
{
- if(TEXT_RELIEF_ENGRAVED == getTextRelief())
- {
- aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault;
- }
- else
- {
- aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
- }
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault;
}
else
{
- if(TEXT_RELIEF_ENGRAVED == getTextRelief())
- {
- aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved;
- }
- else
- {
- aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed;
- }
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
}
- Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
- std::move(aRetval),
- aDecTrans.getTranslate(),
- aDecTrans.getRotate(),
- aTextEffectStyle2D));
- aRetval = Primitive2DContainer { aNewTextEffect };
+ aRetval = Primitive2DContainer {
+ new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ aTextEffectStyle2D)
+ };
}
- else if(bHasOutline)
+ else
{
// create outline using an own helper primitive since this will
// be view-dependent
- Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ aRetval = Primitive2DContainer {
+ new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ TextEffectStyle2D::Outline)
+ };
+ }
+
+ aRetval = Primitive2DContainer {
+ Primitive2DReference(new TextEffectPrimitive2D(
std::move(aRetval),
aDecTrans.getTranslate(),
aDecTrans.getRotate(),
- TextEffectStyle2D::Outline));
- aRetval = Primitive2DContainer { aNewTextEffect };
- }
+ aTextEffectStyle2D))
+ };
+ }
+ else if(bHasOutline)
+ {
+ // create outline using an own helper primitive since this will
+ // be view-dependent
+ aRetval = Primitive2DContainer {
+ Primitive2DReference(new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ TextEffectStyle2D::Outline))
+ };
+ }
- if(aShadow.is())
- {
- // put shadow in front if there is one to paint timely before
- // but placed behind content
- aRetval.insert(aRetval.begin(), aShadow);
- }
+ if(aShadow.is())
+ {
+ // put shadow in front if there is one to paint timely before
+ // but placed behind content
+ aRetval.insert(aRetval.begin(), aShadow);
}
}
- rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
+ return new GroupPrimitive2D(std::move(aRetval));
}
TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
@@ -308,6 +319,7 @@ namespace drawinglayer::primitive2d
sal_Int32 nTextPosition,
sal_Int32 nTextLength,
std::vector< double >&& rDXArray,
+ std::vector< sal_Bool >&& rKashidaArray,
const attribute::FontAttribute& rFontAttribute,
const css::lang::Locale& rLocale,
const basegfx::BColor& rFontColor,
@@ -326,7 +338,7 @@ namespace drawinglayer::primitive2d
bool bEmphasisMarkBelow,
TextRelief eTextRelief,
bool bShadow)
- : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, std::move(rDXArray), rFontAttribute, rLocale, rFontColor, false, 0, rFillColor),
+ : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, std::move(rDXArray), std::move(rKashidaArray), rFontAttribute, rLocale, rFontColor, false, 0, rFillColor),
maOverlineColor(rOverlineColor),
maTextlineColor(rTextlineColor),
meFontOverline(eFontOverline),
diff --git a/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
index d05e727ce50c..bd123d34315d 100644
--- a/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
@@ -28,8 +28,8 @@ namespace drawinglayer::primitive2d
{
const double fDiscreteSize(1.1);
-void TextEffectPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+Primitive2DReference TextEffectPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& rViewInformation) const
{
// get the distance of one discrete units from target display. Use between 1.0 and sqrt(2) to
// have good results on rotated objects, too
@@ -37,10 +37,10 @@ void TextEffectPrimitive2D::create2DDecomposition(
* basegfx::B2DVector(fDiscreteSize, fDiscreteSize));
const basegfx::B2DVector aDiagonalDistance(aDistance * (1.0 / 1.44));
+ Primitive2DContainer aContainer;
switch (getTextEffectStyle2D())
{
case TextEffectStyle2D::ReliefEmbossed:
- case TextEffectStyle2D::ReliefEngraved:
case TextEffectStyle2D::ReliefEmbossedDefault:
case TextEffectStyle2D::ReliefEngravedDefault:
{
@@ -84,14 +84,14 @@ void TextEffectPrimitive2D::create2DDecomposition(
const Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D(
Primitive2DContainer(getTextContent()), aBColorModifierToGray));
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor }));
// add original, too
const basegfx::BColorModifierSharedPtr aBColorModifierToWhite
= std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1.0));
- rContainer.push_back(new ModifiedColorPrimitive2D(
+ aContainer.push_back(new ModifiedColorPrimitive2D(
Primitive2DContainer(getTextContent()), aBColorModifierToWhite));
}
else
@@ -103,11 +103,11 @@ void TextEffectPrimitive2D::create2DDecomposition(
const Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D(
Primitive2DContainer(getTextContent()), aBColorModifierToGray));
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor }));
// add original, too
- rContainer.push_back(new GroupPrimitive2D(Primitive2DContainer(getTextContent())));
+ aContainer.push_back(new GroupPrimitive2D(Primitive2DContainer(getTextContent())));
}
break;
@@ -119,53 +119,54 @@ void TextEffectPrimitive2D::create2DDecomposition(
aTransform.set(0, 2, aDistance.getX());
aTransform.set(1, 2, 0.0);
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, aDiagonalDistance.getX());
aTransform.set(1, 2, aDiagonalDistance.getY());
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, 0.0);
aTransform.set(1, 2, aDistance.getY());
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, -aDiagonalDistance.getX());
aTransform.set(1, 2, aDiagonalDistance.getY());
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, -aDistance.getX());
aTransform.set(1, 2, 0.0);
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, -aDiagonalDistance.getX());
aTransform.set(1, 2, -aDiagonalDistance.getY());
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, 0.0);
aTransform.set(1, 2, -aDistance.getY());
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
aTransform.set(0, 2, aDiagonalDistance.getX());
aTransform.set(1, 2, -aDiagonalDistance.getY());
- rContainer.push_back(
+ aContainer.push_back(
new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
// at last, place original over it, but force to white
const basegfx::BColorModifierSharedPtr aBColorModifierToWhite
= std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1.0, 1.0, 1.0));
- rContainer.push_back(new ModifiedColorPrimitive2D(
+ aContainer.push_back(new ModifiedColorPrimitive2D(
Primitive2DContainer(getTextContent()), aBColorModifierToWhite));
break;
}
}
+ return new GroupPrimitive2D(std::move(aContainer));
}
TextEffectPrimitive2D::TextEffectPrimitive2D(Primitive2DContainer&& rTextContent,
@@ -213,19 +214,16 @@ void TextEffectPrimitive2D::get2DDecomposition(
Primitive2DDecompositionVisitor& rVisitor,
const geometry::ViewInformation2D& rViewInformation) const
{
- std::unique_lock aGuard(m_aMutex);
-
- if (!getBuffered2DDecomposition().empty())
+ if (getBuffered2DDecomposition())
{
if (maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
{
// conditions of last local decomposition have changed, delete
- const_cast<TextEffectPrimitive2D*>(this)->setBuffered2DDecomposition(
- Primitive2DContainer());
+ const_cast<TextEffectPrimitive2D*>(this)->setBuffered2DDecomposition(nullptr);
}
}
- if (getBuffered2DDecomposition().empty())
+ if (!getBuffered2DDecomposition())
{
// remember ViewRange and ViewTransformation
const_cast<TextEffectPrimitive2D*>(this)->maLastObjectToViewTransformation
@@ -233,7 +231,6 @@ void TextEffectPrimitive2D::get2DDecomposition(
}
// use parent implementation
- aGuard.unlock();
BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
index c7af9562adc1..655918904cfb 100644
--- a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
@@ -136,9 +136,18 @@ namespace drawinglayer::primitive2d
}
- TextHierarchyEditPrimitive2D::TextHierarchyEditPrimitive2D(Primitive2DContainer&& aChildren)
- : GroupPrimitive2D(std::move(aChildren))
+ TextHierarchyEditPrimitive2D::TextHierarchyEditPrimitive2D(Primitive2DContainer&& aContent)
+ : GroupPrimitive2D(std::move(aContent))
+ {
+ }
+
+ void TextHierarchyEditPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
{
+ // check if TextEdit is active. If not, process. If yes, suppress the content
+ if (!rViewInformation.getTextEditActive())
+ GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
}
// provide unique ID
diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
index 60370e722941..3daecb4bec62 100644
--- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx
+++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
@@ -22,6 +22,8 @@
#include <algorithm>
#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/attribute/fontattribute.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <comphelper/processfactory.hxx>
@@ -29,6 +31,7 @@
#include <osl/diagnose.h>
#include <tools/gen.hxx>
#include <vcl/canvastools.hxx>
+#include <vcl/kernarray.hxx>
#include <vcl/timer.hxx>
#include <vcl/virdev.hxx>
#include <vcl/font.hxx>
@@ -161,62 +164,84 @@ TextLayouterDevice::TextLayouterDevice()
TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE { releaseGlobalVirtualDevice(); }
-void TextLayouterDevice::setFont(const vcl::Font& rFont) { mrDevice.SetFont(rFont); }
+void TextLayouterDevice::setFont(const vcl::Font& rFont)
+{
+ mrDevice.SetFont(rFont);
+ mnFontScalingFixX = 1.0;
+ mnFontScalingFixY = 1.0;
+}
void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontAttribute,
double fFontScaleX, double fFontScaleY,
const css::lang::Locale& rLocale)
{
- setFont(getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale));
+ vcl::Font aFont
+ = getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale);
+ setFont(aFont);
+ Size aFontSize = aFont.GetFontSize();
+ if (aFontSize.Height())
+ {
+ mnFontScalingFixY = fFontScaleY / aFontSize.Height();
+ // aFontSize.Width() is 0 for uninformly scaled fonts: see getVclFontFromFontAttribute
+ mnFontScalingFixX
+ = fFontScaleX / (aFontSize.Width() ? aFontSize.Width() : aFontSize.Height());
+ }
+ else
+ {
+ mnFontScalingFixX = mnFontScalingFixY = 1.0;
+ }
}
double TextLayouterDevice::getOverlineOffset() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
double fRet = (rMetric.GetInternalLeading() / 2.0) - rMetric.GetAscent();
- return fRet;
+ return fRet * mnFontScalingFixY;
}
double TextLayouterDevice::getUnderlineOffset() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
double fRet = rMetric.GetDescent() / 2.0;
- return fRet;
+ return fRet * mnFontScalingFixY;
}
double TextLayouterDevice::getStrikeoutOffset() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
double fRet = (rMetric.GetAscent() - rMetric.GetInternalLeading()) / 3.0;
- return fRet;
+ return fRet * mnFontScalingFixY;
}
double TextLayouterDevice::getOverlineHeight() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
double fRet = rMetric.GetInternalLeading() / 2.5;
- return fRet;
+ return fRet * mnFontScalingFixY;
}
double TextLayouterDevice::getUnderlineHeight() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
double fRet = rMetric.GetDescent() / 4.0;
- return fRet;
+ return fRet * mnFontScalingFixY;
}
-double TextLayouterDevice::getTextHeight() const { return mrDevice.GetTextHeight(); }
+double TextLayouterDevice::getTextHeight() const
+{
+ return mrDevice.GetTextHeightDouble() * mnFontScalingFixY;
+}
double TextLayouterDevice::getTextWidth(const OUString& rText, sal_uInt32 nIndex,
sal_uInt32 nLength) const
{
- return mrDevice.GetTextWidth(rText, nIndex, nLength);
+ return mrDevice.GetTextWidthDouble(rText, nIndex, nLength) * mnFontScalingFixX;
}
void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
const OUString& rText, sal_uInt32 nIndex,
- sal_uInt32 nLength,
- const std::vector<double>& rDXArray) const
+ sal_uInt32 nLength, const std::vector<double>& rDXArray,
+ const std::vector<sal_Bool>& rKashidaArray) const
{
const sal_uInt32 nDXArrayCount(rDXArray.size());
sal_uInt32 nTextLength(nLength);
@@ -231,20 +256,26 @@ void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPoly
{
OSL_ENSURE(nDXArrayCount == nTextLength,
"DXArray size does not correspond to text portion size (!)");
- std::vector<sal_Int32> aIntegerDXArray(nDXArrayCount);
+ KernArray aIntegerDXArray;
+ aIntegerDXArray.reserve(nDXArrayCount);
for (sal_uInt32 a(0); a < nDXArrayCount; a++)
- {
- aIntegerDXArray[a] = basegfx::fround(rDXArray[a]);
- }
+ aIntegerDXArray.push_back(basegfx::fround(rDXArray[a]));
mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength, 0,
- aIntegerDXArray);
+ aIntegerDXArray, rKashidaArray);
}
else
{
mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength);
}
+ if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0)
+ || !rtl_math_approxEqual(mnFontScalingFixX, 1.0))
+ {
+ auto scale = basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY);
+ for (auto& poly : rB2DPolyPolyVector)
+ poly.transform(scale);
+ }
}
basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sal_uInt32 nIndex,
@@ -260,15 +291,15 @@ basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sa
if (nTextLength)
{
- ::tools::Rectangle aRect;
-
+ basegfx::B2DRange aRect;
mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength);
-
- // #i104432#, #i102556# take empty results into account
- if (!aRect.IsEmpty())
+ if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0)
+ || !rtl_math_approxEqual(mnFontScalingFixX, 1.0))
{
- return vcl::unotools::b2DRectangleFromRectangle(aRect);
+ aRect.transform(
+ basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY));
}
+ return aRect;
}
return basegfx::B2DRange();
@@ -277,13 +308,13 @@ basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sa
double TextLayouterDevice::getFontAscent() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
- return rMetric.GetAscent();
+ return rMetric.GetAscent() * mnFontScalingFixY;
}
double TextLayouterDevice::getFontDescent() const
{
const ::FontMetric& rMetric = mrDevice.GetFontMetric();
- return rMetric.GetDescent();
+ return rMetric.GetDescent() * mnFontScalingFixY;
}
void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle,
@@ -294,30 +325,7 @@ void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle
}
std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_uInt32 nIndex,
- sal_uInt32 nLength) const
-{
- std::vector<double> aRetval;
- sal_uInt32 nTextLength(nLength);
- const sal_uInt32 nStringLength(rText.getLength());
-
- if (nTextLength + nIndex > nStringLength)
- {
- nTextLength = nStringLength - nIndex;
- }
-
- if (nTextLength)
- {
- aRetval.reserve(nTextLength);
- std::vector<sal_Int32> aArray(nTextLength);
- mrDevice.GetTextArray(rText, &aArray, nIndex, nLength);
- aRetval.assign(aArray.begin(), aArray.end());
- }
-
- return aRetval;
-}
-
-std::vector<double> TextLayouterDevice::getCaretPositions(const OUString& rText, sal_uInt32 nIndex,
- sal_uInt32 nLength) const
+ sal_uInt32 nLength, bool bCaret) const
{
std::vector<double> aRetval;
sal_uInt32 nTextLength(nLength);
@@ -330,10 +338,11 @@ std::vector<double> TextLayouterDevice::getCaretPositions(const OUString& rText,
if (nTextLength)
{
- aRetval.reserve(2 * nTextLength);
- std::vector<sal_Int32> aArray(2 * nTextLength);
- mrDevice.GetCaretPositions(rText, aArray.data(), nIndex, nLength);
- aRetval.assign(aArray.begin(), aArray.end());
+ KernArray aArray;
+ mrDevice.GetTextArray(rText, &aArray, nIndex, nTextLength, bCaret);
+ aRetval.reserve(aArray.size());
+ for (size_t i = 0, nEnd = aArray.size(); i < nEnd; ++i)
+ aRetval.push_back(aArray[i] * mnFontScalingFixX);
}
return aRetval;
diff --git a/drawinglayer/source/primitive2d/textlineprimitive2d.cxx b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
index ea23ad25b684..496af0706520 100644
--- a/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
@@ -22,16 +22,17 @@
#include <drawinglayer/attribute/strokeattribute.hxx>
#include <drawinglayer/attribute/lineattribute.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <utility>
namespace drawinglayer::primitive2d
{
- void TextLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference TextLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if(TEXT_LINE_NONE == getTextLine())
- return;
+ return nullptr;
bool bDoubleLine(false);
bool bWaveLine(false);
@@ -207,18 +208,19 @@ namespace drawinglayer::primitive2d
fWaveWidth *= 2.0;
}
- aNewPrimitive = Primitive2DReference(new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5));
+ aNewPrimitive = new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5);
}
else
{
- aNewPrimitive = Primitive2DReference(new PolygonStrokePrimitive2D(aLine, aLineAttribute, aStrokeAttribute));
+ aNewPrimitive = new PolygonStrokePrimitive2D(std::move(aLine), aLineAttribute, std::move(aStrokeAttribute));
}
- // add primitive
- rContainer.push_back(aNewPrimitive);
-
if(!bDoubleLine)
- return;
+ return aNewPrimitive;
+
+ // add primitive
+ Primitive2DContainer aContainer;
+ aContainer.push_back(aNewPrimitive);
// double line, create 2nd primitive with offset using TransformPrimitive based on
// already created NewPrimitive
@@ -243,17 +245,18 @@ namespace drawinglayer::primitive2d
// add transform primitive
Primitive2DContainer aContent { aNewPrimitive };
- rContainer.push_back( new TransformPrimitive2D(aTransform, std::move(aContent)) );
+ aContainer.push_back(new TransformPrimitive2D(aTransform, std::move(aContent)));
+ return new GroupPrimitive2D(std::move(aContainer));
}
TextLinePrimitive2D::TextLinePrimitive2D(
- const basegfx::B2DHomMatrix& rObjectTransformation,
+ basegfx::B2DHomMatrix aObjectTransformation,
double fWidth,
double fOffset,
double fHeight,
TextLine eTextLine,
const basegfx::BColor& rLineColor)
- : maObjectTransformation(rObjectTransformation),
+ : maObjectTransformation(std::move(aObjectTransformation)),
mfWidth(fWidth),
mfOffset(fOffset),
mfHeight(fHeight),
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx b/drawinglayer/source/primitive2d/textprimitive2d.cxx
index 4e584f2b6e92..2d91785c5287 100644
--- a/drawinglayer/source/primitive2d/textprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -22,8 +22,11 @@
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <primitive2d/texteffectprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <utility>
+#include <osl/diagnose.h>
using namespace com::sun::star;
@@ -50,7 +53,7 @@ basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
rScale.setY(1.0 / fDefaultFontScale);
aFontScale.setY(fDefaultFontScale);
}
- else if (basegfx::fTools::less(aFontScale.getY(), 0.0))
+ else if (aFontScale.getY() < 0.0)
{
// negative font height; invert and adapt scale to get back to original scaling
aFontScale.setY(-aFontScale.getY());
@@ -98,7 +101,7 @@ void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
// handle special case: If scale is negative in (x,y) (3rd quadrant), it can
// be expressed as rotation by PI
- if (basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ if (aScale.getX() < 0.0 && aScale.getY() < 0.0)
{
aScale = basegfx::absolute(aScale);
fRotate += M_PI;
@@ -130,13 +133,13 @@ void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
// get the text outlines
aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(),
- aScaledDXArray);
+ aScaledDXArray, getKashidaArray());
}
else
{
// get the text outlines
aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(),
- getDXArray());
+ getDXArray(), getKashidaArray());
}
// create primitives for the outlines
@@ -150,13 +153,12 @@ void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
}
}
-void TextSimplePortionPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+Primitive2DReference TextSimplePortionPrimitive2D::create2DDecomposition(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (!getTextLength())
- return;
+ return nullptr;
- Primitive2DContainer aRetval;
basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
basegfx::B2DHomMatrix aPolygonTransform;
@@ -167,9 +169,10 @@ void TextSimplePortionPrimitive2D::create2DDecomposition(
const sal_uInt32 nCount(aB2DPolyPolyVector.size());
if (!nCount)
- return;
+ return nullptr;
// alloc space for the primitives
+ Primitive2DContainer aRetval;
aRetval.resize(nCount);
// color-filled polypolygons
@@ -189,28 +192,27 @@ void TextSimplePortionPrimitive2D::create2DDecomposition(
aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX);
// create outline text effect with current content and replace
- Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
- std::move(aRetval), aTranslate, fRotate, TextEffectStyle2D::Outline));
-
- aRetval = Primitive2DContainer{ aNewTextEffect };
+ return new TextEffectPrimitive2D(std::move(aRetval), aTranslate, fRotate,
+ TextEffectStyle2D::Outline);
}
- rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
+ return new GroupPrimitive2D(std::move(aRetval));
}
TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
- const basegfx::B2DHomMatrix& rNewTransform, const OUString& rText, sal_Int32 nTextPosition,
- sal_Int32 nTextLength, std::vector<double>&& rDXArray,
- const attribute::FontAttribute& rFontAttribute, const css::lang::Locale& rLocale,
+ basegfx::B2DHomMatrix rNewTransform, OUString rText, sal_Int32 nTextPosition,
+ sal_Int32 nTextLength, std::vector<double>&& rDXArray, std::vector<sal_Bool>&& rKashidaArray,
+ attribute::FontAttribute aFontAttribute, css::lang::Locale aLocale,
const basegfx::BColor& rFontColor, bool bFilled, tools::Long nWidthToFill,
const Color& rTextFillColor)
- : maTextTransform(rNewTransform)
- , maText(rText)
+ : maTextTransform(std::move(rNewTransform))
+ , maText(std::move(rText))
, mnTextPosition(nTextPosition)
, mnTextLength(nTextLength)
, maDXArray(std::move(rDXArray))
- , maFontAttribute(rFontAttribute)
- , maLocale(rLocale)
+ , maKashidaArray(std::move(rKashidaArray))
+ , maFontAttribute(std::move(aFontAttribute))
+ , maLocale(std::move(aLocale))
, maFontColor(rFontColor)
, mbFilled(bFilled)
, mnWidthToFill(nWidthToFill)
@@ -240,6 +242,7 @@ bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive)
&& getTextPosition() == rCompare.getTextPosition()
&& getTextLength() == rCompare.getTextLength()
&& getDXArray() == rCompare.getDXArray()
+ && getKashidaArray() == rCompare.getKashidaArray()
&& getFontAttribute() == rCompare.getFontAttribute()
&& LocalesAreEqual(getLocale(), rCompare.getLocale())
&& getFontColor() == rCompare.getFontColor() && mbFilled == rCompare.mbFilled
diff --git a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
index 1c4983a87c18..41f7299c4486 100644
--- a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
@@ -24,18 +24,20 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/attribute/lineattribute.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <osl/diagnose.h>
#include <rtl/ustrbuf.hxx>
+#include <utility>
namespace drawinglayer::primitive2d
{
BaseTextStrikeoutPrimitive2D::BaseTextStrikeoutPrimitive2D(
- const basegfx::B2DHomMatrix& rObjectTransformation,
+ basegfx::B2DHomMatrix aObjectTransformation,
double fWidth,
const basegfx::BColor& rFontColor)
- : maObjectTransformation(rObjectTransformation),
+ : maObjectTransformation(std::move(aObjectTransformation)),
mfWidth(fWidth),
maFontColor(rFontColor)
{
@@ -56,7 +58,7 @@ namespace drawinglayer::primitive2d
}
- void TextCharacterStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference TextCharacterStrikeoutPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
// strikeout with character
const OUString aSingleCharString(getStrikeoutChar());
@@ -88,16 +90,17 @@ namespace drawinglayer::primitive2d
}
auto len = aStrikeoutString.getLength();
- rContainer.push_back(
+ return
new TextSimplePortionPrimitive2D(
getObjectTransformation(),
aStrikeoutString.makeStringAndClear(),
0,
len,
std::move(aDXArray),
+ {},
getFontAttribute(),
getLocale(),
- getFontColor()));
+ getFontColor());
}
TextCharacterStrikeoutPrimitive2D::TextCharacterStrikeoutPrimitive2D(
@@ -105,12 +108,12 @@ namespace drawinglayer::primitive2d
double fWidth,
const basegfx::BColor& rFontColor,
sal_Unicode aStrikeoutChar,
- const attribute::FontAttribute& rFontAttribute,
- const css::lang::Locale& rLocale)
+ attribute::FontAttribute aFontAttribute,
+ css::lang::Locale aLocale)
: BaseTextStrikeoutPrimitive2D(rObjectTransformation, fWidth, rFontColor),
maStrikeoutChar(aStrikeoutChar),
- maFontAttribute(rFontAttribute),
- maLocale(rLocale)
+ maFontAttribute(std::move(aFontAttribute)),
+ maLocale(std::move(aLocale))
{
}
@@ -134,7 +137,7 @@ namespace drawinglayer::primitive2d
return PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D;
}
- void TextGeometryStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference TextGeometryStrikeoutPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
OSL_ENSURE(TEXT_STRIKEOUT_SLASH != getTextStrikeout() && TEXT_STRIKEOUT_X != getTextStrikeout(),
"Wrong TEXT_STRIKEOUT type; a TextCharacterStrikeoutPrimitive2D should be used (!)");
@@ -188,8 +191,7 @@ namespace drawinglayer::primitive2d
// add primitive
const attribute::LineAttribute aLineAttribute(getFontColor(), fStrikeoutHeight, basegfx::B2DLineJoin::NONE);
- Primitive2DContainer xRetval(1);
- xRetval[0] = new PolygonStrokePrimitive2D(aStrikeoutLine, aLineAttribute);
+ Primitive2DReference xRetval = new PolygonStrokePrimitive2D(std::move(aStrikeoutLine), aLineAttribute);
if(bDoubleLine)
{
@@ -210,13 +212,13 @@ namespace drawinglayer::primitive2d
aTransform.translate(aTranslate.getX(), aTranslate.getY());
// add transform primitive
- xRetval.push_back(
+ xRetval =
new TransformPrimitive2D(
aTransform,
- Primitive2DContainer(xRetval)));
+ Primitive2DContainer{xRetval});
}
- rContainer.insert(rContainer.end(), xRetval.begin(), xRetval.end());
+ return xRetval;
}
TextGeometryStrikeoutPrimitive2D::TextGeometryStrikeoutPrimitive2D(
diff --git a/drawinglayer/source/primitive2d/transformprimitive2d.cxx b/drawinglayer/source/primitive2d/transformprimitive2d.cxx
index ddd1594e270e..8c36fa9a73b9 100644
--- a/drawinglayer/source/primitive2d/transformprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/transformprimitive2d.cxx
@@ -19,6 +19,8 @@
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -27,20 +29,29 @@ using namespace com::sun::star;
namespace drawinglayer::primitive2d
{
TransformPrimitive2D::TransformPrimitive2D(
- const basegfx::B2DHomMatrix& rTransformation,
+ basegfx::B2DHomMatrix aTransformation,
Primitive2DContainer&& aChildren)
- : GroupPrimitive2D(std::move(aChildren)),
- maTransformation(rTransformation)
+ : maTransformation(std::move(aTransformation)),
+ mxChildren(new GroupPrimitive2D(std::move(aChildren)))
+ {
+ }
+
+ TransformPrimitive2D::TransformPrimitive2D(
+ basegfx::B2DHomMatrix aTransformation,
+ GroupPrimitive2D& rChildren)
+ : maTransformation(std::move(aTransformation)),
+ mxChildren(&rChildren)
{
}
bool TransformPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
- if(GroupPrimitive2D::operator==(rPrimitive))
+ if(BasePrimitive2D::operator==(rPrimitive))
{
const TransformPrimitive2D& rCompare = static_cast< const TransformPrimitive2D& >(rPrimitive);
- return (getTransformation() == rCompare.getTransformation());
+ return maTransformation == rCompare.maTransformation
+ && arePrimitive2DReferencesEqual(mxChildren, rCompare.mxChildren);
}
return false;
diff --git a/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
index c50fcb008435..4a8383a5a354 100644
--- a/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
@@ -21,10 +21,10 @@
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
using namespace com::sun::star;
@@ -85,15 +85,15 @@ namespace drawinglayer::primitive2d
// I will take the last one here. The small overhead of two primitives will only be
// used when UnifiedTransparencePrimitive2D is not handled directly.
const basegfx::B2DRange aPolygonRange(getChildren().getB2DRange(rViewInformation));
- const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aPolygonRange));
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aPolygonRange));
const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence());
Primitive2DContainer aTransparenceContent(2);
- aTransparenceContent[0] = Primitive2DReference(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), aGray));
- aTransparenceContent[1] = Primitive2DReference(new PolygonHairlinePrimitive2D(aPolygon, aGray));
+ aTransparenceContent[0] = new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), aGray);
+ aTransparenceContent[1] = new PolygonHairlinePrimitive2D(std::move(aPolygon), aGray);
// create sub-transparence group with a gray-colored rectangular fill polygon
- rVisitor.append(new TransparencePrimitive2D(Primitive2DContainer(getChildren()), std::move(aTransparenceContent)));
+ rVisitor.visit(new TransparencePrimitive2D(Primitive2DContainer(getChildren()), std::move(aTransparenceContent)));
}
else
{
diff --git a/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
index 6e7e134f2fa2..9e3af10415dd 100644
--- a/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
@@ -31,190 +31,186 @@
namespace drawinglayer::primitive2d
{
- void WallpaperBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference WallpaperBitmapPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
+
+ if(getLocalObjectRange().isEmpty() || getBitmapEx().IsEmpty())
+ return nullptr;
+
Primitive2DReference aRetval;
- if(!getLocalObjectRange().isEmpty() && !getBitmapEx().IsEmpty())
- {
- // get bitmap PIXEL size
- const Size& rPixelSize = getBitmapEx().GetSizePixel();
+ // get bitmap PIXEL size
+ const Size& rPixelSize = getBitmapEx().GetSizePixel();
- if(rPixelSize.Width() > 0 && rPixelSize.Height() > 0)
+ if(rPixelSize.Width() <= 0 || rPixelSize.Height() <= 0)
+ return nullptr;
+
+ if(WallpaperStyle::Scale == getWallpaperStyle())
+ {
+ // shortcut for scale; use simple BitmapPrimitive2D
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, getLocalObjectRange().getWidth());
+ aObjectTransform.set(1, 1, getLocalObjectRange().getHeight());
+ aObjectTransform.set(0, 2, getLocalObjectRange().getMinX());
+ aObjectTransform.set(1, 2, getLocalObjectRange().getMinY());
+
+ aRetval.set(
+ new BitmapPrimitive2D(
+ getBitmapEx(),
+ aObjectTransform));
+ }
+ else
+ {
+ // transform to logic size
+ basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation());
+ aInverseViewTransformation.invert();
+ basegfx::B2DVector aLogicSize(rPixelSize.Width(), rPixelSize.Height());
+ aLogicSize = aInverseViewTransformation * aLogicSize;
+
+ // apply layout
+ basegfx::B2DPoint aTargetTopLeft(getLocalObjectRange().getMinimum());
+ bool bUseTargetTopLeft(true);
+ bool bNeedsClipping(false);
+
+ switch(getWallpaperStyle())
{
- if(WallpaperStyle::Scale == getWallpaperStyle())
+ default: //case WallpaperStyle::Tile :, also WallpaperStyle::NONE and WallpaperStyle::ApplicationGradient
+ {
+ bUseTargetTopLeft = false;
+ break;
+ }
+ case WallpaperStyle::Scale :
+ {
+ // handled by shortcut above
+ break;
+ }
+ case WallpaperStyle::TopLeft :
+ {
+ // nothing to do
+ break;
+ }
+ case WallpaperStyle::Top :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5));
+ break;
+ }
+ case WallpaperStyle::TopRight :
{
- // shortcut for scale; use simple BitmapPrimitive2D
- basegfx::B2DHomMatrix aObjectTransform;
+ aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX());
+ break;
+ }
+ case WallpaperStyle::Left :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5));
+ break;
+ }
+ case WallpaperStyle::Center :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft = aCenter - (aLogicSize * 0.5);
+ break;
+ }
+ case WallpaperStyle::Right :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX());
+ aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5));
+ break;
+ }
+ case WallpaperStyle::BottomLeft :
+ {
+ aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY());
+ break;
+ }
+ case WallpaperStyle::Bottom :
+ {
+ const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
+ aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5));
+ aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY());
+ break;
+ }
+ case WallpaperStyle::BottomRight :
+ {
+ aTargetTopLeft = getLocalObjectRange().getMaximum() - aLogicSize;
+ break;
+ }
+ }
- aObjectTransform.set(0, 0, getLocalObjectRange().getWidth());
- aObjectTransform.set(1, 1, getLocalObjectRange().getHeight());
- aObjectTransform.set(0, 2, getLocalObjectRange().getMinX());
- aObjectTransform.set(1, 2, getLocalObjectRange().getMinY());
+ if(bUseTargetTopLeft)
+ {
+ // fill target range
+ const basegfx::B2DRange aTargetRange(aTargetTopLeft, aTargetTopLeft + aLogicSize);
- Primitive2DReference xReference(
- new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
- aObjectTransform));
+ // create aligned, single BitmapPrimitive2D
+ basegfx::B2DHomMatrix aObjectTransform;
- aRetval = xReference;
- }
- else
+ aObjectTransform.set(0, 0, aTargetRange.getWidth());
+ aObjectTransform.set(1, 1, aTargetRange.getHeight());
+ aObjectTransform.set(0, 2, aTargetRange.getMinX());
+ aObjectTransform.set(1, 2, aTargetRange.getMinY());
+
+ aRetval.set(
+ new BitmapPrimitive2D(
+ getBitmapEx(),
+ aObjectTransform));
+
+ // clip when not completely inside object range
+ bNeedsClipping = !getLocalObjectRange().isInside(aTargetRange);
+ }
+ else
+ {
+ // WallpaperStyle::Tile, WallpaperStyle::NONE, WallpaperStyle::ApplicationGradient
+ // convert to relative positions
+ const basegfx::B2DVector aRelativeSize(
+ aLogicSize.getX() / (getLocalObjectRange().getWidth() ? getLocalObjectRange().getWidth() : 1.0),
+ aLogicSize.getY() / (getLocalObjectRange().getHeight() ? getLocalObjectRange().getHeight() : 1.0));
+ basegfx::B2DPoint aRelativeTopLeft(0.0, 0.0);
+
+ if(WallpaperStyle::Tile != getWallpaperStyle())
{
- // transform to logic size
- basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation());
- aInverseViewTransformation.invert();
- basegfx::B2DVector aLogicSize(rPixelSize.Width(), rPixelSize.Height());
- aLogicSize = aInverseViewTransformation * aLogicSize;
-
- // apply layout
- basegfx::B2DPoint aTargetTopLeft(getLocalObjectRange().getMinimum());
- bool bUseTargetTopLeft(true);
- bool bNeedsClipping(false);
-
- switch(getWallpaperStyle())
- {
- default: //case WallpaperStyle::Tile :, also WallpaperStyle::NONE and WallpaperStyle::ApplicationGradient
- {
- bUseTargetTopLeft = false;
- break;
- }
- case WallpaperStyle::Scale :
- {
- // handled by shortcut above
- break;
- }
- case WallpaperStyle::TopLeft :
- {
- // nothing to do
- break;
- }
- case WallpaperStyle::Top :
- {
- const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
- aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5));
- break;
- }
- case WallpaperStyle::TopRight :
- {
- aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX());
- break;
- }
- case WallpaperStyle::Left :
- {
- const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
- aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5));
- break;
- }
- case WallpaperStyle::Center :
- {
- const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
- aTargetTopLeft = aCenter - (aLogicSize * 0.5);
- break;
- }
- case WallpaperStyle::Right :
- {
- const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
- aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX());
- aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5));
- break;
- }
- case WallpaperStyle::BottomLeft :
- {
- aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY());
- break;
- }
- case WallpaperStyle::Bottom :
- {
- const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter());
- aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5));
- aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY());
- break;
- }
- case WallpaperStyle::BottomRight :
- {
- aTargetTopLeft = getLocalObjectRange().getMaximum() - aLogicSize;
- break;
- }
- }
-
- if(bUseTargetTopLeft)
- {
- // fill target range
- const basegfx::B2DRange aTargetRange(aTargetTopLeft, aTargetTopLeft + aLogicSize);
-
- // create aligned, single BitmapPrimitive2D
- basegfx::B2DHomMatrix aObjectTransform;
-
- aObjectTransform.set(0, 0, aTargetRange.getWidth());
- aObjectTransform.set(1, 1, aTargetRange.getHeight());
- aObjectTransform.set(0, 2, aTargetRange.getMinX());
- aObjectTransform.set(1, 2, aTargetRange.getMinY());
-
- Primitive2DReference xReference(
- new BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
- aObjectTransform));
- aRetval = xReference;
-
- // clip when not completely inside object range
- bNeedsClipping = !getLocalObjectRange().isInside(aTargetRange);
- }
- else
- {
- // WallpaperStyle::Tile, WallpaperStyle::NONE, WallpaperStyle::ApplicationGradient
- // convert to relative positions
- const basegfx::B2DVector aRelativeSize(
- aLogicSize.getX() / (getLocalObjectRange().getWidth() ? getLocalObjectRange().getWidth() : 1.0),
- aLogicSize.getY() / (getLocalObjectRange().getHeight() ? getLocalObjectRange().getHeight() : 1.0));
- basegfx::B2DPoint aRelativeTopLeft(0.0, 0.0);
-
- if(WallpaperStyle::Tile != getWallpaperStyle())
- {
- aRelativeTopLeft.setX(0.5 - aRelativeSize.getX());
- aRelativeTopLeft.setY(0.5 - aRelativeSize.getY());
- }
-
- // prepare FillGraphicAttribute
- const attribute::FillGraphicAttribute aFillGraphicAttribute(
- Graphic(getBitmapEx()),
- basegfx::B2DRange(aRelativeTopLeft, aRelativeTopLeft+ aRelativeSize),
- true);
-
- // create ObjectTransform
- const basegfx::B2DHomMatrix aObjectTransform(
- basegfx::utils::createScaleTranslateB2DHomMatrix(
- getLocalObjectRange().getRange(),
- getLocalObjectRange().getMinimum()));
-
- // create FillBitmapPrimitive
- const drawinglayer::primitive2d::Primitive2DReference xFillBitmap(
- new drawinglayer::primitive2d::FillGraphicPrimitive2D(
- aObjectTransform,
- aFillGraphicAttribute));
- aRetval = xFillBitmap;
-
- // always embed tiled fill to clipping
- bNeedsClipping = true;
- }
-
- if(bNeedsClipping)
- {
- // embed to clipping; this is necessary for tiled fills
- const basegfx::B2DPolyPolygon aPolyPolygon(
- basegfx::utils::createPolygonFromRect(getLocalObjectRange()));
- const drawinglayer::primitive2d::Primitive2DReference xClippedFill(
- new drawinglayer::primitive2d::MaskPrimitive2D(
- aPolyPolygon,
- { aRetval }));
- aRetval = xClippedFill;
- }
+ aRelativeTopLeft.setX(0.5 - aRelativeSize.getX());
+ aRelativeTopLeft.setY(0.5 - aRelativeSize.getY());
}
+
+ // prepare FillGraphicAttribute
+ const attribute::FillGraphicAttribute aFillGraphicAttribute(
+ Graphic(getBitmapEx()),
+ basegfx::B2DRange(aRelativeTopLeft, aRelativeTopLeft+ aRelativeSize),
+ true);
+
+ // create ObjectTransform
+ const basegfx::B2DHomMatrix aObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ getLocalObjectRange().getRange(),
+ getLocalObjectRange().getMinimum()));
+
+ // create FillBitmapPrimitive
+ aRetval.set(
+ new drawinglayer::primitive2d::FillGraphicPrimitive2D(
+ aObjectTransform,
+ aFillGraphicAttribute));
+
+ // always embed tiled fill to clipping
+ bNeedsClipping = true;
+ }
+
+ if(bNeedsClipping)
+ {
+ // embed to clipping; this is necessary for tiled fills
+ basegfx::B2DPolyPolygon aPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getLocalObjectRange()));
+ const drawinglayer::primitive2d::Primitive2DReference xClippedFill(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ std::move(aPolyPolygon),
+ { aRetval }));
+ aRetval = xClippedFill;
}
}
- if (aRetval.is())
- rContainer.push_back(aRetval);
+ return aRetval;
}
WallpaperBitmapPrimitive2D::WallpaperBitmapPrimitive2D(
diff --git a/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
index 11a5b1929870..6084814ae71f 100644
--- a/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
@@ -19,14 +19,15 @@
#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <utility>
namespace drawinglayer::primitive2d
{
- void WrongSpellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ Primitive2DReference WrongSpellPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
{
// ATM this decompose is view-independent, what the original VCL-Display is not. To mimic
// the old behaviour here if wanted it is necessary to add get2DDecomposition and implement
@@ -64,15 +65,15 @@ namespace drawinglayer::primitive2d
const attribute::LineAttribute aLineAttribute(getColor());
// create the waveline primitive
- rContainer.push_back(new PolygonWavePrimitive2D(aPolygon, aLineAttribute, fWaveWidth, 0.5 * fWaveWidth));
+ return new PolygonWavePrimitive2D(aPolygon, aLineAttribute, fWaveWidth, 0.5 * fWaveWidth);
}
WrongSpellPrimitive2D::WrongSpellPrimitive2D(
- const basegfx::B2DHomMatrix& rTransformation,
+ basegfx::B2DHomMatrix aTransformation,
double fStart,
double fStop,
const basegfx::BColor& rColor)
- : maTransformation(rTransformation),
+ : maTransformation(std::move(aTransformation)),
mfStart(fStart),
mfStop(fStop),
maColor(rColor)
diff --git a/drawinglayer/source/primitive3d/baseprimitive3d.cxx b/drawinglayer/source/primitive3d/baseprimitive3d.cxx
index 5bd980012a61..c2c8cc9f7e0f 100644
--- a/drawinglayer/source/primitive3d/baseprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/baseprimitive3d.cxx
@@ -29,7 +29,6 @@ using namespace com::sun::star;
namespace drawinglayer::primitive3d
{
BasePrimitive3D::BasePrimitive3D()
- : BasePrimitive3DImplBase(m_aMutex)
{
}
@@ -76,7 +75,7 @@ namespace drawinglayer::primitive3d
Primitive3DContainer BufferedDecompositionPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
if(getBuffered3DDecomposition().empty())
{
diff --git a/drawinglayer/source/primitive3d/groupprimitive3d.cxx b/drawinglayer/source/primitive3d/groupprimitive3d.cxx
index 69291e76872e..a3056e3ed432 100644
--- a/drawinglayer/source/primitive3d/groupprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/groupprimitive3d.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/primitive3d/groupprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -26,12 +27,12 @@ using namespace com::sun::star;
namespace drawinglayer::primitive3d
{
- GroupPrimitive3D::GroupPrimitive3D( const Primitive3DContainer& rChildren )
- : maChildren(rChildren)
+ GroupPrimitive3D::GroupPrimitive3D( Primitive3DContainer aChildren )
+ : maChildren(std::move(aChildren))
{
}
- /** The compare opertator uses the Sequence::==operator, so only checking if
+ /** The compare operator uses the Sequence::==operator, so only checking if
the references are equal. All non-equal references are interpreted as
non-equal.
*/
diff --git a/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
index a699f71706bb..3e0abc582732 100644
--- a/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx
@@ -29,6 +29,7 @@
#include <basegfx/matrix/b3dhommatrix.hxx>
#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -267,13 +268,13 @@ namespace drawinglayer::primitive3d
}
HatchTexturePrimitive3D::HatchTexturePrimitive3D(
- const attribute::FillHatchAttribute& rHatch,
+ attribute::FillHatchAttribute aHatch,
const Primitive3DContainer& rChildren,
const basegfx::B2DVector& rTextureSize,
bool bModulate,
bool bFilter)
: TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
- maHatch(rHatch)
+ maHatch(std::move(aHatch))
{
}
@@ -291,7 +292,7 @@ namespace drawinglayer::primitive3d
Primitive3DContainer HatchTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
if(getBuffered3DDecomposition().empty())
{
diff --git a/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
index 255f0235c911..54fa166f055d 100644
--- a/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -28,9 +29,9 @@ namespace drawinglayer::primitive3d
{
ModifiedColorPrimitive3D::ModifiedColorPrimitive3D(
const Primitive3DContainer& rChildren,
- const basegfx::BColorModifierSharedPtr& rColorModifier)
+ basegfx::BColorModifierSharedPtr xColorModifier)
: GroupPrimitive3D(rChildren),
- maColorModifier(rColorModifier)
+ maColorModifier(std::move(xColorModifier))
{
}
diff --git a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
index 8fc2b4d31298..6127ac77666b 100644
--- a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx
@@ -22,6 +22,7 @@
#include <basegfx/polygon/b3dpolypolygon.hxx>
#include <primitive3d/polygontubeprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -30,9 +31,9 @@ using namespace com::sun::star;
namespace drawinglayer::primitive3d
{
PolygonHairlinePrimitive3D::PolygonHairlinePrimitive3D(
- const basegfx::B3DPolygon& rPolygon,
+ basegfx::B3DPolygon aPolygon,
const basegfx::BColor& rBColor)
- : maPolygon(rPolygon),
+ : maPolygon(std::move(aPolygon)),
maBColor(rBColor)
{
}
@@ -117,12 +118,12 @@ namespace drawinglayer::primitive3d
}
PolygonStrokePrimitive3D::PolygonStrokePrimitive3D(
- const basegfx::B3DPolygon& rPolygon,
+ basegfx::B3DPolygon aPolygon,
const attribute::LineAttribute& rLineAttribute,
- const attribute::StrokeAttribute& rStrokeAttribute)
- : maPolygon(rPolygon),
+ attribute::StrokeAttribute aStrokeAttribute)
+ : maPolygon(std::move(aPolygon)),
maLineAttribute(rLineAttribute),
- maStrokeAttribute(rStrokeAttribute)
+ maStrokeAttribute(std::move(aStrokeAttribute))
{
}
diff --git a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
index d8d3cc9c8b3e..672c78463a7d 100644
--- a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx
@@ -92,9 +92,8 @@ namespace drawinglayer::primitive3d
aNewPolygon.setClosed(true);
- const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
- const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
- m_aLineTubeList[a] = xRef;
+ basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ m_aLineTubeList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false);
aLastLeft = aNextLeft;
aLastRight = aNextRight;
@@ -167,9 +166,8 @@ namespace drawinglayer::primitive3d
aNewPolygon.setClosed(true);
- const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
- const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false));
- m_aLineCapList[a] = xRef;
+ basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ m_aLineCapList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false);
aLast = aNext;
}
@@ -254,12 +252,12 @@ namespace drawinglayer::primitive3d
for (sal_uInt32 a = 0; a < nCount; ++a)
{
const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
- const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
+ basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
// need to create one primitive per Polygon since the primitive
// is for planar PolyPolygons which is definitely not the case here
m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D(
- aPartPolyPolygon,
+ std::move(aPartPolyPolygon),
rMaterial,
false);
}
@@ -306,8 +304,8 @@ namespace drawinglayer::primitive3d
for(sal_uInt32 a(0); a < aSphere.count(); a++)
{
const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a));
- const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
- aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false));
+ basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aPartPolyPolygon), rMaterial, false));
}
}
else
@@ -453,8 +451,8 @@ namespace drawinglayer::primitive3d
// create primitive
if(aNewPolygon.count())
{
- const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
- aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false));
+ basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), rMaterial, false));
}
if(bMiter && aMiterPolygon.count())
@@ -466,8 +464,8 @@ namespace drawinglayer::primitive3d
}
// create primitive
- const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
- aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false));
+ basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon);
+ aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aMiterPolyPolygon), rMaterial, false));
}
// prepare next step
@@ -487,10 +485,7 @@ namespace drawinglayer::primitive3d
Primitive3DContainer aRetval(aResultVector.size());
- for(size_t a(0); a < aResultVector.size(); a++)
- {
- aRetval[a] = Primitive3DReference(aResultVector[a]);
- }
+ std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);});
return aRetval;
}
@@ -522,7 +517,7 @@ using namespace com::sun::star;
if(nPointCount)
{
- if(basegfx::fTools::more(getRadius(), 0.0))
+ if(getRadius() > 0.0)
{
const attribute::MaterialAttribute3D aMaterial(getBColor());
static const sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps)
@@ -539,7 +534,7 @@ using namespace com::sun::star;
const basegfx::B3DVector aForw(aNext - aCurr);
const double fForwLen(aForw.getLength());
- if(basegfx::fTools::more(fForwLen, 0.0))
+ if(fForwLen > 0.0)
{
// find out if linecap is active
const bool bFirst(!a);
@@ -601,7 +596,7 @@ using namespace com::sun::star;
aSequence = getLineCapSegments(nSegments, aMaterial);
}
- aResultVector.push_back(new TransformPrimitive3D(aCapTrans, aSequence));
+ aResultVector.push_back(new TransformPrimitive3D(std::move(aCapTrans), aSequence));
}
else
{
@@ -638,7 +633,7 @@ using namespace com::sun::star;
// line start edge, build transformed primitiveVector3D
aResultVector.push_back(
new TransformPrimitive3D(
- aSphereTrans,
+ std::move(aSphereTrans),
aNewList));
}
}
@@ -646,7 +641,7 @@ using namespace com::sun::star;
// create line segments, build transformed primitiveVector3D
aResultVector.push_back(
new TransformPrimitive3D(
- aTubeTrans,
+ std::move(aTubeTrans),
getLineTubeSegments(nSegments, aMaterial)));
if(bNoLineJoin || (!bClosed && bLast))
@@ -689,7 +684,7 @@ using namespace com::sun::star;
aResultVector.push_back(
new TransformPrimitive3D(
- aBackCapTrans,
+ std::move(aBackCapTrans),
aSequence));
}
}
@@ -709,10 +704,7 @@ using namespace com::sun::star;
// prepare return value
Primitive3DContainer aRetval(aResultVector.size());
- for(size_t a(0); a < aResultVector.size(); a++)
- {
- aRetval[a] = Primitive3DReference(aResultVector[a]);
- }
+ std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);});
return aRetval;
}
@@ -751,7 +743,7 @@ using namespace com::sun::star;
Primitive3DContainer PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
if(getLast3DDecomposition().empty())
{
diff --git a/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
index 41e62b611405..09c8fb515933 100644
--- a/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx
@@ -20,6 +20,7 @@
#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
#include <basegfx/polygon/b3dpolypolygontools.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -28,10 +29,10 @@ using namespace com::sun::star;
namespace drawinglayer::primitive3d
{
PolyPolygonMaterialPrimitive3D::PolyPolygonMaterialPrimitive3D(
- const basegfx::B3DPolyPolygon& rPolyPolygon,
+ basegfx::B3DPolyPolygon aPolyPolygon,
const attribute::MaterialAttribute3D& rMaterial,
bool bDoubleSided)
- : maPolyPolygon(rPolyPolygon),
+ : maPolyPolygon(std::move(aPolyPolygon)),
maMaterial(rMaterial),
mbDoubleSided(bDoubleSided)
{
diff --git a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
index ec6bfaf3c8a4..7690dd300f6e 100644
--- a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
+++ b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx
@@ -144,8 +144,7 @@ namespace drawinglayer::primitive3d
for(sal_uInt32 a(0); a < aScaledPolyPolygon.count(); a++)
{
- const Primitive3DReference xRef(new PolygonStrokePrimitive3D(aScaledPolyPolygon.getB3DPolygon(a), aLineAttribute, aStrokeAttribute));
- aRetval[a] = xRef;
+ aRetval[a] = new PolygonStrokePrimitive3D(aScaledPolyPolygon.getB3DPolygon(a), aLineAttribute, aStrokeAttribute);
}
if(0.0 != rLine.getTransparence())
@@ -185,7 +184,7 @@ namespace drawinglayer::primitive3d
}
const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(
- aScaledPolyPolygon,
+ std::move(aScaledPolyPolygon),
aSdr3DObjectAttribute.getMaterial(),
aSdr3DObjectAttribute.getDoubleSided()));
aRetval[a] = xRef;
diff --git a/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
index 2993b1dd27c3..a7015ff8e578 100644
--- a/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
+++ b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx
@@ -312,26 +312,26 @@ namespace
// polygon is closed, one of the points is a member
const sal_uInt32 nPointCount(rPoly.count());
- if(nPointCount)
- {
- basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0));
- const basegfx::B2DVector aVector(rEnd - rStart);
+ if(!nPointCount)
+ return false;
- for(sal_uInt32 a(0); a < nPointCount; a++)
- {
- const sal_uInt32 nNextIndex((a + 1) % nPointCount);
- const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex));
- const basegfx::B2DVector aEdgeVector(aNext - aCurrent);
+ basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0));
+ const basegfx::B2DVector aVector(rEnd - rStart);
- if(basegfx::utils::findCut(
- rStart, aVector,
- aCurrent, aEdgeVector) != CutFlagValue::NONE)
- {
- return true;
- }
+ for(sal_uInt32 a(0); a < nPointCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+ const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex));
+ const basegfx::B2DVector aEdgeVector(aNext - aCurrent);
- aCurrent = aNext;
+ if(basegfx::utils::findCut(
+ rStart, aVector,
+ aCurrent, aEdgeVector) != CutFlagValue::NONE)
+ {
+ return true;
}
+
+ aCurrent = aNext;
}
return false;
diff --git a/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
index 919fbcf5ab4a..ed0e8d41c4ab 100644
--- a/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx
@@ -30,6 +30,7 @@
#include <drawinglayer/attribute/sdrfillattribute.hxx>
#include <drawinglayer/attribute/sdrlineattribute.hxx>
#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -375,7 +376,7 @@ namespace drawinglayer::primitive3d
// again when no longer geometry is needed for non-visible 3D objects as it is now for chart
if(getPolyPolygon().count() && maSlices.empty())
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
const_cast< SdrExtrudePrimitive3D& >(*this).impCreateSlices();
}
@@ -388,7 +389,7 @@ namespace drawinglayer::primitive3d
const basegfx::B2DVector& rTextureSize,
const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
- const basegfx::B2DPolyPolygon& rPolyPolygon,
+ basegfx::B2DPolyPolygon aPolyPolygon,
double fDepth,
double fDiagonal,
double fBackScale,
@@ -398,7 +399,7 @@ namespace drawinglayer::primitive3d
bool bCloseFront,
bool bCloseBack)
: SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
- maPolyPolygon(rPolyPolygon),
+ maPolyPolygon(std::move(aPolyPolygon)),
mfDepth(fDepth),
mfDiagonal(fDiagonal),
mfBackScale(fBackScale),
@@ -409,13 +410,13 @@ namespace drawinglayer::primitive3d
mbCloseBack(bCloseBack)
{
// make sure depth is positive
- if(basegfx::fTools::lessOrEqual(getDepth(), 0.0))
+ if(getDepth() <= 0.0)
{
mfDepth = 0.0;
}
// make sure the percentage value getDiagonal() is between 0.0 and 1.0
- if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
+ if(getDiagonal() <= 0.0)
{
mfDiagonal = 0.0;
}
@@ -480,7 +481,7 @@ namespace drawinglayer::primitive3d
(!getBuffered3DDecomposition().empty()
&& *mpLastRLGViewInformation != rViewInformation))
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
// conditions of last local decomposition with reduced lines have changed. Remember
// new one and clear current decompositiopn
diff --git a/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
index 2115ebc3abe7..682ea0c54042 100644
--- a/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx
@@ -26,6 +26,7 @@
#include <drawinglayer/attribute/sdrfillattribute.hxx>
#include <drawinglayer/attribute/sdrlineattribute.hxx>
#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -227,7 +228,7 @@ namespace drawinglayer::primitive3d
// again when no longer geometry is needed for non-visible 3D objects as it is now for chart
if(getPolyPolygon().count() && maSlices.empty())
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
const_cast< SdrLathePrimitive3D& >(*this).impCreateSlices();
}
@@ -240,7 +241,7 @@ namespace drawinglayer::primitive3d
const basegfx::B2DVector& rTextureSize,
const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute,
- const basegfx::B2DPolyPolygon& rPolyPolygon,
+ basegfx::B2DPolyPolygon aPolyPolygon,
sal_uInt32 nHorizontalSegments,
sal_uInt32 nVerticalSegments,
double fDiagonal,
@@ -252,7 +253,7 @@ namespace drawinglayer::primitive3d
bool bCloseFront,
bool bCloseBack)
: SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
- maPolyPolygon(rPolyPolygon),
+ maPolyPolygon(std::move(aPolyPolygon)),
mnHorizontalSegments(nHorizontalSegments),
mnVerticalSegments(nVerticalSegments),
mfDiagonal(fDiagonal),
@@ -265,13 +266,13 @@ namespace drawinglayer::primitive3d
mbCloseBack(bCloseBack)
{
// make sure Rotation is positive
- if(basegfx::fTools::lessOrEqual(getRotation(), 0.0))
+ if(getRotation() <= 0.0)
{
mfRotation = 0.0;
}
// make sure the percentage value getDiagonal() is between 0.0 and 1.0
- if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0))
+ if(getDiagonal() <= 0.0)
{
mfDiagonal = 0.0;
}
@@ -338,7 +339,7 @@ namespace drawinglayer::primitive3d
(!getBuffered3DDecomposition().empty()
&& *mpLastRLGViewInformation != rViewInformation))
{
- ::osl::MutexGuard aGuard( m_aMutex );
+ std::unique_lock aGuard( m_aMutex );
// conditions of last local decomposition with reduced lines have changed. Remember
// new one and clear current decompositiopn
diff --git a/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
index 010083b42291..9219cc970c86 100644
--- a/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx
@@ -24,6 +24,7 @@
#include <drawinglayer/attribute/sdrfillattribute.hxx>
#include <drawinglayer/attribute/sdrlineattribute.hxx>
#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -117,13 +118,13 @@ namespace drawinglayer::primitive3d
}
SdrPolyPolygonPrimitive3D::SdrPolyPolygonPrimitive3D(
- const basegfx::B3DPolyPolygon& rPolyPolygon3D,
+ basegfx::B3DPolyPolygon aPolyPolygon3D,
const basegfx::B3DHomMatrix& rTransform,
const basegfx::B2DVector& rTextureSize,
const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
: SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute),
- maPolyPolygon3D(rPolyPolygon3D)
+ maPolyPolygon3D(std::move(aPolyPolygon3D))
{
}
diff --git a/drawinglayer/source/primitive3d/sdrprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
index 54fd2cf4b206..b4007f1a8705 100644
--- a/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx
@@ -20,6 +20,7 @@
#include <drawinglayer/primitive3d/sdrprimitive3d.hxx>
#include <basegfx/polygon/b3dpolypolygontools.hxx>
#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -75,13 +76,13 @@ namespace drawinglayer::primitive3d
}
SdrPrimitive3D::SdrPrimitive3D(
- const basegfx::B3DHomMatrix& rTransform,
+ basegfx::B3DHomMatrix aTransform,
const basegfx::B2DVector& rTextureSize,
- const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute,
+ attribute::SdrLineFillShadowAttribute3D aSdrLFSAttribute,
const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute)
- : maTransform(rTransform),
+ : maTransform(std::move(aTransform)),
maTextureSize(rTextureSize),
- maSdrLFSAttribute(rSdrLFSAttribute),
+ maSdrLFSAttribute(std::move(aSdrLFSAttribute)),
maSdr3DObjectAttribute(rSdr3DObjectAttribute)
{
}
diff --git a/drawinglayer/source/primitive3d/shadowprimitive3d.cxx b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
index cca2e3c6f07f..3c3a07ef7a2f 100644
--- a/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx
@@ -19,6 +19,7 @@
#include <primitive3d/shadowprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -27,13 +28,13 @@ using namespace com::sun::star;
namespace drawinglayer::primitive3d
{
ShadowPrimitive3D::ShadowPrimitive3D(
- const basegfx::B2DHomMatrix& rShadowTransform,
+ basegfx::B2DHomMatrix aShadowTransform,
const basegfx::BColor& rShadowColor,
double fShadowTransparence,
bool bShadow3D,
const Primitive3DContainer& rChildren)
: GroupPrimitive3D(rChildren),
- maShadowTransform(rShadowTransform),
+ maShadowTransform(std::move(aShadowTransform)),
maShadowColor(rShadowColor),
mfShadowTransparence(fShadowTransparence),
mbShadow3D(bShadow3D)
diff --git a/drawinglayer/source/primitive3d/textureprimitive3d.cxx b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
index 7c9226bf40de..ceeca0489487 100644
--- a/drawinglayer/source/primitive3d/textureprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/textureprimitive3d.cxx
@@ -20,6 +20,8 @@
#include <primitive3d/textureprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
#include <basegfx/color/bcolor.hxx>
+#include <basegfx/utils/gradienttools.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -91,7 +93,13 @@ namespace drawinglayer::primitive3d
{
// create TransparenceTexturePrimitive3D with fixed transparence as replacement
const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence());
- const attribute::FillGradientAttribute aFillGradient(attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 0.0, aGray, aGray);
+
+ // create ColorStops with StartColor == EndColor == aGray
+ const basegfx::BColorStops aColorStops {
+ basegfx::BColorStop(0.0, aGray),
+ basegfx::BColorStop(1.0, aGray) };
+
+ const attribute::FillGradientAttribute aFillGradient(css::awt::GradientStyle_LINEAR, 0.0, 0.0, 0.0, 0.0, aColorStops);
const Primitive3DReference xRef(new TransparenceTexturePrimitive3D(aFillGradient, getChildren(), getTextureSize()));
return { xRef };
}
@@ -108,13 +116,13 @@ namespace drawinglayer::primitive3d
GradientTexturePrimitive3D::GradientTexturePrimitive3D(
- const attribute::FillGradientAttribute& rGradient,
+ attribute::FillGradientAttribute aGradient,
const Primitive3DContainer& rChildren,
const basegfx::B2DVector& rTextureSize,
bool bModulate,
bool bFilter)
: TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter),
- maGradient(rGradient)
+ maGradient(std::move(aGradient))
{
}
diff --git a/drawinglayer/source/primitive3d/transformprimitive3d.cxx b/drawinglayer/source/primitive3d/transformprimitive3d.cxx
index 1ddb919bf2e1..c7b926d8dc18 100644
--- a/drawinglayer/source/primitive3d/transformprimitive3d.cxx
+++ b/drawinglayer/source/primitive3d/transformprimitive3d.cxx
@@ -19,6 +19,7 @@
#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -27,10 +28,10 @@ using namespace com::sun::star;
namespace drawinglayer::primitive3d
{
TransformPrimitive3D::TransformPrimitive3D(
- const basegfx::B3DHomMatrix& rTransformation,
+ basegfx::B3DHomMatrix aTransformation,
const Primitive3DContainer& rChildren)
: GroupPrimitive3D(rChildren),
- maTransformation(rTransformation)
+ maTransformation(std::move(aTransformation))
{
}
diff --git a/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx b/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx
new file mode 100644
index 000000000000..7d001cfd67fd
--- /dev/null
+++ b/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/graph.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
+
+namespace drawinglayer::processor2d
+{
+void setOffsetXYCreatedBitmap(
+ drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ const BitmapEx& rBitmap)
+{
+ rFillGraphicPrimitive2D.impSetOffsetXYCreatedBitmap(rBitmap);
+}
+
+void takeCareOfOffsetXY(
+ const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ BitmapEx& rTarget, basegfx::B2DRange& rFillUnitRange)
+{
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute(
+ rFillGraphicPrimitive2D.getFillGraphic());
+ const bool bOffsetXIsUsed(rFillGraphicAttribute.getOffsetX() > 0.0
+ && rFillGraphicAttribute.getOffsetX() < 1.0);
+ const bool bOffsetYIsUsed(rFillGraphicAttribute.getOffsetY() > 0.0
+ && rFillGraphicAttribute.getOffsetY() < 1.0);
+
+ if (bOffsetXIsUsed)
+ {
+ if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
+ {
+ const Size& rSize(rTarget.GetSizePixel());
+ const tools::Long w(rSize.Width());
+ const tools::Long a(
+ basegfx::fround<tools::Long>(w * (1.0 - rFillGraphicAttribute.getOffsetX())));
+
+ if (0 != a && w != a)
+ {
+ const tools::Long h(rSize.Height());
+ const tools::Long b(w - a);
+ BitmapEx aTarget(Size(w, h * 2), rTarget.getPixelFormat());
+
+ aTarget.SetPrefSize(
+ Size(rTarget.GetPrefSize().Width(), rTarget.GetPrefSize().Height() * 2));
+ const tools::Rectangle aSrcDst(Point(), rSize);
+ aTarget.CopyPixel(aSrcDst, // Dst
+ aSrcDst, // Src
+ rTarget);
+ const Size aSizeA(b, h);
+ aTarget.CopyPixel(tools::Rectangle(Point(0, h), aSizeA), // Dst
+ tools::Rectangle(Point(a, 0), aSizeA), // Src
+ rTarget);
+ const Size aSizeB(a, h);
+ aTarget.CopyPixel(tools::Rectangle(Point(b, h), aSizeB), // Dst
+ tools::Rectangle(Point(), aSizeB), // Src
+ rTarget);
+
+ setOffsetXYCreatedBitmap(
+ const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>(
+ rFillGraphicPrimitive2D),
+ aTarget);
+ }
+ }
+
+ if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
+ {
+ rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap();
+ rFillUnitRange.expand(basegfx::B2DPoint(
+ rFillUnitRange.getMinX(), rFillUnitRange.getMaxY() + rFillUnitRange.getHeight()));
+ }
+ }
+ else if (bOffsetYIsUsed)
+ {
+ if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
+ {
+ const Size& rSize(rTarget.GetSizePixel());
+ const tools::Long h(rSize.Height());
+ const tools::Long a(
+ basegfx::fround<tools::Long>(h * (1.0 - rFillGraphicAttribute.getOffsetY())));
+
+ if (0 != a && h != a)
+ {
+ const tools::Long w(rSize.Width());
+ const tools::Long b(h - a);
+ BitmapEx aTarget(Size(w * 2, h), rTarget.getPixelFormat());
+
+ aTarget.SetPrefSize(
+ Size(rTarget.GetPrefSize().Width() * 2, rTarget.GetPrefSize().Height()));
+ const tools::Rectangle aSrcDst(Point(), rSize);
+ aTarget.CopyPixel(aSrcDst, // Dst
+ aSrcDst, // Src
+ rTarget);
+ const Size aSizeA(w, b);
+ aTarget.CopyPixel(tools::Rectangle(Point(w, 0), aSizeA), // Dst
+ tools::Rectangle(Point(0, a), aSizeA), // Src
+ rTarget);
+ const Size aSizeB(w, a);
+ aTarget.CopyPixel(tools::Rectangle(Point(w, b), aSizeB), // Dst
+ tools::Rectangle(Point(), aSizeB), // Src
+ rTarget);
+
+ setOffsetXYCreatedBitmap(
+ const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>(
+ rFillGraphicPrimitive2D),
+ aTarget);
+ }
+ }
+
+ if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
+ {
+ rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap();
+ rFillUnitRange.expand(basegfx::B2DPoint(
+ rFillUnitRange.getMaxX() + rFillUnitRange.getWidth(), rFillUnitRange.getMinY()));
+ }
+ }
+}
+
+bool prepareBitmapForDirectRender(
+ const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, BitmapEx& rTarget,
+ basegfx::B2DRange& rFillUnitRange, double fBigDiscreteArea)
+{
+ const attribute::FillGraphicAttribute& rFillGraphicAttribute(
+ rFillGraphicPrimitive2D.getFillGraphic());
+ const Graphic& rGraphic(rFillGraphicAttribute.getGraphic());
+
+ if (rFillGraphicAttribute.isDefault() || rGraphic.IsNone())
+ {
+ // default attributes or GraphicType::NONE, so no fill .-> done
+ return false;
+ }
+
+ if (!rFillGraphicAttribute.getTiling())
+ {
+ // If no tiling used, the Graphic will need to be painted just once. This
+ // is perfectly done using the decomposition, so use it.
+ // What we want to do here is to optimize tiled paint, for two reasons:
+ // (a) speed: draw one tile, repeat -> obvious
+ // (b) correctness: not so obvious, but since in AAed paint the same edge
+ // of touching polygons both AAed do *not* sum up, but get blended by
+ // multiplication (0.5 * 0.5 -> 0.25) the connection will stay visible,
+ // not only with filled polygons, but also with bitmaps
+ // Signal that paint is needed
+ return true;
+ }
+
+ if (rFillUnitRange.isEmpty())
+ {
+ // no fill range definition, no fill, done
+ return false;
+ }
+
+ const basegfx::B2DHomMatrix aLocalTransform(rViewInformation2D.getObjectToViewTransformation()
+ * rFillGraphicPrimitive2D.getTransformation());
+ const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport());
+
+ if (!rDiscreteViewPort.isEmpty())
+ {
+ // calculate discrete covered pixel area
+ basegfx::B2DRange aDiscreteRange(basegfx::B2DRange::getUnitB2DRange());
+ aDiscreteRange.transform(aLocalTransform);
+
+ if (!rDiscreteViewPort.overlaps(rDiscreteViewPort))
+ {
+ // we have a Viewport and visible range of geometry is outside -> not visible, done
+ return false;
+ }
+ }
+
+ if (GraphicType::Bitmap == rGraphic.GetType() && rGraphic.IsAnimated())
+ {
+ // Need to prepare specialized AnimatedGraphicPrimitive2D,
+ // cannot handle here. Signal that paint is needed
+ return true;
+ }
+
+ if (GraphicType::Bitmap == rGraphic.GetType() && !rGraphic.getVectorGraphicData())
+ {
+ // bitmap graphic, always handle locally, so get bitmap data independent
+ // if it'sie or it's discrete display size
+ rTarget = rGraphic.GetBitmapEx();
+ }
+ else
+ {
+ // Vector Graphic Data fill, including metafile:
+ // We can know about discrete pixel size here, calculate and use it.
+ // To do so, using Vectors is sufficient to get the lengths. It is
+ // not necessary to transform the whole target coordinate system.
+ const basegfx::B2DVector aDiscreteXAxis(
+ aLocalTransform
+ * basegfx::B2DVector(rFillUnitRange.getMaxX() - rFillUnitRange.getMinX(), 0.0));
+ const basegfx::B2DVector aDiscreteYAxis(
+ aLocalTransform
+ * basegfx::B2DVector(0.0, rFillUnitRange.getMaxY() - rFillUnitRange.getMinY()));
+
+ // get and ensure minimal size
+ const double fDiscreteWidth(std::max(1.0, aDiscreteXAxis.getLength()));
+ const double fDiscreteHeight(std::max(1.0, aDiscreteYAxis.getLength()));
+
+ // compare with a big visualization size in discrete pixels
+ const double fTargetDiscreteArea(fDiscreteWidth * fDiscreteHeight);
+
+ if (fTargetDiscreteArea > fBigDiscreteArea)
+ {
+ // When the vector data is visualized big it is better to not handle here
+ // but use decomposition fallback which then will visualize the vector data
+ // directly -> better quality, acceptable number of tile repeat(s)
+ // signal that paint is needed
+ return true;
+ }
+ else
+ {
+ // If visualized small, the amount of repeated fills gets expensive, so
+ // in that case use a Bitmap and the Brush technique below.
+ // The Bitmap may be created here exactly for the needed target size
+ // (using local D2DBitmapPixelProcessor2D and the vector data),
+ // but since we have a HW renderer and re-use of system-dependent data
+ // at BitmapEx is possible, just get the default fallback Bitmap from the
+ // vector data to continue. Trust the existing converters for now to
+ // do something with good quality.
+ rTarget = rGraphic.GetBitmapEx();
+ }
+ }
+
+ if (rTarget.IsEmpty() || rTarget.GetSizePixel().IsEmpty())
+ {
+ // no pixel data, done
+ return false;
+ }
+
+ // react if OffsetX/OffsetY of the FillGraphicAttribute is used
+ takeCareOfOffsetXY(rFillGraphicPrimitive2D, rTarget, rFillUnitRange);
+
+#ifdef DBG_UTIL
+ // allow to check bitmap data, e.g. control OffsetX/OffsetY stuff
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_getreplacement.png",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ vcl::PngImageWriter aPNGWriter(aNew);
+ aPNGWriter.write(rTarget);
+ }
+ }
+#endif
+
+ // signal to render it
+ return true;
+}
+
+void calculateDiscreteVisibleRange(
+ basegfx::B2DRange& rDiscreteVisibleRange, const basegfx::B2DRange& rContentRange,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+{
+ if (rContentRange.isEmpty())
+ {
+ // no content, done
+ rDiscreteVisibleRange.reset();
+ return;
+ }
+
+ basegfx::B2DRange aDiscreteRange(rContentRange);
+ aDiscreteRange.transform(rViewInformation2D.getObjectToViewTransformation());
+ const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport());
+ rDiscreteVisibleRange = aDiscreteRange;
+
+ if (!rDiscreteViewPort.isEmpty())
+ {
+ rDiscreteVisibleRange.intersect(rDiscreteViewPort);
+ }
+}
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/baseprocessor2d.cxx b/drawinglayer/source/processor2d/baseprocessor2d.cxx
index a078fe882e9e..13d6b4c63b08 100644
--- a/drawinglayer/source/processor2d/baseprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/baseprocessor2d.cxx
@@ -17,8 +17,9 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
-#include <comphelper/sequence.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -30,8 +31,8 @@ namespace drawinglayer::processor2d
{
}
- BaseProcessor2D::BaseProcessor2D(const geometry::ViewInformation2D& rViewInformation)
- : maViewInformation2D(rViewInformation)
+ BaseProcessor2D::BaseProcessor2D(geometry::ViewInformation2D aViewInformation)
+ : maViewInformation2D(std::move(aViewInformation))
{
}
@@ -46,16 +47,16 @@ namespace drawinglayer::processor2d
}
// Primitive2DDecompositionVisitor
- void BaseProcessor2D::append(const primitive2d::Primitive2DReference& rCandidate)
+ void BaseProcessor2D::visit(const primitive2d::Primitive2DReference& rCandidate)
{
- const primitive2d::BasePrimitive2D* pBasePrimitive = static_cast< const primitive2d::BasePrimitive2D* >(rCandidate.get());
- processBasePrimitive2D(*pBasePrimitive);
+ if (rCandidate)
+ processBasePrimitive2D(*rCandidate);
}
- void BaseProcessor2D::append(const primitive2d::Primitive2DContainer& rContainer)
+ void BaseProcessor2D::visit(const primitive2d::Primitive2DContainer& rContainer)
{
process(rContainer);
}
- void BaseProcessor2D::append(primitive2d::Primitive2DContainer&& rCandidate)
+ void BaseProcessor2D::visit(primitive2d::Primitive2DContainer&& rCandidate)
{
process(rCandidate);
}
@@ -64,9 +65,8 @@ namespace drawinglayer::processor2d
{
for (const primitive2d::Primitive2DReference& rCandidate : rSource)
{
- const primitive2d::BasePrimitive2D* pBasePrimitive = static_cast< const primitive2d::BasePrimitive2D* >(rCandidate.get());
- if (pBasePrimitive)
- processBasePrimitive2D(*pBasePrimitive);
+ if (rCandidate)
+ processBasePrimitive2D(*rCandidate);
}
}
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
new file mode 100644
index 000000000000..89cb21ddcb74
--- /dev/null
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -0,0 +1,975 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/processor2d/cairopixelprocessor2d.hxx>
+#include <sal/log.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/cairo.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+basegfx::B2DPoint impPixelSnap(const basegfx::B2DPolygon& rPolygon,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation,
+ sal_uInt32 nIndex)
+{
+ const sal_uInt32 nCount(rPolygon.count());
+
+ // get the data
+ const basegfx::B2ITuple aPrevTuple(
+ basegfx::fround(rViewInformation.getObjectToViewTransformation()
+ * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
+ const basegfx::B2DPoint aCurrPoint(rViewInformation.getObjectToViewTransformation()
+ * rPolygon.getB2DPoint(nIndex));
+ const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+ const basegfx::B2ITuple aNextTuple(
+ basegfx::fround(rViewInformation.getObjectToViewTransformation()
+ * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
+
+ // get the states
+ const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+ const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+ const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+ const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+ const bool bSnapX(bPrevVertical || bNextVertical);
+ const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+ if (bSnapX || bSnapY)
+ {
+ basegfx::B2DPoint aSnappedPoint(bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
+ bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
+
+ aSnappedPoint *= rViewInformation.getInverseObjectToViewTransformation();
+
+ return aSnappedPoint;
+ }
+
+ return rPolygon.getB2DPoint(nIndex);
+}
+
+void addB2DPolygonToPathGeometry(cairo_t* cr, const basegfx::B2DPolygon& rPolygon,
+ const drawinglayer::geometry::ViewInformation2D* pViewInformation)
+{
+ // short circuit if there is nothing to do
+ const sal_uInt32 nPointCount(rPolygon.count());
+
+ const bool bHasCurves(rPolygon.areControlPointsUsed());
+ const bool bClosePath(rPolygon.isClosed());
+ basegfx::B2DPoint aLast;
+
+ for (sal_uInt32 nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++)
+ {
+ int nClosedIdx = nPointIdx;
+ if (nPointIdx >= nPointCount)
+ {
+ // prepare to close last curve segment if needed
+ if (bClosePath && (nPointIdx == nPointCount))
+ {
+ nClosedIdx = 0;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ const basegfx::B2DPoint aPoint(nullptr == pViewInformation
+ ? rPolygon.getB2DPoint(nClosedIdx)
+ : impPixelSnap(rPolygon, *pViewInformation, nClosedIdx));
+
+ if (!nPointIdx)
+ {
+ // first point => just move there
+ cairo_move_to(cr, aPoint.getX(), aPoint.getY());
+ aLast = aPoint;
+ continue;
+ }
+
+ bool bPendingCurve(false);
+
+ if (bHasCurves)
+ {
+ bPendingCurve = rPolygon.isNextControlPointUsed(nPrevIdx);
+ bPendingCurve |= rPolygon.isPrevControlPointUsed(nClosedIdx);
+ }
+
+ if (!bPendingCurve) // line segment
+ {
+ cairo_line_to(cr, aPoint.getX(), aPoint.getY());
+ }
+ else // cubic bezier segment
+ {
+ basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint(nPrevIdx);
+ basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint(nClosedIdx);
+
+ // tdf#99165 if the control points are 'empty', create the mathematical
+ // correct replacement ones to avoid problems with the graphical sub-system
+ // tdf#101026 The 1st attempt to create a mathematically correct replacement control
+ // vector was wrong. Best alternative is one as close as possible which means short.
+ if (aCP1.equal(aLast))
+ {
+ aCP1 = aLast + ((aCP2 - aLast) * 0.0005);
+ }
+
+ if (aCP2.equal(aPoint))
+ {
+ aCP2 = aPoint + ((aCP1 - aPoint) * 0.0005);
+ }
+
+ cairo_curve_to(cr, aCP1.getX(), aCP1.getY(), aCP2.getX(), aCP2.getY(), aPoint.getX(),
+ aPoint.getY());
+ }
+
+ aLast = aPoint;
+ }
+
+ if (bClosePath)
+ {
+ cairo_close_path(cr);
+ }
+}
+
+// split alpha remains as a constant irritant
+std::vector<sal_uInt8> createBitmapData(const BitmapEx& rBitmapEx)
+{
+ const Size& rSizePixel(rBitmapEx.GetSizePixel());
+ const bool bAlpha(rBitmapEx.IsAlpha());
+ const sal_uInt32 nStride
+ = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, rSizePixel.Width());
+ std::vector<sal_uInt8> aData(nStride * rSizePixel.Height());
+
+ if (bAlpha)
+ {
+ Bitmap aSrcAlpha(rBitmapEx.GetAlphaMask().GetBitmap());
+ BitmapScopedReadAccess pReadAccess(rBitmapEx.GetBitmap());
+ BitmapScopedReadAccess pAlphaReadAccess(aSrcAlpha);
+ const tools::Long nHeight(pReadAccess->Height());
+ const tools::Long nWidth(pReadAccess->Width());
+
+ for (tools::Long y = 0; y < nHeight; ++y)
+ {
+ unsigned char* pPixelData = aData.data() + (nStride * y);
+ for (tools::Long x = 0; x < nWidth; ++x)
+ {
+ const BitmapColor aColor(pReadAccess->GetColor(y, x));
+ const BitmapColor aAlpha(pAlphaReadAccess->GetColor(y, x));
+ const sal_uInt16 nAlpha(255 - aAlpha.GetRed());
+
+ pPixelData[SVP_CAIRO_RED] = vcl::bitmap::premultiply(nAlpha, aColor.GetRed());
+ pPixelData[SVP_CAIRO_GREEN] = vcl::bitmap::premultiply(nAlpha, aColor.GetGreen());
+ pPixelData[SVP_CAIRO_BLUE] = vcl::bitmap::premultiply(nAlpha, aColor.GetBlue());
+ pPixelData[SVP_CAIRO_ALPHA] = nAlpha;
+ pPixelData += 4;
+ }
+ }
+ }
+ else
+ {
+ BitmapScopedReadAccess pReadAccess(rBitmapEx.GetBitmap());
+ const tools::Long nHeight(pReadAccess->Height());
+ const tools::Long nWidth(pReadAccess->Width());
+
+ for (tools::Long y = 0; y < nHeight; ++y)
+ {
+ unsigned char* pPixelData = aData.data() + (nStride * y);
+ for (tools::Long x = 0; x < nWidth; ++x)
+ {
+ const BitmapColor aColor(pReadAccess->GetColor(y, x));
+ pPixelData[SVP_CAIRO_RED] = aColor.GetRed();
+ pPixelData[SVP_CAIRO_GREEN] = aColor.GetGreen();
+ pPixelData[SVP_CAIRO_BLUE] = aColor.GetBlue();
+ pPixelData[SVP_CAIRO_ALPHA] = 255;
+ pPixelData += 4;
+ }
+ }
+ }
+
+ return aData;
+}
+}
+
+namespace drawinglayer::processor2d
+{
+CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation)
+ : BaseProcessor2D(rViewInformation)
+ , maBColorModifierStack()
+ , mpRT(nullptr)
+{
+}
+
+CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ cairo_surface_t* pTarget)
+ : BaseProcessor2D(rViewInformation)
+ , maBColorModifierStack()
+ , mpRT(nullptr)
+{
+ if (pTarget)
+ {
+ cairo_t* pRT = cairo_create(pTarget);
+ cairo_set_antialias(pRT, rViewInformation.getUseAntiAliasing() ? CAIRO_ANTIALIAS_DEFAULT
+ : CAIRO_ANTIALIAS_NONE);
+ setRenderTarget(pRT);
+ }
+}
+
+CairoPixelProcessor2D::~CairoPixelProcessor2D()
+{
+ if (mpRT)
+ cairo_destroy(mpRT);
+}
+
+void CairoPixelProcessor2D::processPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D)
+{
+ const basegfx::B2DPolygon& rPolygon(rPolygonHairlinePrimitive2D.getB2DPolygon());
+
+ if (!rPolygon.count())
+ return;
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset);
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rPolygonHairlinePrimitive2D.getBColor()));
+ cairo_set_source_rgb(mpRT, aHairlineColor.getRed(), aHairlineColor.getGreen(),
+ aHairlineColor.getBlue());
+
+ // TODO: Unfortunately Direct2D paint of one pixel wide lines does not
+ // correctly and completely blend 100% over the background. Experimenting
+ // shows that a value around/slightly below 2.0 is needed which hints that
+ // alpha blending the half-shifted lines (see fAAOffset above) is involved.
+ // To get correct blending I try to use just wider hairlines for now. This
+ // may need to be improved - or balanced (trying sqrt(2) now...)
+ cairo_set_line_width(mpRT, 1.44f);
+
+ addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+ cairo_stroke(mpRT);
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processPolyPolygonColorPrimitive2D(
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D)
+{
+ const basegfx::B2DPolyPolygon& rPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon());
+ const sal_uInt32 nCount(rPolyPolygon.count());
+
+ if (!nCount)
+ return;
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset);
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ const basegfx::BColor aFillColor(
+ maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor()));
+ cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue());
+
+ for (const auto& rPolygon : rPolyPolygon)
+ addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+ cairo_fill(mpRT);
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processBitmapPrimitive2D(
+ const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
+{
+ // check if graphic content is inside discrete local ViewPort
+ const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
+ const basegfx::B2DHomMatrix aLocalTransform(
+ getViewInformation2D().getObjectToViewTransformation() * rBitmapCandidate.getTransform());
+
+ if (!rDiscreteViewPort.isEmpty())
+ {
+ basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+
+ aUnitRange.transform(aLocalTransform);
+
+ if (!aUnitRange.overlaps(rDiscreteViewPort))
+ {
+ // content is outside discrete local ViewPort
+ return;
+ }
+ }
+
+ BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
+
+ if (aBitmapEx.IsEmpty() || aBitmapEx.GetSizePixel().IsEmpty())
+ {
+ return;
+ }
+
+ if (maBColorModifierStack.count())
+ {
+ aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+
+ if (aBitmapEx.IsEmpty())
+ {
+ // color gets completely replaced, get it
+ const basegfx::BColor aModifiedColor(
+ maBColorModifierStack.getModifiedColor(basegfx::BColor()));
+
+ // use unit geometry as fallback object geometry. Do *not*
+ // transform, the below used method will use the already
+ // correctly initialized local ViewInformation
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+
+ rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> xTemp(
+ new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon),
+ aModifiedColor));
+ processPolyPolygonColorPrimitive2D(*xTemp);
+ return;
+ }
+ }
+
+ // nasty copy of bitmap data
+ std::vector<sal_uInt8> aPixelData(createBitmapData(aBitmapEx));
+ const Size& rSizePixel(aBitmapEx.GetSizePixel());
+ cairo_surface_t* pBitmapSurface = cairo_image_surface_create_for_data(
+ aPixelData.data(), CAIRO_FORMAT_ARGB32, rSizePixel.Width(), rSizePixel.Height(),
+ cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, rSizePixel.Width()));
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ cairo_matrix_init(&aMatrix, aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(),
+ aLocalTransform.d(), aLocalTransform.e() + fAAOffset,
+ aLocalTransform.f() + fAAOffset);
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ // destinationRectangle is part of transformation above, so use UnitRange
+ cairo_rectangle(mpRT, 0, 0, 1, 1);
+ cairo_clip(mpRT);
+
+ cairo_set_source_surface(mpRT, pBitmapSurface, 0, 0);
+ // get the pattern created by cairo_set_source_surface
+ cairo_pattern_t* sourcepattern = cairo_get_source(mpRT);
+ cairo_pattern_get_matrix(sourcepattern, &aMatrix);
+ // scale to match the current transformation
+ cairo_matrix_scale(&aMatrix, rSizePixel.Width(), rSizePixel.Height());
+ cairo_pattern_set_matrix(sourcepattern, &aMatrix);
+
+ cairo_paint(mpRT);
+
+ cairo_surface_destroy(pBitmapSurface);
+
+ cairo_restore(mpRT);
+}
+
+namespace
+{
+// This bit-tweaking looping is unpleasant and unfortunate
+void LuminanceToAlpha(cairo_surface_t* pMask)
+{
+ cairo_surface_flush(pMask);
+
+ int nWidth = cairo_image_surface_get_width(pMask);
+ int nHeight = cairo_image_surface_get_height(pMask);
+ int nStride = cairo_image_surface_get_stride(pMask);
+ unsigned char* mask_surface_data = cairo_image_surface_get_data(pMask);
+
+ // include/basegfx/color/bcolormodifier.hxx
+ const double nRedMul = 0.2125 / 255.0;
+ const double nGreenMul = 0.7154 / 255.0;
+ const double nBlueMul = 0.0721 / 255.0;
+ for (int y = 0; y < nHeight; ++y)
+ {
+ unsigned char* pMaskPixelData = mask_surface_data + (nStride * y);
+ for (int x = 0; x < nWidth; ++x)
+ {
+ double fLuminance = pMaskPixelData[SVP_CAIRO_RED] * nRedMul
+ + pMaskPixelData[SVP_CAIRO_GREEN] * nGreenMul
+ + pMaskPixelData[SVP_CAIRO_BLUE] * nBlueMul;
+ // Only this alpha channel is taken into account by cairo_mask_surface
+ // so reuse this surface for the alpha result
+ pMaskPixelData[SVP_CAIRO_ALPHA] = 255.0 * fLuminance;
+ pMaskPixelData += 4;
+ }
+ }
+
+ cairo_surface_mark_dirty(pMask);
+}
+}
+
+void CairoPixelProcessor2D::processTransparencePrimitive2D(
+ const primitive2d::TransparencePrimitive2D& rTransCandidate)
+{
+ if (rTransCandidate.getChildren().empty())
+ return;
+
+ if (rTransCandidate.getTransparence().empty())
+ return;
+
+ cairo_surface_t* pTarget = cairo_get_target(mpRT);
+
+ double clip_x1, clip_x2, clip_y1, clip_y2;
+ cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
+
+ // calculate visible range, create only for that range
+ basegfx::B2DRange aDiscreteRange(
+ rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
+ aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation());
+ const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1),
+ basegfx::B2DPoint(clip_x2, clip_y2));
+ basegfx::B2DRange aVisibleRange(aDiscreteRange);
+ aVisibleRange.intersect(aViewRange);
+
+ if (aVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return;
+ }
+
+ const basegfx::B2DHomMatrix aEmbedTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aVisibleRange.getMinX(), -aVisibleRange.getMinY()));
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setViewTransformation(aEmbedTransform
+ * getViewInformation2D().getViewTransformation());
+ // draw mask to temporary surface
+ cairo_surface_t* pMask = cairo_surface_create_similar_image(pTarget, CAIRO_FORMAT_ARGB32,
+ ceil(aVisibleRange.getWidth()),
+ ceil(aVisibleRange.getHeight()));
+ CairoPixelProcessor2D aMaskRenderer(aViewInformation2D, pMask);
+ aMaskRenderer.process(rTransCandidate.getTransparence());
+
+ // convert mask to something cairo can use
+ LuminanceToAlpha(pMask);
+
+ // draw content to temporary surface
+ cairo_surface_t* pContent = cairo_surface_create_similar(
+ pTarget, cairo_surface_get_content(pTarget), ceil(aVisibleRange.getWidth()),
+ ceil(aVisibleRange.getHeight()));
+ CairoPixelProcessor2D aContent(aViewInformation2D, pContent);
+ aContent.process(rTransCandidate.getChildren());
+
+ // munge the temporary surfaces to our target surface
+ cairo_set_source_surface(mpRT, pContent, aVisibleRange.getMinX(), aVisibleRange.getMinY());
+ cairo_mask_surface(mpRT, pMask, aVisibleRange.getMinX(), aVisibleRange.getMinY());
+
+ cairo_surface_destroy(pContent);
+ cairo_surface_destroy(pMask);
+}
+
+void CairoPixelProcessor2D::processMaskPrimitive2DPixel(
+ const primitive2d::MaskPrimitive2D& rMaskCandidate)
+{
+ if (rMaskCandidate.getChildren().empty())
+ return;
+
+ basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
+
+ if (!aMask.count())
+ return;
+
+ double clip_x1, clip_x2, clip_y1, clip_y2;
+ cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
+
+ basegfx::B2DRange aMaskRange(aMask.getB2DRange());
+ aMaskRange.transform(getViewInformation2D().getObjectToViewTransformation());
+ const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1),
+ basegfx::B2DPoint(clip_x2, clip_y2));
+
+ if (!aViewRange.overlaps(aMaskRange))
+ return;
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e(), rObjectToView.f());
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ // put mask as path
+ for (const auto& rPolygon : aMask)
+ addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+ // clip to this mask
+ cairo_clip(mpRT);
+
+ process(rMaskCandidate.getChildren());
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processPointArrayPrimitive2D(
+ const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
+{
+ const std::vector<basegfx::B2DPoint>& rPositions(rPointArrayCandidate.getPositions());
+ if (rPositions.empty())
+ return;
+
+ const basegfx::BColor aPointColor(
+ maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
+ cairo_set_source_rgb(mpRT, aPointColor.getRed(), aPointColor.getGreen(), aPointColor.getBlue());
+
+ // To really paint a single pixel I found nothing better than
+ // switch off AA and draw a pixel-aligned rectangle
+ const cairo_antialias_t eOldAAMode(cairo_get_antialias(mpRT));
+ cairo_set_antialias(mpRT, CAIRO_ANTIALIAS_NONE);
+
+ for (auto const& pos : rPositions)
+ {
+ const basegfx::B2DPoint aDiscretePos(getViewInformation2D().getObjectToViewTransformation()
+ * pos);
+ const double fX(ceil(aDiscretePos.getX()));
+ const double fY(ceil(aDiscretePos.getY()));
+
+ cairo_rectangle(mpRT, fX, fY, 1, 1);
+ cairo_fill(mpRT);
+ }
+
+ cairo_set_antialias(mpRT, eOldAAMode);
+}
+
+void CairoPixelProcessor2D::processModifiedColorPrimitive2D(
+ const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
+{
+ if (!rModifiedCandidate.getChildren().empty())
+ {
+ maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
+ process(rModifiedCandidate.getChildren());
+ maBColorModifierStack.pop();
+ }
+}
+
+void CairoPixelProcessor2D::processTransformPrimitive2D(
+ const primitive2d::TransformPrimitive2D& rTransformCandidate)
+{
+ // remember current transformation and ViewInformation
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new transformations for local ViewInformation2D
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
+ * rTransformCandidate.getTransformation());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+}
+
+void CairoPixelProcessor2D::processPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
+{
+ const basegfx::B2DPolygon& rPolygon(rPolygonStrokeCandidate.getB2DPolygon());
+ const attribute::LineAttribute& rLineAttribute(rPolygonStrokeCandidate.getLineAttribute());
+
+ if (!rPolygon.count() || rLineAttribute.getWidth() < 0.0)
+ {
+ // no geometry, done
+ return;
+ }
+
+ // get some values early that might be used for decisions
+ const bool bHairline(0.0 == rLineAttribute.getWidth());
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ const double fDiscreteLineWidth(
+ bHairline
+ ? 1.0
+ : (rObjectToView * basegfx::B2DVector(rLineAttribute.getWidth(), 0.0)).getLength());
+
+ // Here for every combination which the system-specific implementation is not
+ // capable of visualizing, use the (for decomposable Primitives always possible)
+ // fallback to the decomposition.
+ if (basegfx::B2DLineJoin::NONE == rLineAttribute.getLineJoin() && fDiscreteLineWidth > 1.5)
+ {
+ // basegfx::B2DLineJoin::NONE is special for our office, no other GraphicSystem
+ // knows that (so far), so fallback to decomposition. This is only needed if
+ // LineJoin will be used, so also check for discrete LineWidth before falling back
+ process(rPolygonStrokeCandidate);
+ return;
+ }
+
+ // This is a method every system-specific implementation of a decomposable Primitive
+ // can use to allow simple optical control of paint implementation:
+ // Create a copy, e.g. change color to 'red' as here and paint before the system
+ // paints it using the decomposition. That way you can - if active - directly
+ // optically compare if the system-specific solution is geometrically identical to
+ // the decomposition (which defines our interpretation that we need to visualize).
+ // Look below in the impl for bRenderDecomposeForCompareInRed to see that in that case
+ // we create a half-transparent paint to better support visual control
+ static bool bRenderDecomposeForCompareInRed(false);
+
+ if (bRenderDecomposeForCompareInRed)
+ {
+ const attribute::LineAttribute aRed(
+ basegfx::BColor(1.0, 0.0, 0.0), rLineAttribute.getWidth(), rLineAttribute.getLineJoin(),
+ rLineAttribute.getLineCap(), rLineAttribute.getMiterMinimumAngle());
+ rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xCopy(
+ new primitive2d::PolygonStrokePrimitive2D(
+ rPolygonStrokeCandidate.getB2DPolygon(), aRed,
+ rPolygonStrokeCandidate.getStrokeAttribute()));
+ process(*xCopy);
+ }
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset);
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ // setup line attributes
+ cairo_line_join_t eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
+ switch (rLineAttribute.getLineJoin())
+ {
+ case basegfx::B2DLineJoin::Bevel:
+ eCairoLineJoin = CAIRO_LINE_JOIN_BEVEL;
+ break;
+ case basegfx::B2DLineJoin::Round:
+ eCairoLineJoin = CAIRO_LINE_JOIN_ROUND;
+ break;
+ case basegfx::B2DLineJoin::NONE:
+ case basegfx::B2DLineJoin::Miter:
+ eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
+ break;
+ }
+
+ // convert miter minimum angle to miter limit
+ double fMiterLimit
+ = 1.0 / sin(std::max(rLineAttribute.getMiterMinimumAngle(), 0.01 * M_PI) / 2.0);
+
+ // setup cap attribute
+ cairo_line_cap_t eCairoLineCap(CAIRO_LINE_CAP_BUTT);
+
+ switch (rLineAttribute.getLineCap())
+ {
+ default: // css::drawing::LineCap_BUTT:
+ {
+ eCairoLineCap = CAIRO_LINE_CAP_BUTT;
+ break;
+ }
+ case css::drawing::LineCap_ROUND:
+ {
+ eCairoLineCap = CAIRO_LINE_CAP_ROUND;
+ break;
+ }
+ case css::drawing::LineCap_SQUARE:
+ {
+ eCairoLineCap = CAIRO_LINE_CAP_SQUARE;
+ break;
+ }
+ }
+
+ basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
+ if (bRenderDecomposeForCompareInRed)
+ aLineColor.setRed(0.5);
+
+ cairo_set_source_rgb(mpRT, aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue());
+
+ cairo_set_line_join(mpRT, eCairoLineJoin);
+ cairo_set_line_cap(mpRT, eCairoLineCap);
+
+ // TODO: Hairline LineWidth, see comment at processPolygonHairlinePrimitive2D
+ cairo_set_line_width(mpRT, bHairline ? 1.44 : fDiscreteLineWidth);
+ cairo_set_miter_limit(mpRT, fMiterLimit);
+
+ const attribute::StrokeAttribute& rStrokeAttribute(
+ rPolygonStrokeCandidate.getStrokeAttribute());
+ const bool bDashUsed(!rStrokeAttribute.isDefault()
+ && !rStrokeAttribute.getDotDashArray().empty()
+ && 0.0 < rStrokeAttribute.getFullDotDashLen());
+ if (bDashUsed)
+ {
+ const std::vector<double>& rStroke = rStrokeAttribute.getDotDashArray();
+ cairo_set_dash(mpRT, rStroke.data(), rStroke.size(), 0.0);
+ }
+
+ addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+ cairo_stroke(mpRT);
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processLineRectanglePrimitive2D(
+ const primitive2d::LineRectanglePrimitive2D& rLineRectanglePrimitive2D)
+{
+ if (rLineRectanglePrimitive2D.getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset);
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rLineRectanglePrimitive2D.getBColor()));
+ cairo_set_source_rgb(mpRT, aHairlineColor.getRed(), aHairlineColor.getGreen(),
+ aHairlineColor.getBlue());
+
+ const double fDiscreteLineWidth((getViewInformation2D().getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(1.44, 0.0))
+ .getLength());
+ cairo_set_line_width(mpRT, fDiscreteLineWidth);
+
+ const basegfx::B2DRange& rRange(rLineRectanglePrimitive2D.getB2DRange());
+ cairo_rectangle(mpRT, rRange.getMinX(), rRange.getMinY(), rRange.getWidth(),
+ rRange.getHeight());
+ cairo_stroke(mpRT);
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processFilledRectanglePrimitive2D(
+ const primitive2d::FilledRectanglePrimitive2D& rFilledRectanglePrimitive2D)
+{
+ if (rFilledRectanglePrimitive2D.getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ cairo_save(mpRT);
+
+ cairo_matrix_t aMatrix;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset);
+
+ // set linear transformation
+ cairo_set_matrix(mpRT, &aMatrix);
+
+ const basegfx::BColor aFillColor(
+ maBColorModifierStack.getModifiedColor(rFilledRectanglePrimitive2D.getBColor()));
+ cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue());
+
+ const basegfx::B2DRange& rRange(rFilledRectanglePrimitive2D.getB2DRange());
+ cairo_rectangle(mpRT, rRange.getMinX(), rRange.getMinY(), rRange.getWidth(),
+ rRange.getHeight());
+ cairo_fill(mpRT);
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processSingleLinePrimitive2D(
+ const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D)
+{
+ cairo_save(mpRT);
+
+ const basegfx::BColor aLineColor(
+ maBColorModifierStack.getModifiedColor(rSingleLinePrimitive2D.getBColor()));
+ cairo_set_source_rgb(mpRT, aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue());
+
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ const basegfx::B2DPoint aStart(rObjectToView * rSingleLinePrimitive2D.getStart());
+ const basegfx::B2DPoint aEnd(rObjectToView * rSingleLinePrimitive2D.getEnd());
+
+ cairo_set_line_width(mpRT, 1.44f);
+
+ cairo_move_to(mpRT, aStart.getX() + fAAOffset, aStart.getY() + fAAOffset);
+ cairo_line_to(mpRT, aEnd.getX() + fAAOffset, aEnd.getY() + fAAOffset);
+ cairo_stroke(mpRT);
+
+ cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ switch (rCandidate.getPrimitive2DID())
+ {
+ // geometry that *has* to be processed
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ {
+ processBitmapPrimitive2D(
+ static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ {
+ processPointArrayPrimitive2D(
+ static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ {
+ processPolygonHairlinePrimitive2D(
+ static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ {
+ processPolyPolygonColorPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ // embedding/groups that *have* to be processed
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+ {
+ processTransparencePrimitive2D(
+ static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
+ {
+ // TODO: fallback is at VclPixelProcessor2D::processInvertPrimitive2D, so
+ // not in reach. Ignore for now.
+ // processInvertPrimitive2D(rCandidate);
+ break;
+ }
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ {
+ processMaskPrimitive2DPixel(
+ static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ {
+ processModifiedColorPrimitive2D(
+ static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ {
+ processTransformPrimitive2D(
+ static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
+ break;
+ }
+#if 0
+ // geometry that *may* be processed due to being able to do it better
+ // then using the decomposition
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ {
+ processUnifiedTransparencePrimitive2D(
+ static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ {
+ processMarkerArrayPrimitive2D(
+ static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
+ {
+ processBackgroundColorPrimitive2D(
+ static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
+ break;
+ }
+#endif
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ {
+ processPolygonStrokePrimitive2D(
+ static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D:
+ {
+ processLineRectanglePrimitive2D(
+ static_cast<const primitive2d::LineRectanglePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D:
+ {
+ processFilledRectanglePrimitive2D(
+ static_cast<const primitive2d::FilledRectanglePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D:
+ {
+ processSingleLinePrimitive2D(
+ static_cast<const primitive2d::SingleLinePrimitive2D&>(rCandidate));
+ break;
+ }
+
+ // continue with decompose
+ default:
+ {
+ SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
+ rCandidate.getPrimitive2DID()));
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/drawinglayer/source/processor2d/contourextractor2d.cxx b/drawinglayer/source/processor2d/contourextractor2d.cxx
index 5fc51695ddc5..65e8ef86a29e 100644
--- a/drawinglayer/source/processor2d/contourextractor2d.cxx
+++ b/drawinglayer/source/processor2d/contourextractor2d.cxx
@@ -19,7 +19,7 @@
#include <drawinglayer/processor2d/contourextractor2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
@@ -123,12 +123,8 @@ namespace drawinglayer::processor2d
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
// create new local ViewInformation2D
- const geometry::ViewInformation2D aViewInformation2D(
- getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
- getViewInformation2D().getViewTransformation(),
- getViewInformation2D().getViewport(),
- getViewInformation2D().getVisualizedPage(),
- getViewInformation2D().getViewTime());
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation());
updateViewInformation(aViewInformation2D);
// process content
diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
new file mode 100644
index 000000000000..6bfc95878332
--- /dev/null
+++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
@@ -0,0 +1,2191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+// win-specific
+#include <prewin.h>
+#include <d2d1.h>
+#include <d2d1_1.h>
+#include <postwin.h>
+
+#include <drawinglayer/processor2d/d2dpixelprocessor2d.hxx>
+#include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx>
+#include <sal/log.hxx>
+#include <vcl/outdev.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+class ID2D1GlobalFactoryProvider
+{
+ sal::systools::COMReference<ID2D1Factory> mpD2DFactory;
+
+public:
+ ID2D1GlobalFactoryProvider()
+ : mpD2DFactory(nullptr)
+ {
+ const HRESULT hr(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
+ __uuidof(ID2D1Factory), nullptr,
+ reinterpret_cast<void**>(&mpD2DFactory)));
+
+ if (!SUCCEEDED(hr))
+ mpD2DFactory.clear();
+ }
+
+ sal::systools::COMReference<ID2D1Factory>& getID2D1Factory() { return mpD2DFactory; }
+};
+
+ID2D1GlobalFactoryProvider aID2D1GlobalFactoryProvider;
+
+class ID2D1GlobalRenderTargetProvider
+{
+ sal::systools::COMReference<ID2D1DCRenderTarget> mpID2D1DCRenderTarget;
+
+public:
+ ID2D1GlobalRenderTargetProvider()
+ : mpID2D1DCRenderTarget()
+ {
+ }
+
+ sal::systools::COMReference<ID2D1DCRenderTarget>& getID2D1DCRenderTarget()
+ {
+ if (!mpID2D1DCRenderTarget && aID2D1GlobalFactoryProvider.getID2D1Factory())
+ {
+ const D2D1_RENDER_TARGET_PROPERTIES aRTProps(D2D1::RenderTargetProperties(
+ D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_IGNORE), //D2D1_ALPHA_MODE_PREMULTIPLIED),
+ 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT));
+
+ const HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateDCRenderTarget(
+ &aRTProps, &mpID2D1DCRenderTarget));
+
+ // interestingly this ID2D1DCRenderTarget already works and can hold
+ // created ID2D1Bitmap(s) in RenderTarget-specific form, *without*
+ // any call to "BindDC", thus *without* the need of a real HDC - nice :-)
+ // When that would be needed, Application::GetDefaultDevice() would need
+ // to have a HDC that is valid during LO's lifetime.
+
+ if (!SUCCEEDED(hr))
+ mpID2D1DCRenderTarget.clear();
+ }
+
+ return mpID2D1DCRenderTarget;
+ }
+};
+
+ID2D1GlobalRenderTargetProvider aID2D1GlobalRenderTargetProvider;
+
+class SystemDependentData_ID2D1PathGeometry : public basegfx::SystemDependentData
+{
+private:
+ sal::systools::COMReference<ID2D1PathGeometry> mpID2D1PathGeometry;
+
+public:
+ SystemDependentData_ID2D1PathGeometry(
+ sal::systools::COMReference<ID2D1PathGeometry>& rID2D1PathGeometry)
+ : basegfx::SystemDependentData(Application::GetSystemDependentDataManager())
+ , mpID2D1PathGeometry(rID2D1PathGeometry)
+ {
+ }
+
+ const sal::systools::COMReference<ID2D1PathGeometry>& getID2D1PathGeometry() const
+ {
+ return mpID2D1PathGeometry;
+ }
+ virtual sal_Int64 estimateUsageInBytes() const override;
+};
+
+sal_Int64 SystemDependentData_ID2D1PathGeometry::estimateUsageInBytes() const
+{
+ sal_Int64 aRetval(0);
+
+ if (getID2D1PathGeometry())
+ {
+ UINT32 nCount(0);
+ const HRESULT hr(getID2D1PathGeometry()->GetSegmentCount(&nCount));
+
+ if (SUCCEEDED(hr))
+ {
+ // without completely receiving and tracing the GeometrySink
+ // do a rough estimation - each segment is 2D, so has two doubles.
+ // Some are beziers, so add some guessed buffer for two additional
+ // control points
+ aRetval = static_cast<sal_Int64>(nCount) * (6 * sizeof(double));
+ }
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPoint impPixelSnap(const basegfx::B2DPolygon& rPolygon,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation,
+ sal_uInt32 nIndex)
+{
+ const sal_uInt32 nCount(rPolygon.count());
+
+ // get the data
+ const basegfx::B2ITuple aPrevTuple(
+ basegfx::fround(rViewInformation.getObjectToViewTransformation()
+ * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
+ const basegfx::B2DPoint aCurrPoint(rViewInformation.getObjectToViewTransformation()
+ * rPolygon.getB2DPoint(nIndex));
+ const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+ const basegfx::B2ITuple aNextTuple(
+ basegfx::fround(rViewInformation.getObjectToViewTransformation()
+ * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
+
+ // get the states
+ const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+ const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+ const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+ const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+ const bool bSnapX(bPrevVertical || bNextVertical);
+ const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+ if (bSnapX || bSnapY)
+ {
+ basegfx::B2DPoint aSnappedPoint(bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
+ bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
+
+ aSnappedPoint *= rViewInformation.getInverseObjectToViewTransformation();
+
+ return aSnappedPoint;
+ }
+
+ return rPolygon.getB2DPoint(nIndex);
+}
+
+void addB2DPolygonToPathGeometry(sal::systools::COMReference<ID2D1GeometrySink>& rSink,
+ const basegfx::B2DPolygon& rPolygon,
+ const drawinglayer::geometry::ViewInformation2D* pViewInformation)
+{
+ const sal_uInt32 nPointCount(rPolygon.count());
+ const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nPointCount : nPointCount - 1);
+ basegfx::B2DCubicBezier aEdge;
+
+ for (sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ rPolygon.getBezierSegment(a, aEdge);
+
+ const basegfx::B2DPoint aEndPoint(
+ nullptr == pViewInformation
+ ? aEdge.getEndPoint()
+ : impPixelSnap(rPolygon, *pViewInformation, (a + 1) % nPointCount));
+
+ if (aEdge.isBezier())
+ {
+ rSink->AddBezier(
+ D2D1::BezierSegment(D2D1::Point2F(aEdge.getControlPointA().getX(),
+ aEdge.getControlPointA().getY()), //C1
+ D2D1::Point2F(aEdge.getControlPointB().getX(),
+ aEdge.getControlPointB().getY()), //c2
+ D2D1::Point2F(aEndPoint.getX(), aEndPoint.getY()))); //end
+ }
+ else
+ {
+ rSink->AddLine(D2D1::Point2F(aEndPoint.getX(), aEndPoint.getY()));
+ }
+ }
+}
+
+std::shared_ptr<SystemDependentData_ID2D1PathGeometry>
+getOrCreatePathGeometry(const basegfx::B2DPolygon& rPolygon,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation)
+{
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
+ rPolygon.getSystemDependentData<SystemDependentData_ID2D1PathGeometry>());
+
+ if (pSystemDependentData_ID2D1PathGeometry)
+ {
+ if (rViewInformation.getPixelSnapHairline())
+ {
+ // do not buffer when PixelSnap is active
+ pSystemDependentData_ID2D1PathGeometry.reset();
+ }
+ else
+ {
+ // use and return buffered data
+ return pSystemDependentData_ID2D1PathGeometry;
+ }
+ }
+
+ sal::systools::COMReference<ID2D1PathGeometry> pID2D1PathGeometry;
+ HRESULT hr(
+ aID2D1GlobalFactoryProvider.getID2D1Factory()->CreatePathGeometry(&pID2D1PathGeometry));
+ const sal_uInt32 nPointCount(rPolygon.count());
+
+ if (SUCCEEDED(hr) && nPointCount)
+ {
+ sal::systools::COMReference<ID2D1GeometrySink> pSink;
+ hr = pID2D1PathGeometry->Open(&pSink);
+
+ if (SUCCEEDED(hr) && pSink)
+ {
+ const basegfx::B2DPoint aStart(rViewInformation.getPixelSnapHairline()
+ ? rPolygon.getB2DPoint(0)
+ : impPixelSnap(rPolygon, rViewInformation, 0));
+
+ pSink->BeginFigure(D2D1::Point2F(aStart.getX(), aStart.getY()),
+ D2D1_FIGURE_BEGIN_HOLLOW);
+ addB2DPolygonToPathGeometry(pSink, rPolygon, &rViewInformation);
+ pSink->EndFigure(rPolygon.isClosed() ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN);
+ pSink->Close();
+ }
+ }
+
+ // add to buffering mechanism
+ if (pID2D1PathGeometry)
+ {
+ if (rViewInformation.getPixelSnapHairline() || nPointCount <= 4)
+ {
+ // do not buffer when PixelSnap is active or small polygon
+ return std::make_shared<SystemDependentData_ID2D1PathGeometry>(pID2D1PathGeometry);
+ }
+ else
+ {
+ return rPolygon.addOrReplaceSystemDependentData<SystemDependentData_ID2D1PathGeometry>(
+ pID2D1PathGeometry);
+ }
+ }
+
+ return std::shared_ptr<SystemDependentData_ID2D1PathGeometry>();
+}
+
+std::shared_ptr<SystemDependentData_ID2D1PathGeometry>
+getOrCreateFillGeometry(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
+ rPolyPolygon.getSystemDependentData<SystemDependentData_ID2D1PathGeometry>());
+
+ if (pSystemDependentData_ID2D1PathGeometry)
+ {
+ // use and return buffered data
+ return pSystemDependentData_ID2D1PathGeometry;
+ }
+
+ sal::systools::COMReference<ID2D1PathGeometry> pID2D1PathGeometry;
+ HRESULT hr(
+ aID2D1GlobalFactoryProvider.getID2D1Factory()->CreatePathGeometry(&pID2D1PathGeometry));
+ const sal_uInt32 nCount(rPolyPolygon.count());
+
+ if (SUCCEEDED(hr) && nCount)
+ {
+ sal::systools::COMReference<ID2D1GeometrySink> pSink;
+ hr = pID2D1PathGeometry->Open(&pSink);
+
+ if (SUCCEEDED(hr) && pSink)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ const basegfx::B2DPolygon& rPolygon(rPolyPolygon.getB2DPolygon(a));
+ const sal_uInt32 nPointCount(rPolygon.count());
+
+ if (nPointCount)
+ {
+ const basegfx::B2DPoint aStart(rPolygon.getB2DPoint(0));
+
+ pSink->BeginFigure(D2D1::Point2F(aStart.getX(), aStart.getY()),
+ D2D1_FIGURE_BEGIN_FILLED);
+ addB2DPolygonToPathGeometry(pSink, rPolygon, nullptr);
+ pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+ }
+ }
+
+ pSink->Close();
+ }
+ }
+
+ // add to buffering mechanism
+ if (pID2D1PathGeometry)
+ {
+ return rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_ID2D1PathGeometry>(
+ pID2D1PathGeometry);
+ }
+
+ return std::shared_ptr<SystemDependentData_ID2D1PathGeometry>();
+}
+
+class SystemDependentData_ID2D1Bitmap : public basegfx::SystemDependentData
+{
+private:
+ sal::systools::COMReference<ID2D1Bitmap> mpD2DBitmap;
+ const std::shared_ptr<SalBitmap> maAssociatedAlpha;
+
+public:
+ SystemDependentData_ID2D1Bitmap(sal::systools::COMReference<ID2D1Bitmap>& rD2DBitmap,
+ const std::shared_ptr<SalBitmap>& rAssociatedAlpha)
+ : basegfx::SystemDependentData(Application::GetSystemDependentDataManager())
+ , mpD2DBitmap(rD2DBitmap)
+ , maAssociatedAlpha(rAssociatedAlpha)
+ {
+ }
+
+ const sal::systools::COMReference<ID2D1Bitmap>& getID2D1Bitmap() const { return mpD2DBitmap; }
+ const std::shared_ptr<SalBitmap>& getAssociatedAlpha() const { return maAssociatedAlpha; }
+
+ virtual sal_Int64 estimateUsageInBytes() const override;
+};
+
+sal_Int64 SystemDependentData_ID2D1Bitmap::estimateUsageInBytes() const
+{
+ sal_Int64 aRetval(0);
+
+ if (getID2D1Bitmap())
+ {
+ // use factor 4 for RGBA_8 as estimation
+ const D2D1_SIZE_U aSizePixel(getID2D1Bitmap()->GetPixelSize());
+ aRetval = static_cast<sal_Int64>(aSizePixel.width)
+ * static_cast<sal_Int64>(aSizePixel.height) * 4;
+ }
+
+ return aRetval;
+}
+
+sal::systools::COMReference<ID2D1Bitmap> createB2DBitmap(const BitmapEx& rBitmapEx)
+{
+ const Size& rSizePixel(rBitmapEx.GetSizePixel());
+ const bool bAlpha(rBitmapEx.IsAlpha());
+ const sal_uInt32 nPixelCount(rSizePixel.Width() * rSizePixel.Height());
+ std::unique_ptr<sal_uInt32[]> aData(new sal_uInt32[nPixelCount]);
+ sal_uInt32* pTarget = aData.get();
+
+ if (bAlpha)
+ {
+ Bitmap aSrcAlpha(rBitmapEx.GetAlphaMask().GetBitmap());
+ BitmapScopedReadAccess pReadAccess(rBitmapEx.GetBitmap());
+ BitmapScopedReadAccess pAlphaReadAccess(aSrcAlpha);
+ const tools::Long nHeight(pReadAccess->Height());
+ const tools::Long nWidth(pReadAccess->Width());
+
+ for (tools::Long y = 0; y < nHeight; ++y)
+ {
+ for (tools::Long x = 0; x < nWidth; ++x)
+ {
+ const BitmapColor aColor(pReadAccess->GetColor(y, x));
+ const BitmapColor aAlpha(pAlphaReadAccess->GetColor(y, x));
+ const sal_uInt16 nAlpha(aAlpha.GetRed());
+
+ *pTarget++ = sal_uInt32(BitmapColor(
+ ColorAlpha, sal_uInt8((sal_uInt16(aColor.GetRed()) * nAlpha) >> 8),
+ sal_uInt8((sal_uInt16(aColor.GetGreen()) * nAlpha) >> 8),
+ sal_uInt8((sal_uInt16(aColor.GetBlue()) * nAlpha) >> 8), aAlpha.GetRed()));
+ }
+ }
+ }
+ else
+ {
+ BitmapScopedReadAccess pReadAccess(const_cast<Bitmap&>(rBitmapEx.GetBitmap()));
+ const tools::Long nHeight(pReadAccess->Height());
+ const tools::Long nWidth(pReadAccess->Width());
+
+ for (tools::Long y = 0; y < nHeight; ++y)
+ {
+ for (tools::Long x = 0; x < nWidth; ++x)
+ {
+ const BitmapColor aColor(pReadAccess->GetColor(y, x));
+ *pTarget++ = sal_uInt32(aColor);
+ }
+ }
+ }
+
+ // use GlobalRenderTarget to allow usage combined with
+ // the Direct2D CreateSharedBitmap-mechanism. This is needed
+ // since ID2D1Bitmap is a ID2D1RenderTarget-dependent resource
+ // and thus - in principle - would have to be re-created for
+ // *each* new ID2D1RenderTarget, that means for *each* new
+ // target HDC, resp. OutputDevice
+ sal::systools::COMReference<ID2D1Bitmap> pID2D1Bitmap;
+
+ if (aID2D1GlobalRenderTargetProvider.getID2D1DCRenderTarget())
+ {
+ const HRESULT hr(aID2D1GlobalRenderTargetProvider.getID2D1DCRenderTarget()->CreateBitmap(
+ D2D1::SizeU(rSizePixel.Width(), rSizePixel.Height()), &aData[0],
+ rSizePixel.Width() * sizeof(sal_uInt32),
+ D2D1::BitmapProperties(
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, // DXGI_FORMAT
+ bAlpha ? D2D1_ALPHA_MODE_PREMULTIPLIED
+ : D2D1_ALPHA_MODE_IGNORE)), // D2D1_ALPHA_MODE
+ &pID2D1Bitmap));
+
+ if (!SUCCEEDED(hr))
+ pID2D1Bitmap.clear();
+ }
+
+ return pID2D1Bitmap;
+}
+
+sal::systools::COMReference<ID2D1Bitmap>
+getOrCreateB2DBitmap(sal::systools::COMReference<ID2D1RenderTarget>& rRT, const BitmapEx& rBitmapEx)
+{
+ const basegfx::SystemDependentDataHolder* pHolder(
+ rBitmapEx.GetBitmap().accessSystemDependentDataHolder());
+ std::shared_ptr<SystemDependentData_ID2D1Bitmap> pSystemDependentData_ID2D1Bitmap;
+
+ if (nullptr != pHolder)
+ {
+ // try to access SystemDependentDataHolder and buffered data
+ pSystemDependentData_ID2D1Bitmap
+ = std::static_pointer_cast<SystemDependentData_ID2D1Bitmap>(
+ pHolder->getSystemDependentData(
+ typeid(SystemDependentData_ID2D1Bitmap).hash_code()));
+
+ // check data validity for associated Alpha
+ if (pSystemDependentData_ID2D1Bitmap
+ && pSystemDependentData_ID2D1Bitmap->getAssociatedAlpha()
+ != rBitmapEx.GetAlphaMask().GetBitmap().ImplGetSalBitmap())
+ {
+ // AssociatedAlpha did change, data invalid
+ pSystemDependentData_ID2D1Bitmap.reset();
+ }
+ }
+
+ if (!pSystemDependentData_ID2D1Bitmap)
+ {
+ // have to create newly
+ sal::systools::COMReference<ID2D1Bitmap> pID2D1Bitmap(createB2DBitmap(rBitmapEx));
+
+ if (pID2D1Bitmap)
+ {
+ // creation worked, create SystemDependentData_ID2D1Bitmap
+ pSystemDependentData_ID2D1Bitmap = std::make_shared<SystemDependentData_ID2D1Bitmap>(
+ pID2D1Bitmap, rBitmapEx.GetAlphaMask().GetBitmap().ImplGetSalBitmap());
+
+ // only add if feasible
+ if (nullptr != pHolder
+ && pSystemDependentData_ID2D1Bitmap->calculateCombinedHoldCyclesInSeconds() > 0)
+ {
+ basegfx::SystemDependentData_SharedPtr r2(pSystemDependentData_ID2D1Bitmap);
+ const_cast<basegfx::SystemDependentDataHolder*>(pHolder)
+ ->addOrReplaceSystemDependentData(r2);
+ }
+ }
+ }
+
+ sal::systools::COMReference<ID2D1Bitmap> pWrappedD2DBitmap;
+
+ if (pSystemDependentData_ID2D1Bitmap)
+ {
+ // embed to CreateSharedBitmap, that makes it usable on
+ // the specified RenderTarget
+ const HRESULT hr(rRT->CreateSharedBitmap(
+ __uuidof(ID2D1Bitmap),
+ static_cast<void*>(pSystemDependentData_ID2D1Bitmap->getID2D1Bitmap()), nullptr,
+ &pWrappedD2DBitmap));
+
+ if (!SUCCEEDED(hr))
+ pWrappedD2DBitmap.clear();
+ }
+
+ return pWrappedD2DBitmap;
+}
+
+// This is a simple local derivation of D2DPixelProcessor2D to be used
+// when sub-content needs to be rendered to pixels. Hand over the adapted
+// ViewInformation2D, a pixel size and the parent RenderTarget. It will
+// locally create and use a ID2D1BitmapRenderTarget to render the stuff
+// (you need to call process() with the primitives to be painted of
+// course). Then use the local helper getID2D1Bitmap() to access the
+// ID2D1Bitmap which was the target of that operation.
+class D2DBitmapPixelProcessor2D final : public drawinglayer::processor2d::D2DPixelProcessor2D
+{
+ // the local ID2D1BitmapRenderTarget
+ sal::systools::COMReference<ID2D1BitmapRenderTarget> mpBitmapRenderTarget;
+
+public:
+ // helper class to create another instance of D2DPixelProcessor2D for
+ // creating helper-ID2D1Bitmap's for a given ID2D1RenderTarget
+ D2DBitmapPixelProcessor2D(const drawinglayer::geometry::ViewInformation2D& rViewInformation,
+ sal_uInt32 nWidth, sal_uInt32 nHeight,
+ const sal::systools::COMReference<ID2D1RenderTarget>& rParent)
+ : drawinglayer::processor2d::D2DPixelProcessor2D(rViewInformation)
+ , mpBitmapRenderTarget()
+ {
+ if (0 == nWidth || 0 == nHeight)
+ {
+ // no width/height, done
+ increaseError();
+ }
+
+ if (!hasError())
+ {
+ // Allocate compatible RGBA render target
+ const D2D1_SIZE_U aRenderTargetSizePixel(D2D1::SizeU(nWidth, nHeight));
+ const HRESULT hr(rParent->CreateCompatibleRenderTarget(
+ nullptr, &aRenderTargetSizePixel, nullptr,
+ D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, &mpBitmapRenderTarget));
+
+ if (!SUCCEEDED(hr) || nullptr == mpBitmapRenderTarget)
+ {
+ // did not work, done
+ increaseError();
+ }
+ else
+ {
+ sal::systools::COMReference<ID2D1RenderTarget> pRT;
+ mpBitmapRenderTarget->QueryInterface(__uuidof(ID2D1RenderTarget),
+ reinterpret_cast<void**>(&pRT));
+ setRenderTarget(pRT);
+ }
+ }
+
+ if (hasRenderTarget())
+ {
+ // set Viewort if none was given. We have a fixed pixel target, s we know the
+ // exact Viewport to work on
+ if (getViewInformation2D().getViewport().isEmpty())
+ {
+ drawinglayer::geometry::ViewInformation2D aViewInformation(getViewInformation2D());
+ basegfx::B2DRange aViewport(0.0, 0.0, nWidth, nHeight);
+ basegfx::B2DHomMatrix aInvViewTransform(aViewInformation.getViewTransformation());
+
+ aInvViewTransform.invert();
+ aViewport.transform(aInvViewTransform);
+ aViewInformation.setViewport(aViewport);
+ updateViewInformation(aViewInformation);
+ }
+
+ // clear as render preparation
+ getRenderTarget()->BeginDraw();
+ getRenderTarget()->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
+ getRenderTarget()->EndDraw();
+ }
+ }
+
+ sal::systools::COMReference<ID2D1Bitmap> getID2D1Bitmap() const
+ {
+ sal::systools::COMReference<ID2D1Bitmap> pResult;
+
+ // access the resulting bitmap if exists
+ if (mpBitmapRenderTarget)
+ {
+ mpBitmapRenderTarget->GetBitmap(&pResult);
+ }
+
+ return pResult;
+ }
+};
+
+bool createBitmapSubContent(sal::systools::COMReference<ID2D1Bitmap>& rResult,
+ basegfx::B2DRange& rDiscreteVisibleRange,
+ const drawinglayer::primitive2d::Primitive2DContainer& rContent,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D,
+ const sal::systools::COMReference<ID2D1RenderTarget>& rRenderTarget)
+{
+ if (rContent.empty() || !rRenderTarget)
+ {
+ // no content or no render target, done
+ return false;
+ }
+
+ drawinglayer::processor2d::calculateDiscreteVisibleRange(
+ rDiscreteVisibleRange, rContent.getB2DRange(rViewInformation2D), rViewInformation2D);
+
+ if (rDiscreteVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return false;
+ }
+
+ // Use a temporary second instance of a D2DBitmapPixelProcessor2D with adapted
+ // ViewInformation2D, it will create the needed ID2D1BitmapRenderTarget
+ // locally and Clear() it.
+ drawinglayer::geometry::ViewInformation2D aAdaptedViewInformation2D(rViewInformation2D);
+ const double fTargetWidth(ceil(rDiscreteVisibleRange.getWidth()));
+ const double fTargetHeight(ceil(rDiscreteVisibleRange.getHeight()));
+
+ {
+ // create adapted ViewTransform, needs to be offset in discrete coordinates,
+ // so multiply from left
+ basegfx::B2DHomMatrix aAdapted(
+ basegfx::utils::createTranslateB2DHomMatrix(-rDiscreteVisibleRange.getMinX(),
+ -rDiscreteVisibleRange.getMinY())
+ * rViewInformation2D.getViewTransformation());
+ aAdaptedViewInformation2D.setViewTransformation(aAdapted);
+
+ // reset Viewport (world coordinates), so the helper renderer will create it's
+ // own based on it's given internal discrete size
+ aAdaptedViewInformation2D.setViewport(basegfx::B2DRange());
+ }
+
+ D2DBitmapPixelProcessor2D aSubContentRenderer(aAdaptedViewInformation2D, fTargetWidth,
+ fTargetHeight, rRenderTarget);
+
+ if (!aSubContentRenderer.valid())
+ {
+ // did not work, done
+ return false;
+ }
+
+ // render sub-content recursively
+ aSubContentRenderer.process(rContent);
+
+ // grab Bitmap & prepare results from RGBA content rendering
+ rResult = aSubContentRenderer.getID2D1Bitmap();
+ return true;
+}
+}
+
+namespace drawinglayer::processor2d
+{
+D2DPixelProcessor2D::D2DPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation)
+ : BaseProcessor2D(rViewInformation)
+ , maBColorModifierStack()
+ , mpRT()
+ , mnRecursionCounter(0)
+ , mnErrorCounter(0)
+{
+}
+
+D2DPixelProcessor2D::D2DPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+ HDC aHdc)
+ : BaseProcessor2D(rViewInformation)
+ , maBColorModifierStack()
+ , mpRT()
+ , mnRecursionCounter(0)
+ , mnErrorCounter(0)
+{
+ sal::systools::COMReference<ID2D1DCRenderTarget> pDCRT;
+ tools::Long aOutWidth(0), aOutHeight(0);
+
+ if (aHdc)
+ {
+ aOutWidth = GetDeviceCaps(aHdc, HORZRES);
+ aOutHeight = GetDeviceCaps(aHdc, VERTRES);
+ }
+
+ if (aOutWidth > 0 && aOutHeight > 0 && aID2D1GlobalFactoryProvider.getID2D1Factory())
+ {
+ const D2D1_RENDER_TARGET_PROPERTIES aRTProps(D2D1::RenderTargetProperties(
+ D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_IGNORE), //D2D1_ALPHA_MODE_PREMULTIPLIED),
+ 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT));
+
+ const HRESULT hr(
+ aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateDCRenderTarget(&aRTProps, &pDCRT));
+
+ if (!SUCCEEDED(hr))
+ pDCRT.clear();
+ }
+
+ if (pDCRT)
+ {
+ const RECT rc(
+ { 0, 0, o3tl::narrowing<LONG>(aOutWidth), o3tl::narrowing<LONG>(aOutHeight) });
+ const HRESULT hr(pDCRT->BindDC(aHdc, &rc));
+
+ if (!SUCCEEDED(hr))
+ pDCRT.clear();
+ }
+
+ if (pDCRT)
+ {
+ if (rViewInformation.getUseAntiAliasing())
+ {
+ D2D1_ANTIALIAS_MODE eAAMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+ pDCRT->SetAntialiasMode(eAAMode);
+ }
+ else
+ {
+ D2D1_ANTIALIAS_MODE eAAMode = D2D1_ANTIALIAS_MODE_ALIASED;
+ pDCRT->SetAntialiasMode(eAAMode);
+ }
+
+ // since ID2D1DCRenderTarget depends on the transformation
+ // set at hdc, be careful and reset it to identity
+ XFORM aXForm;
+ aXForm.eM11 = 1.0;
+ aXForm.eM12 = 0.0;
+ aXForm.eM21 = 0.0;
+ aXForm.eM22 = 1.0;
+ aXForm.eDx = 0.0;
+ aXForm.eDy = 0.0;
+ SetWorldTransform(aHdc, &aXForm);
+ }
+
+ if (pDCRT)
+ {
+ sal::systools::COMReference<ID2D1RenderTarget> pRT;
+ pDCRT->QueryInterface(__uuidof(ID2D1RenderTarget), reinterpret_cast<void**>(&pRT));
+ setRenderTarget(pRT);
+ }
+ else
+ {
+ increaseError();
+ }
+}
+
+void D2DPixelProcessor2D::processPolygonHairlinePrimitive2D(
+ const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D)
+{
+ const basegfx::B2DPolygon& rPolygon(rPolygonHairlinePrimitive2D.getB2DPolygon());
+
+ if (!rPolygon.count())
+ {
+ // no geometry, done
+ return;
+ }
+
+ bool bDone(false);
+ std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
+ getOrCreatePathGeometry(rPolygon, getViewInformation2D()));
+
+ if (pSystemDependentData_ID2D1PathGeometry)
+ {
+ sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
+ pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(),
+ D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset),
+ &pTransformedGeometry));
+
+ if (SUCCEEDED(hr) && pTransformedGeometry)
+ {
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rPolygonHairlinePrimitive2D.getBColor()));
+ const D2D1::ColorF aD2DColor(aHairlineColor.getRed(), aHairlineColor.getGreen(),
+ aHairlineColor.getBlue());
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ // TODO: Unfortunately Direct2D paint of one pixel wide lines does not
+ // correctly and completely blend 100% over the background. Experimenting
+ // shows that a value around/slightly below 2.0 is needed which hints that
+ // alpha blending the half-shifted lines (see fAAOffset above) is involved.
+ // To get correct blending I try to use just wider hairlines for now. This
+ // may need to be improved - or balanced (trying sqrt(2) now...)
+ getRenderTarget()->DrawGeometry(pTransformedGeometry, pColorBrush, 1.44f);
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+bool D2DPixelProcessor2D::drawPolyPolygonColorTransformed(
+ const basegfx::B2DHomMatrix& rTansformation, const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const basegfx::BColor& rColor)
+{
+ std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
+ getOrCreateFillGeometry(rPolyPolygon));
+
+ if (pSystemDependentData_ID2D1PathGeometry)
+ {
+ sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ basegfx::B2DHomMatrix aTansformation(getViewInformation2D().getObjectToViewTransformation()
+ * rTansformation);
+ HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
+ pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(),
+ D2D1::Matrix3x2F(aTansformation.a(), aTansformation.b(), aTansformation.c(),
+ aTansformation.d(), aTansformation.e() + fAAOffset,
+ aTansformation.f() + fAAOffset),
+ &pTransformedGeometry));
+
+ if (SUCCEEDED(hr) && pTransformedGeometry)
+ {
+ const basegfx::BColor aFillColor(maBColorModifierStack.getModifiedColor(rColor));
+ const D2D1::ColorF aD2DColor(aFillColor.getRed(), aFillColor.getGreen(),
+ aFillColor.getBlue());
+
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ getRenderTarget()->FillGeometry(pTransformedGeometry, pColorBrush);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void D2DPixelProcessor2D::processPolyPolygonColorPrimitive2D(
+ const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D)
+{
+ const basegfx::B2DPolyPolygon& rPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon());
+ const sal_uInt32 nCount(rPolyPolygon.count());
+
+ if (!nCount)
+ {
+ // no geometry, done
+ return;
+ }
+
+ const bool bDone(drawPolyPolygonColorTransformed(basegfx::B2DHomMatrix(), rPolyPolygon,
+ rPolyPolygonColorPrimitive2D.getBColor()));
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processBitmapPrimitive2D(
+ const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
+{
+ // check if graphic content is inside discrete local ViewPort
+ if (!getViewInformation2D().getDiscreteViewport().isEmpty())
+ {
+ // calculate logic object range, remember: the helper below will
+ // transform using getObjectToViewTransformation, so the bitmap-local
+ // transform would be missing
+ basegfx::B2DRange aDiscreteVisibleRange(basegfx::B2DRange::getUnitB2DRange());
+ aDiscreteVisibleRange.transform(rBitmapCandidate.getTransform());
+
+ // calculate visible range
+ calculateDiscreteVisibleRange(aDiscreteVisibleRange, aDiscreteVisibleRange,
+ getViewInformation2D());
+
+ if (aDiscreteVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return;
+ }
+ }
+
+ BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
+
+ if (aBitmapEx.IsEmpty() || aBitmapEx.GetSizePixel().IsEmpty())
+ {
+ // no pixel data, done
+ return;
+ }
+
+ if (maBColorModifierStack.count())
+ {
+ // need to apply ColorModifier to Bitmap data
+ aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+
+ if (aBitmapEx.IsEmpty())
+ {
+ // color gets completely replaced, get it (any input works)
+ const basegfx::BColor aModifiedColor(
+ maBColorModifierStack.getModifiedColor(basegfx::BColor()));
+
+ // use unit geometry as fallback object geometry. Do *not*
+ // transform, the below used method will use the already
+ // correctly initialized local ViewInformation
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+
+ rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> aTemp(
+ new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon),
+ aModifiedColor));
+
+ // draw as Polygon, done
+ processPolyPolygonColorPrimitive2D(*aTemp);
+ return;
+ }
+ }
+
+ bool bDone(false);
+ sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap(
+ getOrCreateB2DBitmap(getRenderTarget(), aBitmapEx));
+
+ if (pD2DBitmap)
+ {
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix aLocalTransform(
+ getViewInformation2D().getObjectToViewTransformation()
+ * rBitmapCandidate.getTransform());
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
+ aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
+ aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
+
+ // destinationRectangle is part of transformation above, so use UnitRange
+ getRenderTarget()->DrawBitmap(pD2DBitmap, D2D1::RectF(0.0, 0.0, 1.0, 1.0));
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_Direct(
+ const primitive2d::TransparencePrimitive2D& rTransCandidate)
+{
+ // Try if we can use ID2D1DeviceContext/d2d1_1 by querying for interface.
+ // Only then can we use ID2D1Effect/CLSID_D2D1LuminanceToAlpha and it makes
+ // sense to try to do it this way in this implementation
+ sal::systools::COMReference<ID2D1DeviceContext> pID2D1DeviceContext;
+ getRenderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext),
+ reinterpret_cast<void**>(&pID2D1DeviceContext));
+ sal::systools::COMReference<ID2D1Bitmap> pRetval;
+
+ if (!pID2D1DeviceContext)
+ {
+ // no, done - tell caller to use fallback by returning empty - we have
+ // not the preconditions for this
+ return pRetval;
+ }
+
+ // Release early
+ pID2D1DeviceContext.clear();
+ basegfx::B2DRange aDiscreteVisibleRange;
+
+ if (!createBitmapSubContent(pRetval, aDiscreteVisibleRange, rTransCandidate.getTransparence(),
+ getViewInformation2D(), getRenderTarget())
+ || !pRetval)
+ {
+ // return of false means no display needed, return
+ return pRetval;
+ }
+
+ // Now we need a target to render this to, using the ID2D1Effect tooling.
+ // We can directly apply the effect to an alpha-only 8bit target here,
+ // so create one (no RGBA needed for this).
+ // We need another render target: I tried to render pInBetweenResult
+ // to pContent again, but that does not work due to the bitmap
+ // fetched being probably only an internal reference to the
+ // ID2D1BitmapRenderTarget, thus it would draw onto itself -> chaos
+ sal::systools::COMReference<ID2D1BitmapRenderTarget> pContent;
+ const D2D1_PIXEL_FORMAT aAlphaFormat(
+ D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT));
+ const D2D1_SIZE_U aRenderTargetSizePixel(D2D1::SizeU(ceil(aDiscreteVisibleRange.getWidth()),
+ ceil(aDiscreteVisibleRange.getHeight())));
+ const HRESULT hr(getRenderTarget()->CreateCompatibleRenderTarget(
+ nullptr, &aRenderTargetSizePixel, &aAlphaFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE,
+ &pContent));
+
+ if (SUCCEEDED(hr) && pContent)
+ {
+ // try to access ID2D1DeviceContext of that target, we need that *now*
+ pContent->QueryInterface(__uuidof(ID2D1DeviceContext),
+ reinterpret_cast<void**>(&pID2D1DeviceContext));
+
+ if (pID2D1DeviceContext)
+ {
+ // create the effect
+ sal::systools::COMReference<ID2D1Effect> pLuminanceToAlpha;
+ pID2D1DeviceContext->CreateEffect(CLSID_D2D1LuminanceToAlpha, &pLuminanceToAlpha);
+
+ if (pLuminanceToAlpha)
+ {
+ // chain effect stuff together & paint it
+ pLuminanceToAlpha->SetInput(0, pRetval);
+
+ pID2D1DeviceContext->BeginDraw();
+ pID2D1DeviceContext->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
+ pID2D1DeviceContext->DrawImage(pLuminanceToAlpha);
+ pID2D1DeviceContext->EndDraw();
+
+ // grab result
+ pContent->GetBitmap(&pRetval);
+ }
+ }
+ }
+
+ return pRetval;
+}
+
+sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_B2DBitmap(
+ const primitive2d::TransparencePrimitive2D& rTransCandidate,
+ const basegfx::B2DRange& rVisibleRange, D2D1_MATRIX_3X2_F& rMaskScale)
+{
+ // Use this fallback that will also use a pixel processor indirectly,
+ // but allows to get the AlphaMask as vcl Bitmap using existing tooling
+ const sal_uInt32 nDiscreteClippedWidth(ceil(rVisibleRange.getWidth()));
+ const sal_uInt32 nDiscreteClippedHeight(ceil(rVisibleRange.getHeight()));
+ const sal_uInt32 nMaximumQuadraticPixels(250000);
+
+ // Embed content graphics to TransformPrimitive2D
+ const basegfx::B2DHomMatrix aAlphaEmbedding(
+ basegfx::utils::createTranslateB2DHomMatrix(-rVisibleRange.getMinX(),
+ -rVisibleRange.getMinY())
+ * getViewInformation2D().getObjectToViewTransformation());
+ const primitive2d::Primitive2DReference xAlphaEmbedRef(new primitive2d::TransformPrimitive2D(
+ aAlphaEmbedding,
+ drawinglayer::primitive2d::Primitive2DContainer(rTransCandidate.getTransparence())));
+ drawinglayer::primitive2d::Primitive2DContainer xEmbedSeq{ xAlphaEmbedRef };
+
+ // use empty ViewInformation to have neutral transformation
+ const geometry::ViewInformation2D aEmptyViewInformation2D;
+
+ // use new mode to create AlphaChannel (not just AlphaMask) for transparency channel
+ const AlphaMask aAlpha(::drawinglayer::createAlphaMask(
+ std::move(xEmbedSeq), aEmptyViewInformation2D, nDiscreteClippedWidth,
+ nDiscreteClippedHeight, nMaximumQuadraticPixels, true));
+ sal::systools::COMReference<ID2D1Bitmap> pRetval;
+
+ if (aAlpha.IsEmpty())
+ {
+ // if we have no content we are done
+ return pRetval;
+ }
+
+ // use alpha data to create the ID2D1Bitmap
+ const Size& rSizePixel(aAlpha.GetSizePixel());
+ const sal_uInt32 nPixelCount(rSizePixel.Width() * rSizePixel.Height());
+ std::unique_ptr<sal_uInt8[]> aData(new sal_uInt8[nPixelCount]);
+ sal_uInt8* pTarget = aData.get();
+ Bitmap aSrcAlpha(aAlpha.GetBitmap());
+ BitmapScopedReadAccess pReadAccess(aSrcAlpha);
+ const tools::Long nHeight(pReadAccess->Height());
+ const tools::Long nWidth(pReadAccess->Width());
+
+ for (tools::Long y = 0; y < nHeight; ++y)
+ {
+ for (tools::Long x = 0; x < nWidth; ++x)
+ {
+ const BitmapColor aColor(pReadAccess->GetColor(y, x));
+ *pTarget++ = aColor.GetLuminance();
+ }
+ }
+
+ const D2D1_BITMAP_PROPERTIES aBmProps(D2D1::BitmapProperties(
+ D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)));
+ const HRESULT hr(getRenderTarget()->CreateBitmap(
+ D2D1::SizeU(rSizePixel.Width(), rSizePixel.Height()), &aData[0],
+ rSizePixel.Width() * sizeof(sal_uInt8), &aBmProps, &pRetval));
+
+ if (!SUCCEEDED(hr) || !pRetval)
+ {
+ // did not work, done
+ return pRetval;
+ }
+
+ // create needed adapted transformation for alpha brush.
+ // We may have to take a corrective scaling into account when the
+ // MaximumQuadraticPixel limit was used/triggered
+ const Size& rBitmapExSizePixel(aAlpha.GetSizePixel());
+
+ if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
+ || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
+ {
+ // scale in X and Y should be the same (see fReduceFactor in createAlphaMask),
+ // so adapt numerically to a single scale value, they are integer rounded values
+ const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
+ / static_cast<double>(nDiscreteClippedWidth));
+ const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
+ / static_cast<double>(nDiscreteClippedHeight));
+
+ const double fScale(1.0 / ((fScaleX + fScaleY) * 0.5));
+ rMaskScale = D2D1::Matrix3x2F::Scale(fScale, fScale);
+ }
+
+ return pRetval;
+}
+
+void D2DPixelProcessor2D::processTransparencePrimitive2D(
+ const primitive2d::TransparencePrimitive2D& rTransCandidate)
+{
+ if (rTransCandidate.getChildren().empty())
+ {
+ // no content, done
+ return;
+ }
+
+ if (rTransCandidate.getTransparence().empty())
+ {
+ // no mask (so nothing visible), done
+ return;
+ }
+
+ // calculate visible range, create only for that range
+ basegfx::B2DRange aDiscreteVisibleRange;
+ calculateDiscreteVisibleRange(aDiscreteVisibleRange,
+ rTransCandidate.getChildren().getB2DRange(getViewInformation2D()),
+ getViewInformation2D());
+
+ if (aDiscreteVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return;
+ }
+
+ // try to create directly, this needs the current mpRT to be a ID2D1DeviceContext/d2d1_1
+ // what is not guaranteed but usually works for more modern windows (after 7)
+ sal::systools::COMReference<ID2D1Bitmap> pAlphaBitmap(implCreateAlpha_Direct(rTransCandidate));
+ D2D1_MATRIX_3X2_F aMaskScale(D2D1::Matrix3x2F::Identity());
+
+ if (!pAlphaBitmap)
+ {
+ // did not work, use more expensive fallback to existing tooling
+ pAlphaBitmap
+ = implCreateAlpha_B2DBitmap(rTransCandidate, aDiscreteVisibleRange, aMaskScale);
+ }
+
+ if (!pAlphaBitmap)
+ {
+ // could not create alpha channel, error
+ increaseError();
+ return;
+ }
+
+ sal::systools::COMReference<ID2D1Layer> pLayer;
+ HRESULT hr(getRenderTarget()->CreateLayer(nullptr, &pLayer));
+ bool bDone(false);
+
+ if (SUCCEEDED(hr) && pLayer)
+ {
+ sal::systools::COMReference<ID2D1BitmapBrush> pBitmapBrush;
+ hr = getRenderTarget()->CreateBitmapBrush(pAlphaBitmap, &pBitmapBrush);
+
+ if (SUCCEEDED(hr) && pBitmapBrush)
+ {
+ // apply MaskScale to Brush, maybe used if implCreateAlpha_B2DBitmap was needed
+ pBitmapBrush->SetTransform(aMaskScale);
+
+ // need to set transform offset for Layer initialization, we work
+ // in discrete device coordinates
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Translation(
+ floor(aDiscreteVisibleRange.getMinX()), floor(aDiscreteVisibleRange.getMinY())));
+
+ getRenderTarget()->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::Matrix3x2F::Identity(), 1.0,
+ pBitmapBrush),
+ pLayer);
+
+ // ... but need to reset to paint content unchanged
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+
+ // draw content recursively
+ process(rTransCandidate.getChildren());
+
+ getRenderTarget()->PopLayer();
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processUnifiedTransparencePrimitive2D(
+ const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
+{
+ if (rTransCandidate.getChildren().empty())
+ {
+ // no content, done
+ return;
+ }
+
+ if (0.0 == rTransCandidate.getTransparence())
+ {
+ // not transparent at all, use content
+ process(rTransCandidate.getChildren());
+ return;
+ }
+
+ if (rTransCandidate.getTransparence() < 0.0 || rTransCandidate.getTransparence() > 1.0)
+ {
+ // invalid transparence, done
+ return;
+ }
+
+ // calculate visible range
+ basegfx::B2DRange aDiscreteVisibleRange;
+ calculateDiscreteVisibleRange(aDiscreteVisibleRange,
+ rTransCandidate.getChildren().getB2DRange(getViewInformation2D()),
+ getViewInformation2D());
+
+ if (aDiscreteVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return;
+ }
+
+ bool bDone(false);
+ sal::systools::COMReference<ID2D1Layer> pLayer;
+ const HRESULT hr(getRenderTarget()->CreateLayer(nullptr, &pLayer));
+
+ if (SUCCEEDED(hr) && pLayer)
+ {
+ // need to set correct transform for Layer initialization, we work
+ // in discrete device coordinates
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ getRenderTarget()->PushLayer(
+ D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ 1.0 - rTransCandidate.getTransparence()), // opacity
+ pLayer);
+ process(rTransCandidate.getChildren());
+ getRenderTarget()->PopLayer();
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processMaskPrimitive2DPixel(
+ const primitive2d::MaskPrimitive2D& rMaskCandidate)
+{
+ if (rMaskCandidate.getChildren().empty())
+ {
+ // no content, done
+ return;
+ }
+
+ basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
+
+ if (!aMask.count())
+ {
+ // no mask (so nothing inside), done
+ return;
+ }
+
+ // calculate visible range
+ basegfx::B2DRange aDiscreteVisibleRange;
+ calculateDiscreteVisibleRange(aDiscreteVisibleRange, aMask.getB2DRange(),
+ getViewInformation2D());
+
+ if (aDiscreteVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return;
+ }
+
+ bool bDone(false);
+ std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1MaskGeometry(
+ getOrCreateFillGeometry(rMaskCandidate.getMask()));
+
+ if (pSystemDependentData_ID2D1MaskGeometry)
+ {
+ sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedMaskGeometry;
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
+ pSystemDependentData_ID2D1MaskGeometry->getID2D1PathGeometry(),
+ D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e(), rObjectToView.f()),
+ &pTransformedMaskGeometry));
+
+ if (SUCCEEDED(hr) && pTransformedMaskGeometry)
+ {
+ sal::systools::COMReference<ID2D1Layer> pLayer;
+ hr = getRenderTarget()->CreateLayer(nullptr, &pLayer);
+
+ if (SUCCEEDED(hr) && pLayer)
+ {
+ // need to set correct transform for Layer initialization, we work
+ // in discrete device coordinates
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ getRenderTarget()->PushLayer(
+ D2D1::LayerParameters(D2D1::InfiniteRect(), pTransformedMaskGeometry), pLayer);
+ process(rMaskCandidate.getChildren());
+ getRenderTarget()->PopLayer();
+ bDone = true;
+ }
+ }
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processPointArrayPrimitive2D(
+ const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
+{
+ const std::vector<basegfx::B2DPoint>& rPositions(rPointArrayCandidate.getPositions());
+
+ if (rPositions.empty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ const basegfx::BColor aPointColor(
+ maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ D2D1::ColorF aD2DColor(aPointColor.getRed(), aPointColor.getGreen(), aPointColor.getBlue());
+ const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
+ bool bDone(false);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+
+ // To really paint a single pixel I found nothing better than
+ // switch off AA and draw a pixel-aligned rectangle
+ const D2D1_ANTIALIAS_MODE aOldAAMode(getRenderTarget()->GetAntialiasMode());
+ getRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+ for (auto const& pos : rPositions)
+ {
+ const basegfx::B2DPoint aDiscretePos(
+ getViewInformation2D().getObjectToViewTransformation() * pos);
+ const double fX(ceil(aDiscretePos.getX()));
+ const double fY(ceil(aDiscretePos.getY()));
+ const D2D1_RECT_F rect = { FLOAT(fX), FLOAT(fY), FLOAT(fX), FLOAT(fY) };
+
+ getRenderTarget()->DrawRectangle(&rect, pColorBrush);
+ }
+
+ getRenderTarget()->SetAntialiasMode(aOldAAMode);
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processMarkerArrayPrimitive2D(
+ const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate)
+{
+ const std::vector<basegfx::B2DPoint>& rPositions(rMarkerArrayCandidate.getPositions());
+
+ if (rPositions.empty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ const BitmapEx& rMarker(rMarkerArrayCandidate.getMarker());
+
+ if (rMarker.IsEmpty())
+ {
+ // no marker defined, done
+ return;
+ }
+
+ sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap(
+ getOrCreateB2DBitmap(getRenderTarget(), rMarker));
+ bool bDone(false);
+
+ if (pD2DBitmap)
+ {
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ const Size& rSizePixel(rMarker.GetSizePixel());
+ const tools::Long nMiX((rSizePixel.Width() / 2) + 1);
+ const tools::Long nMiY((rSizePixel.Height() / 2) + 1);
+ const tools::Long nPlX(rSizePixel.Width() - nMiX);
+ const tools::Long nPlY(rSizePixel.Height() - nMiY);
+
+ // draw with non-AA to show unhampered, clear, non-scaled marker
+ const D2D1_ANTIALIAS_MODE aOldAAMode(getRenderTarget()->GetAntialiasMode());
+ getRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+ for (auto const& pos : rPositions)
+ {
+ const basegfx::B2DPoint aDiscretePos(
+ getViewInformation2D().getObjectToViewTransformation() * pos);
+ const double fX(ceil(aDiscretePos.getX()));
+ const double fY(ceil(aDiscretePos.getY()));
+ const D2D1_RECT_F rect
+ = { FLOAT(fX - nMiX), FLOAT(fY - nMiY), FLOAT(fX + nPlX), FLOAT(fY + nPlY) };
+
+ getRenderTarget()->DrawBitmap(pD2DBitmap, &rect);
+ }
+
+ getRenderTarget()->SetAntialiasMode(aOldAAMode);
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processBackgroundColorPrimitive2D(
+ const primitive2d::BackgroundColorPrimitive2D& rBackgroundColorCandidate)
+{
+ // check for allowed range [0.0 .. 1.0[
+ if (rBackgroundColorCandidate.getTransparency() < 0.0
+ || rBackgroundColorCandidate.getTransparency() >= 1.0)
+ return;
+
+ const D2D1::ColorF aD2DColor(rBackgroundColorCandidate.getBColor().getRed(),
+ rBackgroundColorCandidate.getBColor().getGreen(),
+ rBackgroundColorCandidate.getBColor().getBlue(),
+ 1.0 - rBackgroundColorCandidate.getTransparency());
+
+ getRenderTarget()->Clear(aD2DColor);
+}
+
+void D2DPixelProcessor2D::processModifiedColorPrimitive2D(
+ const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
+{
+ if (!rModifiedCandidate.getChildren().empty())
+ {
+ maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
+ process(rModifiedCandidate.getChildren());
+ maBColorModifierStack.pop();
+ }
+}
+
+void D2DPixelProcessor2D::processTransformPrimitive2D(
+ const primitive2d::TransformPrimitive2D& rTransformCandidate)
+{
+ // remember current transformation and ViewInformation
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new transformations for local ViewInformation2D
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
+ * rTransformCandidate.getTransformation());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+}
+
+void D2DPixelProcessor2D::processPolygonStrokePrimitive2D(
+ const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
+{
+ const basegfx::B2DPolygon& rPolygon(rPolygonStrokeCandidate.getB2DPolygon());
+ const attribute::LineAttribute& rLineAttribute(rPolygonStrokeCandidate.getLineAttribute());
+
+ if (!rPolygon.count() || rLineAttribute.getWidth() < 0.0)
+ {
+ // no geometry, done
+ return;
+ }
+
+ // get some values early that might be used for decisions
+ const bool bHairline(0.0 == rLineAttribute.getWidth());
+ const basegfx::B2DHomMatrix& rObjectToView(
+ getViewInformation2D().getObjectToViewTransformation());
+ const double fDiscreteLineWidth(
+ bHairline
+ ? 1.0
+ : (rObjectToView * basegfx::B2DVector(rLineAttribute.getWidth(), 0.0)).getLength());
+
+ // Here for every combination which the system-specific implementation is not
+ // capable of visualizing, use the (for decomposable Primitives always possible)
+ // fallback to the decomposition.
+ if (basegfx::B2DLineJoin::NONE == rLineAttribute.getLineJoin() && fDiscreteLineWidth > 1.5)
+ {
+ // basegfx::B2DLineJoin::NONE is special for our office, no other GraphicSystem
+ // knows that (so far), so fallback to decomposition. This is only needed if
+ // LineJoin will be used, so also check for discrete LineWidth before falling back
+ process(rPolygonStrokeCandidate);
+ return;
+ }
+
+ // This is a method every system-specific implementation of a decomposable Primitive
+ // can use to allow simple optical control of paint implementation:
+ // Create a copy, e.g. change color to 'red' as here and paint before the system
+ // paints it using the decomposition. That way you can - if active - directly
+ // optically compare if the system-specific solution is geometrically identical to
+ // the decomposition (which defines our interpretation that we need to visualize).
+ // Look below in the impl for bRenderDecomposeForCompareInRed to see that in that case
+ // we create a half-transparent paint to better support visual control
+ static bool bRenderDecomposeForCompareInRed(false);
+
+ if (bRenderDecomposeForCompareInRed)
+ {
+ const attribute::LineAttribute aRed(
+ basegfx::BColor(1.0, 0.0, 0.0), rLineAttribute.getWidth(), rLineAttribute.getLineJoin(),
+ rLineAttribute.getLineCap(), rLineAttribute.getMiterMinimumAngle());
+ rtl::Reference<primitive2d::PolygonStrokePrimitive2D> aCopy(
+ new primitive2d::PolygonStrokePrimitive2D(
+ rPolygonStrokeCandidate.getB2DPolygon(), aRed,
+ rPolygonStrokeCandidate.getStrokeAttribute()));
+ process(*aCopy);
+ }
+
+ bool bDone(false);
+ std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
+ getOrCreatePathGeometry(rPolygon, getViewInformation2D()));
+
+ if (pSystemDependentData_ID2D1PathGeometry)
+ {
+ sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry;
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
+ pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(),
+ D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
+ rObjectToView.d(), rObjectToView.e() + fAAOffset,
+ rObjectToView.f() + fAAOffset),
+ &pTransformedGeometry));
+
+ if (SUCCEEDED(hr) && pTransformedGeometry)
+ {
+ const basegfx::BColor aLineColor(
+ maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
+ D2D1::ColorF aD2DColor(aLineColor.getRed(), aLineColor.getGreen(),
+ aLineColor.getBlue());
+
+ if (bRenderDecomposeForCompareInRed)
+ {
+ aD2DColor.a = 0.5;
+ }
+
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ sal::systools::COMReference<ID2D1StrokeStyle> pStrokeStyle;
+ D2D1_CAP_STYLE aCapStyle(D2D1_CAP_STYLE_FLAT);
+ D2D1_LINE_JOIN aLineJoin(D2D1_LINE_JOIN_MITER);
+ const attribute::StrokeAttribute& rStrokeAttribute(
+ rPolygonStrokeCandidate.getStrokeAttribute());
+ const bool bDashUsed(!rStrokeAttribute.isDefault()
+ && !rStrokeAttribute.getDotDashArray().empty()
+ && 0.0 < rStrokeAttribute.getFullDotDashLen());
+ D2D1_DASH_STYLE aDashStyle(bDashUsed ? D2D1_DASH_STYLE_CUSTOM
+ : D2D1_DASH_STYLE_SOLID);
+ std::vector<float> dashes;
+ float miterLimit(1.0);
+
+ switch (rLineAttribute.getLineCap())
+ {
+ case css::drawing::LineCap_ROUND:
+ aCapStyle = D2D1_CAP_STYLE_ROUND;
+ break;
+ case css::drawing::LineCap_SQUARE:
+ aCapStyle = D2D1_CAP_STYLE_SQUARE;
+ break;
+ default:
+ break;
+ }
+
+ switch (rLineAttribute.getLineJoin())
+ {
+ case basegfx::B2DLineJoin::NONE:
+ break;
+ case basegfx::B2DLineJoin::Bevel:
+ aLineJoin = D2D1_LINE_JOIN_BEVEL;
+ break;
+ case basegfx::B2DLineJoin::Miter:
+ {
+ // for basegfx::B2DLineJoin::Miter there are two problems:
+ // (1) MS uses D2D1_LINE_JOIN_MITER which handles the cut-off when MiterLimit is hit not by
+ // fallback to Bevel, but by cutting miter geometry at the defined distance. That is
+ // nice, but not what we need or is the standard for other graphic systems. Luckily there
+ // is also D2D1_LINE_JOIN_MITER_OR_BEVEL and (after some search) the page
+ // https://learn.microsoft.com/en-us/windows/win32/api/d2d1/ne-d2d1-d2d1_line_join
+ // which gives some explanation, so that is what we need to use here.
+ // (2) Instead of using an angle in radians (15 deg default) MS uses
+ // "miterLimit is relative to 1/2 LineWidth", so a length. After some experimenting
+ // it shows that the (better understandable) angle has to be converted to the length
+ // that a miter prolongation would have at that angle, so use some trigonometry.
+ // Unfortunately there is also some'precision' problem (probably), so I had to
+ // experimentally come to a correction value around 0.9925. Since that seems to
+ // be no obvious numerical value involved somehow (and as long as I find no other
+ // explanation) I will have to use that.
+ // NOTE: To find that correction value I usd that handy bRenderDecomposeForCompareInRed
+ // and changes in debugger - as work tipp
+ // With both done I can use Direct2D for Miter completely - what is good for speed.
+ aLineJoin = D2D1_LINE_JOIN_MITER_OR_BEVEL;
+
+ // snap absolute value of angle in radians to [0.0 .. PI]
+ double fVal(::basegfx::snapToZeroRange(
+ fabs(rLineAttribute.getMiterMinimumAngle()), M_PI));
+
+ // cut at 0.0 and PI since sin would be zero ('endless' miter)
+ const double fSmallValue(M_PI * 0.0000001);
+ fVal = std::max(fSmallValue, fVal);
+ fVal = std::min(M_PI - fSmallValue, fVal);
+
+ // get relative length
+ fVal = 1.0 / sin(fVal);
+
+ // use for miterLimit, we need factor 2.0 (relative to double LineWidth)
+ // and the correction mentioned in (2) above
+ const double fCorrector(2.0 * 0.9925);
+
+ miterLimit = fVal * fCorrector;
+ break;
+ }
+ case basegfx::B2DLineJoin::Round:
+ aLineJoin = D2D1_LINE_JOIN_ROUND;
+ break;
+ default:
+ break;
+ }
+
+ if (bDashUsed)
+ {
+ // dashes need to be discrete and relative to LineWidth
+ for (auto& value : rStrokeAttribute.getDotDashArray())
+ {
+ dashes.push_back(
+ (rObjectToView * basegfx::B2DVector(value, 0.0)).getLength()
+ / fDiscreteLineWidth);
+ }
+ }
+
+ hr = aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateStrokeStyle(
+ D2D1::StrokeStyleProperties(aCapStyle, // startCap
+ aCapStyle, // endCap
+ aCapStyle, // dashCap
+ aLineJoin, // lineJoin
+ miterLimit, // miterLimit
+ aDashStyle, // dashStyle
+ 0.0f), // dashOffset
+ bDashUsed ? dashes.data() : nullptr, bDashUsed ? dashes.size() : 0,
+ &pStrokeStyle);
+
+ if (SUCCEEDED(hr) && pStrokeStyle)
+ {
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ getRenderTarget()->DrawGeometry(
+ pTransformedGeometry, pColorBrush,
+ // TODO: Hairline LineWidth, see comment at processPolygonHairlinePrimitive2D
+ bHairline ? 1.44 : fDiscreteLineWidth, pStrokeStyle);
+ bDone = true;
+ }
+ }
+ }
+ }
+
+ if (!bDone)
+ {
+ // fallback to decomposition
+ process(rPolygonStrokeCandidate);
+ }
+}
+
+void D2DPixelProcessor2D::processLineRectanglePrimitive2D(
+ const primitive2d::LineRectanglePrimitive2D& rLineRectanglePrimitive2D)
+{
+ if (rLineRectanglePrimitive2D.getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ const basegfx::BColor aHairlineColor(
+ maBColorModifierStack.getModifiedColor(rLineRectanglePrimitive2D.getBColor()));
+ const D2D1::ColorF aD2DColor(aHairlineColor.getRed(), aHairlineColor.getGreen(),
+ aHairlineColor.getBlue());
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
+ bool bDone(false);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix aLocalTransform(
+ getViewInformation2D().getObjectToViewTransformation());
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
+ aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
+ aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
+ const basegfx::B2DRange& rRange(rLineRectanglePrimitive2D.getB2DRange());
+ const D2D1_RECT_F rect = { FLOAT(rRange.getMinX()), FLOAT(rRange.getMinY()),
+ FLOAT(rRange.getMaxX()), FLOAT(rRange.getMaxY()) };
+ const double fDiscreteLineWidth(
+ (getViewInformation2D().getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(1.44, 0.0))
+ .getLength());
+
+ getRenderTarget()->DrawRectangle(&rect, pColorBrush, fDiscreteLineWidth);
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processFilledRectanglePrimitive2D(
+ const primitive2d::FilledRectanglePrimitive2D& rFilledRectanglePrimitive2D)
+{
+ if (rFilledRectanglePrimitive2D.getB2DRange().isEmpty())
+ {
+ // no geometry, done
+ return;
+ }
+
+ const basegfx::BColor aFillColor(
+ maBColorModifierStack.getModifiedColor(rFilledRectanglePrimitive2D.getBColor()));
+ const D2D1::ColorF aD2DColor(aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue());
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
+ bool bDone(false);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix aLocalTransform(
+ getViewInformation2D().getObjectToViewTransformation());
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
+ aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
+ aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
+ const basegfx::B2DRange& rRange(rFilledRectanglePrimitive2D.getB2DRange());
+ const D2D1_RECT_F rect = { FLOAT(rRange.getMinX()), FLOAT(rRange.getMinY()),
+ FLOAT(rRange.getMaxX()), FLOAT(rRange.getMaxY()) };
+
+ getRenderTarget()->FillRectangle(&rect, pColorBrush);
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processSingleLinePrimitive2D(
+ const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D)
+{
+ const basegfx::BColor aLineColor(
+ maBColorModifierStack.getModifiedColor(rSingleLinePrimitive2D.getBColor()));
+ const D2D1::ColorF aD2DColor(aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue());
+ sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
+ const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
+ bool bDone(false);
+
+ if (SUCCEEDED(hr) && pColorBrush)
+ {
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ basegfx::B2DHomMatrix aLocalTransform(
+ getViewInformation2D().getObjectToViewTransformation());
+ const basegfx::B2DPoint aStart(aLocalTransform * rSingleLinePrimitive2D.getStart());
+ const basegfx::B2DPoint aEnd(aLocalTransform * rSingleLinePrimitive2D.getEnd());
+
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ const D2D1_POINT_2F aD2D1Start
+ = { FLOAT(aStart.getX() + fAAOffset), FLOAT(aStart.getY() + fAAOffset) };
+ const D2D1_POINT_2F aD2D1End
+ = { FLOAT(aEnd.getX() + fAAOffset), FLOAT(aEnd.getY() + fAAOffset) };
+
+ getRenderTarget()->DrawLine(aD2D1Start, aD2D1End, pColorBrush, 1.44f);
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processFillGraphicPrimitive2D(
+ const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D)
+{
+ BitmapEx aPreparedBitmap;
+ basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange());
+ static double fBigDiscreteArea(300.0 * 300.0);
+
+ // use tooling to do various checks and prepare tiled rendering, see
+ // description of method, parameters and return value there
+ if (!prepareBitmapForDirectRender(rFillGraphicPrimitive2D, getViewInformation2D(),
+ aPreparedBitmap, aFillUnitRange, fBigDiscreteArea))
+ {
+ // no output needed, done
+ return;
+ }
+
+ if (aPreparedBitmap.IsEmpty())
+ {
+ // output needed and Bitmap data empty, so no bitmap data based
+ // tiled rendering is suggested. Use fallback for paint (decomposition)
+ process(rFillGraphicPrimitive2D);
+ return;
+ }
+
+ // render tiled using the prepared Bitmap data
+ if (maBColorModifierStack.count())
+ {
+ // need to apply ColorModifier to Bitmap data
+ aPreparedBitmap = aPreparedBitmap.ModifyBitmapEx(maBColorModifierStack);
+
+ if (aPreparedBitmap.IsEmpty())
+ {
+ // color gets completely replaced, get it (any input works)
+ const basegfx::BColor aModifiedColor(
+ maBColorModifierStack.getModifiedColor(basegfx::BColor()));
+
+ // use unit geometry as fallback object geometry. Do *not*
+ // transform, the below used method will use the already
+ // correctly initialized local ViewInformation
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+
+ // what we still need to apply is the object transform from the
+ // local primitive, that is not part of DisplayInfo yet
+ aPolygon.transform(rFillGraphicPrimitive2D.getTransformation());
+
+ rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> aTemp(
+ new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon),
+ aModifiedColor));
+
+ // draw as colored Polygon, done
+ processPolyPolygonColorPrimitive2D(*aTemp);
+ return;
+ }
+ }
+
+ bool bDone(false);
+ sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap(
+ getOrCreateB2DBitmap(getRenderTarget(), aPreparedBitmap));
+
+ if (pD2DBitmap)
+ {
+ sal::systools::COMReference<ID2D1BitmapBrush> pBitmapBrush;
+ const HRESULT hr(getRenderTarget()->CreateBitmapBrush(pD2DBitmap, &pBitmapBrush));
+
+ if (SUCCEEDED(hr) && pBitmapBrush)
+ {
+ // set extended to repeat/wrap AKA tiling
+ pBitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_WRAP);
+ pBitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_WRAP);
+
+ // set interpolation mode
+ // NOTE: This uses D2D1_BITMAP_INTERPOLATION_MODE, but there seem to be
+ // advanced modes when using D2D1_INTERPOLATION_MODE, but that needs
+ // D2D1_BITMAP_BRUSH_PROPERTIES1 and ID2D1BitmapBrush1
+ sal::systools::COMReference<ID2D1BitmapBrush1> pBrush1;
+ pBitmapBrush->QueryInterface(__uuidof(ID2D1BitmapBrush1),
+ reinterpret_cast<void**>(&pBrush1));
+
+ if (pBrush1)
+ {
+ pBrush1->SetInterpolationMode1(D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR);
+ }
+ else
+ {
+ pBitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
+ }
+
+ // set BitmapBrush transformation relative to it's PixelSize and
+ // the used FillUnitRange. Since we use unit coordinates here this
+ // is pretty simple
+ const D2D1_SIZE_U aBMSPixel(pD2DBitmap->GetPixelSize());
+ const double fScaleX((aFillUnitRange.getMaxX() - aFillUnitRange.getMinX())
+ / aBMSPixel.width);
+ const double fScaleY((aFillUnitRange.getMaxY() - aFillUnitRange.getMinY())
+ / aBMSPixel.height);
+ const D2D1_MATRIX_3X2_F aBTrans(D2D1::Matrix3x2F(
+ fScaleX, 0.0, 0.0, fScaleY, aFillUnitRange.getMinX(), aFillUnitRange.getMinY()));
+ pBitmapBrush->SetTransform(&aBTrans);
+
+ // set transform to ObjectToWorld to be able to paint in unit coordinates, so
+ // evtl. shear/rotate in that transform is used and does not influence the
+ // orthogonal and unit-oriented brush handling
+ const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
+ const basegfx::B2DHomMatrix aLocalTransform(
+ getViewInformation2D().getObjectToViewTransformation()
+ * rFillGraphicPrimitive2D.getTransformation());
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
+ aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
+ aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
+
+ // use unit rectangle, transformation is already set to include ObjectToWorld
+ const D2D1_RECT_F rect = { FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(1.0) };
+
+ // draw as unit rectangle as brush filled rectangle
+ getRenderTarget()->FillRectangle(&rect, pBitmapBrush);
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processFillGradientPrimitive2D(
+ const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
+{
+ // draw all-covering initial BG polygon 1st
+ bool bDone(drawPolyPolygonColorTransformed(
+ basegfx::B2DHomMatrix(),
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(rFillGradientPrimitive2D.getOutputRange())),
+ rFillGradientPrimitive2D.getOuterColor()));
+
+ if (bDone)
+ {
+ const basegfx::B2DPolyPolygon aForm(rFillGradientPrimitive2D.getUnitPolygon());
+
+ // paint solid fill steps by providing callback as lambda
+ auto aCallback([&aForm, &bDone, this](const basegfx::B2DHomMatrix& rMatrix,
+ const basegfx::BColor& rColor) {
+ if (bDone)
+ {
+ bDone = drawPolyPolygonColorTransformed(rMatrix, aForm, rColor);
+ }
+ });
+
+ // call value generator to trigger callbacks
+ rFillGradientPrimitive2D.generateMatricesAndColors(aCallback);
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processInvertPrimitive2D(
+ const primitive2d::InvertPrimitive2D& rInvertPrimitive2D)
+{
+ if (rInvertPrimitive2D.getChildren().empty())
+ {
+ // no content, done
+ return;
+ }
+
+ // Try if we can use ID2D1DeviceContext/d2d1_1 by querying for interface.
+ // Only with ID2D1DeviceContext we can use ::DrawImage which supports
+ // D2D1_COMPOSITE_MODE_XOR
+ sal::systools::COMReference<ID2D1DeviceContext> pID2D1DeviceContext;
+ getRenderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext),
+ reinterpret_cast<void**>(&pID2D1DeviceContext));
+
+ if (!pID2D1DeviceContext)
+ {
+ // TODO: We have *no* ID2D1DeviceContext and cannot use D2D1_COMPOSITE_MODE_XOR,
+ // so there is currently no (simple?) way to solve this, there is no 'Invert' method.
+ // It may be possible to convert to a WICBitmap (gets read access) and do the invert
+ // there, but that needs experimenting and is probably not performant - but doable.
+ increaseError();
+ return;
+ }
+
+ sal::systools::COMReference<ID2D1Bitmap> pInBetweenResult;
+ basegfx::B2DRange aDiscreteVisibleRange;
+
+ // create in-between result in discrete coordinates, clipped against visible
+ // part of ViewInformation (if available)
+ if (!createBitmapSubContent(pInBetweenResult, aDiscreteVisibleRange,
+ rInvertPrimitive2D.getChildren(), getViewInformation2D(),
+ getRenderTarget()))
+ {
+ // return of false means no display needed, return
+ return;
+ }
+
+ bool bDone(false);
+
+ if (pInBetweenResult)
+ {
+ getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
+ const D2D1_POINT_2F aTopLeft = { FLOAT(floor(aDiscreteVisibleRange.getMinX())),
+ FLOAT(floor(aDiscreteVisibleRange.getMinY())) };
+
+ pID2D1DeviceContext->DrawImage(pInBetweenResult, aTopLeft, D2D1_INTERPOLATION_MODE_LINEAR,
+ D2D1_COMPOSITE_MODE_XOR);
+ bDone = true;
+ }
+
+ if (!bDone)
+ increaseError();
+}
+
+void D2DPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ if (0 == mnRecursionCounter)
+ getRenderTarget()->BeginDraw();
+ mnRecursionCounter++;
+
+ switch (rCandidate.getPrimitive2DID())
+ {
+ // Geometry that *has* to be processed
+ //
+ // These Primitives have *no* decompose implementation, so these are the basic ones
+ // Just four to go to make a processor work completely (but not optimized)
+ // NOTE: This *could* theoretically be reduced to one and all could implement
+ // a decompose to pixel data, but that seemed not to make sense to me when
+ // I designed this. Thus these four are the lowest-level best representation
+ // from my POV
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ {
+ processBitmapPrimitive2D(
+ static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ {
+ processPointArrayPrimitive2D(
+ static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ {
+ processPolygonHairlinePrimitive2D(
+ static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ {
+ processPolyPolygonColorPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
+ break;
+ }
+
+ // Embedding/groups that *have* to be processed
+ //
+ // These represent qualifiers for freely defined content, e.g. making
+ // any combination of primitives freely transformed or transparent
+ // NOTE: PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D and
+ // PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D are pretty much default-
+ // implementations that can and are re-used in all processors.
+ // So - with these and PRIMITIVE2D_ID_INVERTPRIMITIVE2D marked to
+ // be removed in the future - just three really to be implemented
+ // locally specifically
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+ {
+ processTransparencePrimitive2D(
+ static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
+ {
+ // We urgently should get rid of XOR paint, modern graphic systems
+ // allow no read access to the pixel targets, but that's naturally
+ // a precondition for XOR. While we can do that for the office's
+ // visualization, we can in principle *not* fully avoid getting
+ // stuff that needs/defines XOR paint, e.g. EMF/WMF imports, so
+ // we *have* to support it (for now - sigh)...
+ processInvertPrimitive2D(
+ static_cast<const primitive2d::InvertPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ {
+ processMaskPrimitive2DPixel(
+ static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ {
+ processModifiedColorPrimitive2D(
+ static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ {
+ processTransformPrimitive2D(
+ static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
+ break;
+ }
+
+ // Geometry that *may* be processed due to being able to do it better
+ // then using the decomposition.
+ // NOTE: In these implementations you could always call what the default
+ // case below does - call process(rCandidate) to use the decomposition.
+ // So these impls should only do something if they can do it better/
+ // faster that the decomposition. So some of them check if they could
+ // - and if not - use exactly that.
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ {
+ // transparence with a fixed alpha for all content, can be done
+ // significally faster
+ processUnifiedTransparencePrimitive2D(
+ static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ {
+ // can be done simpler and without AA better locally
+ processMarkerArrayPrimitive2D(
+ static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
+ {
+ // reset to a color, can be done more effectively locally, would
+ // else decompose to a polygon fill
+ processBackgroundColorPrimitive2D(
+ static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ {
+ // fat and stroked lines - much better doable locally, would decompose
+ // to the full line geometry creation (tessellation)
+ processPolygonStrokePrimitive2D(
+ static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D:
+ {
+ // simple primitive to support future fast callbacks from OutputDevice
+ // (see 'Example POC' in Gerrit), decomposes to polygon primitive
+ processLineRectanglePrimitive2D(
+ static_cast<const primitive2d::LineRectanglePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D:
+ {
+ // simple primitive to support future fast callbacks from OutputDevice
+ // (see 'Example POC' in Gerrit), decomposes to filled polygon primitive
+ processFilledRectanglePrimitive2D(
+ static_cast<const primitive2d::FilledRectanglePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D:
+ {
+ // simple primitive to support future fast callbacks from OutputDevice
+ // (see 'Example POC' in Gerrit), decomposes to polygon primitive
+ processSingleLinePrimitive2D(
+ static_cast<const primitive2d::SingleLinePrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
+ {
+ processFillGraphicPrimitive2D(
+ static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate));
+ break;
+ }
+ case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
+ {
+ processFillGradientPrimitive2D(
+ static_cast<const primitive2d::FillGradientPrimitive2D&>(rCandidate));
+ break;
+ }
+
+ // continue with decompose as fallback
+ default:
+ {
+ SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
+ rCandidate.getPrimitive2DID()));
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+
+ mnRecursionCounter--;
+ if (0 == mnRecursionCounter)
+ getRenderTarget()->EndDraw();
+}
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/getdigitlanguage.cxx b/drawinglayer/source/processor2d/getdigitlanguage.cxx
index 858284b23e91..464fbf642af4 100644
--- a/drawinglayer/source/processor2d/getdigitlanguage.cxx
+++ b/drawinglayer/source/processor2d/getdigitlanguage.cxx
@@ -18,7 +18,7 @@
#include "getdigitlanguage.hxx"
LanguageType drawinglayer::detail::getDigitLanguage() {
- switch (SvtCTLOptions().GetCTLTextNumerals()) {
+ switch (SvtCTLOptions::GetCTLTextNumerals()) {
case SvtCTLOptions::NUMERALS_ARABIC:
return LANGUAGE_ENGLISH;
case SvtCTLOptions::NUMERALS_HINDI:
diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx
index 9f838a7e1b61..d4f14a13ce6c 100644
--- a/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx
+++ b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx
@@ -50,8 +50,10 @@ bool renderWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWron
* basegfx::B2DPoint(rWrongSpellCandidate.getStart(), 0.0));
const basegfx::B2DPoint aStop(aLocalTransform
* basegfx::B2DPoint(rWrongSpellCandidate.getStop(), 0.0));
- const Point aVclStart(basegfx::fround(aStart.getX()), basegfx::fround(aStart.getY()));
- const Point aVclStop(basegfx::fround(aStop.getX()), basegfx::fround(aStop.getY()));
+ const Point aVclStart(basegfx::fround<tools::Long>(aStart.getX()),
+ basegfx::fround<tools::Long>(aStart.getY()));
+ const Point aVclStop(basegfx::fround<tools::Long>(aStop.getX()),
+ basegfx::fround<tools::Long>(aStop.getY()));
// #i101075# draw it. Do not forget to use the evtl. offsetted origin of the target device,
// e.g. when used with mask/transparence buffer device
diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
index ddccfb988654..cf6d3eec9447 100644
--- a/drawinglayer/source/processor2d/hittestprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
@@ -20,7 +20,9 @@
#include <drawinglayer/processor2d/hittestprocessor2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
@@ -39,27 +41,25 @@ namespace drawinglayer::processor2d
{
HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation,
const basegfx::B2DPoint& rLogicHitPosition,
- double fLogicHitTolerance,
+ const basegfx::B2DVector& rLogicHitTolerancePerAxis,
bool bHitTextOnly)
: BaseProcessor2D(rViewInformation),
- mfDiscreteHitTolerance(0.0),
+ maDiscreteHitTolerancePerAxis(rLogicHitTolerancePerAxis),
mbCollectHitStack(false),
mbHit(false),
mbHitTextOnly(bHitTextOnly)
{
- // init hit tolerance
- mfDiscreteHitTolerance = fLogicHitTolerance;
+ // ensure input parameters for hit tolerance is >= 0.0
+ if (maDiscreteHitTolerancePerAxis.getX() < 0.0)
+ maDiscreteHitTolerancePerAxis.setX(0.0);
+ if (maDiscreteHitTolerancePerAxis.getY() < 0.0)
+ maDiscreteHitTolerancePerAxis.setY(0.0);
- if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0))
- {
- // ensure input parameter for hit tolerance is >= 0.0
- mfDiscreteHitTolerance = 0.0;
- }
- else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0))
+ if (!maDiscreteHitTolerancePerAxis.equalZero())
{
// generate discrete hit tolerance
- mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation()
- * basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength();
+ maDiscreteHitTolerancePerAxis
+ = getViewInformation2D().getObjectToViewTransformation() * rLogicHitTolerancePerAxis;
}
// generate discrete hit position
@@ -72,7 +72,7 @@ namespace drawinglayer::processor2d
bool HitTestProcessor2D::checkHairlineHitWithTolerance(
const basegfx::B2DPolygon& rPolygon,
- double fDiscreteHitTolerance) const
+ const basegfx::B2DVector& rDiscreteHitTolerancePerAxis) const
{
basegfx::B2DPolygon aLocalPolygon(rPolygon);
aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
@@ -80,9 +80,9 @@ namespace drawinglayer::processor2d
// get discrete range
basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange());
- if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0))
+ if(rDiscreteHitTolerancePerAxis.getX() > 0 || rDiscreteHitTolerancePerAxis.getY() > 0)
{
- aPolygonRange.grow(fDiscreteHitTolerance);
+ aPolygonRange.grow(rDiscreteHitTolerancePerAxis);
}
// do rough range test first
@@ -92,7 +92,7 @@ namespace drawinglayer::processor2d
return basegfx::utils::isInEpsilonRange(
aLocalPolygon,
getDiscreteHitPosition(),
- fDiscreteHitTolerance);
+ std::max(rDiscreteHitTolerancePerAxis.getX(), rDiscreteHitTolerancePerAxis.getY()));
}
return false;
@@ -100,7 +100,7 @@ namespace drawinglayer::processor2d
bool HitTestProcessor2D::checkFillHitWithTolerance(
const basegfx::B2DPolyPolygon& rPolyPolygon,
- double fDiscreteHitTolerance) const
+ const basegfx::B2DVector& rDiscreteHitTolerancePerAxis) const
{
bool bRetval(false);
basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
@@ -108,11 +108,13 @@ namespace drawinglayer::processor2d
// get discrete range
basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange());
- const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0));
- if(bDiscreteHitToleranceUsed)
+ const bool bDiscreteHitToleranceUsed(rDiscreteHitTolerancePerAxis.getX() > 0
+ || rDiscreteHitTolerancePerAxis.getY() > 0);
+
+ if (bDiscreteHitToleranceUsed)
{
- aPolygonRange.grow(fDiscreteHitTolerance);
+ aPolygonRange.grow(rDiscreteHitTolerancePerAxis);
}
// do rough range test first
@@ -123,7 +125,7 @@ namespace drawinglayer::processor2d
basegfx::utils::isInEpsilonRange(
aLocalPolyPolygon,
getDiscreteHitPosition(),
- fDiscreteHitTolerance))
+ std::max(rDiscreteHitTolerancePerAxis.getX(), rDiscreteHitTolerancePerAxis.getY())))
{
bRetval = true;
}
@@ -231,12 +233,8 @@ namespace drawinglayer::processor2d
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
// create new local ViewInformation2D containing transformation
- const geometry::ViewInformation2D aViewInformation2D(
- getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
- getViewInformation2D().getViewTransformation(),
- getViewInformation2D().getViewport(),
- getViewInformation2D().getVisualizedPage(),
- getViewInformation2D().getViewTime());
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation());
updateViewInformation(aViewInformation2D);
// process child content recursively
@@ -293,10 +291,10 @@ namespace drawinglayer::processor2d
{
// for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance
const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
- * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0));
+ * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, rLineAttribute.getWidth() * 0.5));
mbHit = checkHairlineHitWithTolerance(
rPolygonCandidate.getB2DPolygon(),
- getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
+ getDiscreteHitTolerance() + aDiscreteHalfLineVector);
}
}
else
@@ -330,11 +328,11 @@ namespace drawinglayer::processor2d
}
const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
- * basegfx::B2DVector(fLogicHitTolerance, 0.0));
+ * basegfx::B2DVector(fLogicHitTolerance, fLogicHitTolerance));
mbHit = checkHairlineHitWithTolerance(
rPolygonCandidate.getB2DPolygon(),
- getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
+ getDiscreteHitTolerance() + aDiscreteHalfLineVector);
}
break;
@@ -430,7 +428,7 @@ namespace drawinglayer::processor2d
if(!aRange.isEmpty())
{
const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
- const BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap()));
+ const BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
const Size& rSizePixel(aBitmapEx.GetSizePixel());
// When tiled rendering, don't bother with the pixel size of the candidate.
@@ -518,7 +516,8 @@ namespace drawinglayer::processor2d
const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]);
const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition());
- if(aDistance.getLength() <= getDiscreteHitTolerance())
+ if (aDistance.getLength() <= std::max(getDiscreteHitTolerance().getX(),
+ getDiscreteHitTolerance().getY()))
{
mbHit = true;
}
@@ -540,7 +539,7 @@ namespace drawinglayer::processor2d
{
/// push candidate to HitStack to create it. This only happens when a hit is found and
/// creating the HitStack was requested (see collectHitStack)
- maHitStack.append(primitive2d::Primitive2DReference(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate)));
+ maHitStack.append(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate));
}
}
diff --git a/drawinglayer/source/processor2d/linegeometryextractor2d.cxx b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx
index 1123833cfc99..11af79725b41 100644
--- a/drawinglayer/source/processor2d/linegeometryextractor2d.cxx
+++ b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx
@@ -19,7 +19,7 @@
#include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
@@ -84,12 +84,8 @@ namespace drawinglayer::processor2d
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
// create new transformations for CurrentTransformation and for local ViewInformation2D
- const geometry::ViewInformation2D aViewInformation2D(
- getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
- getViewInformation2D().getViewTransformation(),
- getViewInformation2D().getViewport(),
- getViewInformation2D().getVisualizedPage(),
- getViewInformation2D().getViewTime());
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation());
updateViewInformation(aViewInformation2D);
// process content
diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx b/drawinglayer/source/processor2d/processor2dtools.cxx
index 7bc0f5fa0536..cf823b005ed8 100644
--- a/drawinglayer/source/processor2d/processor2dtools.cxx
+++ b/drawinglayer/source/processor2d/processor2dtools.cxx
@@ -18,45 +18,75 @@
*/
#include <drawinglayer/processor2d/processor2dtools.hxx>
#include <vcl/gdimtf.hxx>
+#include <vcl/sysdata.hxx>
#include "vclpixelprocessor2d.hxx"
#include "vclmetafileprocessor2d.hxx"
+#include <config_vclplug.h>
+#if defined(_WIN32)
+#include <drawinglayer/processor2d/d2dpixelprocessor2d.hxx>
+#elif USE_HEADLESS_CODE
+#include <drawinglayer/processor2d/cairopixelprocessor2d.hxx>
+#endif
using namespace com::sun::star;
-
namespace drawinglayer::processor2d
{
- std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice(
- OutputDevice& rTargetOutDev,
- const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice(
+ OutputDevice& rTargetOutDev,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+{
+ static const bool bTestSystemPrimitiveRenderer(nullptr != std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER"));
+ if(bTestSystemPrimitiveRenderer)
+ {
+ drawinglayer::geometry::ViewInformation2D aViewInformation2D(rViewInformation2D);
+ // if mnOutOffX/mnOutOffY is set (a 'hack' to get a cheap additional offset), apply it additionally
+ if(0 != rTargetOutDev.GetOutOffXPixel() || 0 != rTargetOutDev.GetOutOffYPixel())
{
- // create Pixel Vcl-Processor
- return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev);
+ basegfx::B2DHomMatrix aTransform(aViewInformation2D.getViewTransformation());
+ aTransform.translate(rTargetOutDev.GetOutOffXPixel(), rTargetOutDev.GetOutOffYPixel());
+ aViewInformation2D.setViewTransformation(aTransform);
}
+#if defined(_WIN32)
+ SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
+ std::unique_ptr<D2DPixelProcessor2D> aRetval(
+ std::make_unique<D2DPixelProcessor2D>(aViewInformation2D, aData.hDC));
+ if (aRetval->valid())
+ return aRetval;
+#elif USE_HEADLESS_CODE
+ SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
+ std::unique_ptr<CairoPixelProcessor2D> aRetval(
+ std::make_unique<CairoPixelProcessor2D>(aViewInformation2D, static_cast<cairo_surface_t*>(aData.pSurface)));
+ if (aRetval->valid())
+ return aRetval;
+#endif
+ }
- std::unique_ptr<BaseProcessor2D> createProcessor2DFromOutputDevice(
- OutputDevice& rTargetOutDev,
- const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
- {
- const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile();
- const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
-
- if(bOutputToRecordingMetaFile)
- {
- // create MetaFile Vcl-Processor and process
- return std::make_unique<VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev);
- }
- else
- {
- // create Pixel Vcl-Processor
- return createPixelProcessor2DFromOutputDevice(
- rTargetOutDev,
- rViewInformation2D);
- }
- }
+ // create Pixel Vcl-Processor
+ return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev);
+}
-} // end of namespace
+std::unique_ptr<BaseProcessor2D> createProcessor2DFromOutputDevice(
+ OutputDevice& rTargetOutDev,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
+{
+ const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile();
+ const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord()
+ && !pMetaFile->IsPause());
+ if (bOutputToRecordingMetaFile)
+ {
+ // create MetaFile Vcl-Processor and process
+ return std::make_unique<VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev);
+ }
+ else
+ {
+ // create Pixel Vcl-Processor
+ return createPixelProcessor2DFromOutputDevice(rTargetOutDev, rViewInformation2D);
+ }
+}
+
+} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/processorfromoutputdevice.cxx b/drawinglayer/source/processor2d/processorfromoutputdevice.cxx
deleted file mode 100644
index c8433753aeff..000000000000
--- a/drawinglayer/source/processor2d/processorfromoutputdevice.cxx
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * This file incorporates work covered by the following license notice:
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed
- * with this work for additional information regarding copyright
- * ownership. The ASF licenses this file to you under the Apache
- * License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.apache.org/licenses/LICENSE-2.0 .
- */
-
-#include <vcl/outdev.hxx>
-#include <vcl/gdimtf.hxx>
-#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
-#include "vclmetafileprocessor2d.hxx"
-#include "vclpixelprocessor2d.hxx"
-
-using namespace com::sun::star;
-
-namespace drawinglayer::processor2d
-{
- std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> createBaseProcessor2DFromOutputDevice(
- OutputDevice& rTargetOutDev,
- const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
- {
- const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile();
- const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
-
- if(bOutputToRecordingMetaFile)
- {
- // create MetaFile Vcl-Processor and process
- return std::make_unique<drawinglayer::processor2d::VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev);
- }
- else
- {
- // create Pixel Vcl-Processor
- return std::make_unique<drawinglayer::processor2d::VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev);
- }
- }
-} // end of namespace
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx
index a2943fd8ac16..1a091d027a85 100644
--- a/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx
+++ b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx
@@ -19,65 +19,56 @@
#include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
namespace drawinglayer::processor2d
{
+ void TextAsPolygonExtractor2D::processTextPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+ {
+ // PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
+ // TextDecoratedPortionPrimitive2D can produce the following primitives
+ // when being decomposed:
+ //
+ // - TextSimplePortionPrimitive2D
+ // - PolygonWavePrimitive2D
+ // - PolygonStrokePrimitive2D
+ // - PolygonStrokePrimitive2D
+ // - PolyPolygonColorPrimitive2D
+ // - PolyPolygonHairlinePrimitive2D
+ // - PolygonHairlinePrimitive2D
+ // - ShadowPrimitive2D
+ // - ModifiedColorPrimitive2D
+ // - TransformPrimitive2D
+ // - TextEffectPrimitive2D
+ // - ModifiedColorPrimitive2D
+ // - TransformPrimitive2D
+ // - GroupPrimitive2D
+
+ // PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
+ // TextSimplePortionPrimitive2D can produce the following primitives
+ // when being decomposed:
+ //
+ // - PolyPolygonColorPrimitive2D
+ // - TextEffectPrimitive2D
+ // - ModifiedColorPrimitive2D
+ // - TransformPrimitive2D
+ // - GroupPrimitive2D
+
+ // encapsulate with flag and use decomposition
+ mnInText++;
+ process(rCandidate);
+ mnInText--;
+ }
+
void TextAsPolygonExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
- switch(rCandidate.getPrimitive2DID())
+ switch (rCandidate.getPrimitive2DID())
{
- case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
- {
- // TextDecoratedPortionPrimitive2D can produce the following primitives
- // when being decomposed:
- //
- // - TextSimplePortionPrimitive2D
- // - PolygonWavePrimitive2D
- // - PolygonStrokePrimitive2D
- // - PolygonStrokePrimitive2D
- // - PolyPolygonColorPrimitive2D
- // - PolyPolygonHairlinePrimitive2D
- // - PolygonHairlinePrimitive2D
- // - ShadowPrimitive2D
- // - ModifiedColorPrimitive2D
- // - TransformPrimitive2D
- // - TextEffectPrimitive2D
- // - ModifiedColorPrimitive2D
- // - TransformPrimitive2D
- // - GroupPrimitive2D
-
- // encapsulate with flag and use decomposition
- mnInText++;
- process(rCandidate);
- mnInText--;
-
- break;
- }
- case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
- {
- // TextSimplePortionPrimitive2D can produce the following primitives
- // when being decomposed:
- //
- // - PolyPolygonColorPrimitive2D
- // - TextEffectPrimitive2D
- // - ModifiedColorPrimitive2D
- // - TransformPrimitive2D
- // - GroupPrimitive2D
-
- // encapsulate with flag and use decomposition
- mnInText++;
- process(rCandidate);
- mnInText--;
-
- break;
- }
-
// as can be seen from the TextSimplePortionPrimitive2D and the
// TextDecoratedPortionPrimitive2D, inside of the mnInText marks
// the following primitives can occur containing geometry data
@@ -177,12 +168,8 @@ namespace drawinglayer::processor2d
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
// create new transformations for CurrentTransformation and for local ViewInformation2D
- const geometry::ViewInformation2D aViewInformation2D(
- getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
- getViewInformation2D().getViewTransformation(),
- getViewInformation2D().getViewport(),
- getViewInformation2D().getVisualizedPage(),
- getViewInformation2D().getViewTime());
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation());
updateViewInformation(aViewInformation2D);
// process content
@@ -194,29 +181,14 @@ namespace drawinglayer::processor2d
break;
}
- // ignorable primitives
- case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
- case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
- case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
- case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
- case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
- case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
- case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
- {
+ default:
+ TextExtractor2D::processBasePrimitive2D(rCandidate);
break;
- }
-
- default :
- {
- // process recursively
- process(rCandidate);
- break;
- }
}
}
TextAsPolygonExtractor2D::TextAsPolygonExtractor2D(const geometry::ViewInformation2D& rViewInformation)
- : BaseProcessor2D(rViewInformation),
+ : TextExtractor2D(rViewInformation),
maBColorModifierStack(),
mnInText(0)
{
diff --git a/drawinglayer/source/processor2d/textextractor2d.cxx b/drawinglayer/source/processor2d/textextractor2d.cxx
new file mode 100644
index 000000000000..105014a750ca
--- /dev/null
+++ b/drawinglayer/source/processor2d/textextractor2d.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawinglayer/processor2d/textextractor2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+
+namespace drawinglayer::processor2d
+{
+void TextExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
+{
+ switch (rCandidate.getPrimitive2DID())
+ {
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
+ {
+ processTextPrimitive2D(rCandidate);
+ break;
+ }
+
+ // usage of transformation stack is needed
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ {
+ // remember current transformation and ViewInformation
+ const primitive2d::TransformPrimitive2D& rTransformCandidate(
+ static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+
+ // create new transformations for CurrentTransformation and for local ViewInformation2D
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(
+ getViewInformation2D().getObjectTransformation()
+ * rTransformCandidate.getTransformation());
+ updateViewInformation(aViewInformation2D);
+
+ // process content
+ process(rTransformCandidate.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation2D);
+
+ break;
+ }
+
+ // ignorable primitives
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D:
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D:
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ break;
+
+ default:
+ {
+ // process recursively
+ process(rCandidate);
+ break;
+ }
+ }
+}
+
+TextExtractor2D::TextExtractor2D(const geometry::ViewInformation2D& rViewInformation)
+ : BaseProcessor2D(rViewInformation)
+{
+}
+
+TextExtractor2D::~TextExtractor2D() {}
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
index 9129271bcb6b..28d383230eef 100644
--- a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx
@@ -28,30 +28,39 @@
#include <basegfx/range/b2drange.hxx>
#include <vcl/bitmapex.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
-#include <tools/stream.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <vcl/timer.hxx>
-#include <cppuhelper/basemutex.hxx>
#include <vcl/lazydelete.hxx>
#include <vcl/dibtools.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+#include <mutex>
-// buffered VDev usage
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#endif
+// #define SPEED_COMPARE
+#ifdef SPEED_COMPARE
+#include <tools/time.hxx>
+#endif
+
+// buffered VDev usage
namespace
{
-class VDevBuffer : public Timer, protected cppu::BaseMutex
+class VDevBuffer : public Timer
{
private:
struct Entry
{
VclPtr<VirtualDevice> buf;
- bool isTransparent = false;
- Entry(const VclPtr<VirtualDevice>& vdev, bool bTransparent)
+ Entry(const VclPtr<VirtualDevice>& vdev)
: buf(vdev)
- , isTransparent(bTransparent)
{
}
};
+ std::mutex m_aMutex;
+
// available buffers
std::vector<Entry> maFreeBuffers;
@@ -63,11 +72,13 @@ private:
// virtualdevice because that isn't safe to do at least for Gtk2
std::map<VclPtr<VirtualDevice>, VclPtr<OutputDevice>> maDeviceTemplates;
+ static bool isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size& size);
+
public:
VDevBuffer();
virtual ~VDevBuffer() override;
- VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bTransparent);
+ VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel);
void free(VirtualDevice& rDevice);
// Timer virtuals
@@ -82,7 +93,7 @@ VDevBuffer::VDevBuffer()
VDevBuffer::~VDevBuffer()
{
- ::osl::MutexGuard aGuard(m_aMutex);
+ std::unique_lock aGuard(m_aMutex);
Stop();
while (!maFreeBuffers.empty())
@@ -98,10 +109,37 @@ VDevBuffer::~VDevBuffer()
}
}
-VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel,
- bool bTransparent)
+bool VDevBuffer::isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size& rSizePixel)
{
- ::osl::MutexGuard aGuard(m_aMutex);
+ if (device->GetOutputWidthPixel() >= rSizePixel.getWidth()
+ && device->GetOutputHeightPixel() >= rSizePixel.getHeight())
+ {
+ bool requireSmall = false;
+#if defined(UNX)
+ // HACK: See the small size handling in SvpSalVirtualDevice::CreateSurface().
+ // Make sure to not reuse a larger device when a small one should be preferred.
+ if (device->GetRenderBackendName() == "svp")
+ requireSmall = true;
+#endif
+ // The same for Skia, see renderMethodToUseForSize().
+ if (SkiaHelper::isVCLSkiaEnabled())
+ requireSmall = true;
+ if (requireSmall)
+ {
+ if (rSizePixel.getWidth() <= 32 && rSizePixel.getHeight() <= 32
+ && (device->GetOutputWidthPixel() > 32 || device->GetOutputHeightPixel() > 32))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel)
+{
+ std::unique_lock aGuard(m_aMutex);
VclPtr<VirtualDevice> pRetval;
sal_Int32 nBits = rOutDev.GetBitCount();
@@ -115,7 +153,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
{
assert(a->buf && "Empty pointer in VDevBuffer (!)");
- if (nBits == a->buf->GetBitCount() && bTransparent == a->isTransparent)
+ if (nBits == a->buf->GetBitCount())
{
// candidate is valid due to bit depth
if (aFound != maFreeBuffers.end())
@@ -124,9 +162,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
if (bOkay)
{
// found is valid
- const bool bCandidateOkay(
- a->buf->GetOutputWidthPixel() >= rSizePixel.getWidth()
- && a->buf->GetOutputHeightPixel() >= rSizePixel.getHeight());
+ const bool bCandidateOkay = isSizeSuitable(a->buf, rSizePixel);
if (bCandidateOkay)
{
@@ -151,16 +187,14 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
{
// found is invalid, use candidate
aFound = a;
- bOkay = aFound->buf->GetOutputWidthPixel() >= rSizePixel.getWidth()
- && aFound->buf->GetOutputHeightPixel() >= rSizePixel.getHeight();
+ bOkay = isSizeSuitable(aFound->buf, rSizePixel);
}
}
else
{
// none yet, use candidate
aFound = a;
- bOkay = aFound->buf->GetOutputWidthPixel() >= rSizePixel.getWidth()
- && aFound->buf->GetOutputHeightPixel() >= rSizePixel.getHeight();
+ bOkay = isSizeSuitable(aFound->buf, rSizePixel);
}
}
}
@@ -200,9 +234,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
// no success yet, create new buffer
if (!pRetval)
{
- pRetval = VclPtr<VirtualDevice>::Create(rOutDev, DeviceFormat::DEFAULT,
- bTransparent ? DeviceFormat::DEFAULT
- : DeviceFormat::NONE);
+ pRetval = VclPtr<VirtualDevice>::Create(rOutDev, DeviceFormat::WITHOUT_ALPHA);
maDeviceTemplates[pRetval] = &rOutDev;
pRetval->SetOutputSizePixel(rSizePixel, true);
}
@@ -214,14 +246,14 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize
}
// remember allocated buffer
- maUsedBuffers.emplace_back(pRetval, bTransparent);
+ maUsedBuffers.emplace_back(pRetval);
return pRetval;
}
void VDevBuffer::free(VirtualDevice& rDevice)
{
- ::osl::MutexGuard aGuard(m_aMutex);
+ std::unique_lock aGuard(m_aMutex);
const auto aUsedFound
= std::find_if(maUsedBuffers.begin(), maUsedBuffers.end(),
[&rDevice](const Entry& el) { return el.buf == &rDevice; });
@@ -239,7 +271,7 @@ void VDevBuffer::free(VirtualDevice& rDevice)
void VDevBuffer::Invoke()
{
- ::osl::MutexGuard aGuard(m_aMutex);
+ std::unique_lock aGuard(m_aMutex);
while (!maFreeBuffers.empty())
{
@@ -249,13 +281,103 @@ void VDevBuffer::Invoke()
maFreeBuffers.pop_back();
}
}
+
+#ifdef SPEED_COMPARE
+void doSpeedCompare(double fTrans, const Bitmap& rContent, const tools::Rectangle& rDestPixel,
+ OutputDevice& rOutDev)
+{
+ const int nAvInd(500);
+ static double fFactors[nAvInd];
+ static int nIndex(nAvInd + 1);
+ static int nRepeat(5);
+ static int nWorseTotal(0);
+ static int nBetterTotal(0);
+ int a(0);
+
+ const Size aSizePixel(rDestPixel.GetSize());
+
+ // init statics
+ if (nIndex > nAvInd)
+ {
+ for (a = 0; a < nAvInd; a++)
+ fFactors[a] = 1.0;
+ nIndex = 0;
+ }
+
+ // get start time
+ const sal_uInt64 nTimeA(tools::Time::GetSystemTicks());
+
+ // loop nRepeat times to get somewhat better timings, else
+ // numbers are pretty small
+ for (a = 0; a < nRepeat; a++)
+ {
+ // "Former" method using a temporary AlphaMask & DrawBitmapEx
+ sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0)));
+ const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
+ rOutDev.DrawBitmapEx(rDestPixel.TopLeft(), BitmapEx(rContent, aAlphaMask));
+ }
+
+ // get intermediate time
+ const sal_uInt64 nTimeB(tools::Time::GetSystemTicks());
+
+ // loop nRepeat times
+ for (a = 0; a < nRepeat; a++)
+ {
+ // New method using DrawTransformedBitmapEx & fTrans directly
+ rOutDev.DrawTransformedBitmapEx(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aSizePixel.Width(), aSizePixel.Height(),
+ rDestPixel.TopLeft().X(), rDestPixel.TopLeft().Y()),
+ BitmapEx(rContent), 1 - fTrans);
+ }
+
+ // get end time
+ const sal_uInt64 nTimeC(tools::Time::GetSystemTicks());
+
+ // calculate deltas
+ const sal_uInt64 nTimeFormer(nTimeB - nTimeA);
+ const sal_uInt64 nTimeNew(nTimeC - nTimeB);
+
+ // compare & note down
+ if (nTimeFormer != nTimeNew && 0 != nTimeFormer && 0 != nTimeNew)
+ {
+ if ((nTimeFormer < 10 || nTimeNew < 10) && nRepeat < 500)
+ {
+ nRepeat += 1;
+ SAL_INFO("drawinglayer.processor2d", "Increment nRepeat to " << nRepeat);
+ return;
+ }
+
+ const double fNewFactor((double)nTimeFormer / nTimeNew);
+ fFactors[nIndex % nAvInd] = fNewFactor;
+ nIndex++;
+ double fAverage(0.0);
+ {
+ for (a = 0; a < nAvInd; a++)
+ fAverage += fFactors[a];
+ fAverage /= nAvInd;
+ }
+ if (fNewFactor < 1.0)
+ nWorseTotal++;
+ else
+ nBetterTotal++;
+
+ char buf[300];
+ sprintf(buf,
+ "Former: %ld New: %ld It got %s (factor %f) (av. last %d Former/New is %f, "
+ "WorseTotal: %d, BetterTotal: %d)",
+ nTimeFormer, nTimeNew, fNewFactor < 1.0 ? "WORSE" : "BETTER",
+ fNewFactor < 1.0 ? 1.0 / fNewFactor : fNewFactor, nAvInd, fAverage, nWorseTotal,
+ nBetterTotal);
+ SAL_INFO("drawinglayer.processor2d", buf);
+ }
+}
+#endif
}
// support for rendering Bitmap and BitmapEx contents
-
namespace drawinglayer
{
-// static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D)
+// static global VDev buffer for VclProcessor2D/VclPixelProcessor2D
VDevBuffer& getVDevBuffer()
{
// secure global instance with Vcl's safe destroyer of external (seen by
@@ -272,22 +394,28 @@ impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange&
{
basegfx::B2DRange aRangePixel(rRange);
aRangePixel.transform(mrOutDev.GetViewTransformation());
- const ::tools::Rectangle aRectPixel(floor(aRangePixel.getMinX()), floor(aRangePixel.getMinY()),
- ceil(aRangePixel.getMaxX()), ceil(aRangePixel.getMaxY()));
- const Point aEmptyPoint;
- maDestPixel = ::tools::Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel());
- maDestPixel.Intersection(aRectPixel);
+ maDestPixel = tools::Rectangle(floor(aRangePixel.getMinX()), floor(aRangePixel.getMinY()),
+ ceil(aRangePixel.getMaxX()), ceil(aRangePixel.getMaxY()));
+ maDestPixel.Intersection(tools::Rectangle{ Point{}, mrOutDev.GetOutputSizePixel() });
if (!isVisible())
return;
- mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true);
+ mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize());
// #i93485# assert when copying from window to VDev is used
SAL_WARN_IF(
mrOutDev.GetOutDevType() == OUTDEV_WINDOW, "drawinglayer",
"impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)");
+ // initialize buffer by blitting content of source to prepare for
+ // transparence/ copying back
+ const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled());
+ mrOutDev.EnableMapMode(false);
+ mpContent->DrawOutDev(Point(), maDestPixel.GetSize(), maDestPixel.TopLeft(),
+ maDestPixel.GetSize(), mrOutDev);
+ mrOutDev.EnableMapMode(bWasEnabledSrc);
+
MapMode aNewMapMode(mrOutDev.GetMapMode());
const Point aLogicTopLeft(mrOutDev.PixelToLogic(maDestPixel.TopLeft()));
@@ -323,23 +451,18 @@ void impBufferDevice::paint(double fTrans)
const Point aEmptyPoint;
const Size aSizePixel(maDestPixel.GetSize());
const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled());
-#ifdef DBG_UTIL
- static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
-#endif
mrOutDev.EnableMapMode(false);
mpContent->EnableMapMode(false);
#ifdef DBG_UTIL
- if (bDoSaveForVisualControl)
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ static const OUString sDumpPath(OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+
+ if (!sDumpPath.isEmpty() && bDoSaveForVisualControl)
{
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\content.bmp",
-#else
- "~/content.bmp",
-#endif
- StreamMode::WRITE | StreamMode::TRUNC);
+ SvFileStream aNew(sDumpPath + "content.bmp", StreamMode::WRITE | StreamMode::TRUNC);
Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
WriteDIB(aContent, aNew, false, true);
}
@@ -353,32 +476,86 @@ void impBufferDevice::paint(double fTrans)
{
mpAlpha->EnableMapMode(false);
AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel));
+ aAlphaMask.Invert(); // convert transparency to alpha
#ifdef DBG_UTIL
- if (bDoSaveForVisualControl)
+ if (!sDumpPath.isEmpty() && bDoSaveForVisualControl)
{
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\transparence.bmp",
-#else
- "~/transparence.bmp",
-#endif
- StreamMode::WRITE | StreamMode::TRUNC);
+ SvFileStream aNew(sDumpPath + "transparence.bmp",
+ StreamMode::WRITE | StreamMode::TRUNC);
WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true);
}
#endif
- BitmapEx aContent(mpContent->GetBitmapEx(aEmptyPoint, aSizePixel));
- aAlphaMask.BlendWith(aContent.GetAlpha());
- mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent.GetBitmap(), aAlphaMask));
+ Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
+ mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
}
else if (0.0 != fTrans)
{
- basegfx::B2DHomMatrix trans, scale;
- trans.translate(maDestPixel.TopLeft().X(), maDestPixel.TopLeft().Y());
- scale.scale(aSizePixel.Width(), aSizePixel.Height());
- mrOutDev.DrawTransformedBitmapEx(
- trans * scale, mpContent->GetBitmapEx(aEmptyPoint, aSizePixel), 1 - fTrans);
+ const Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel));
+
+#ifdef SPEED_COMPARE
+ static bool bCompareFormerAndNewTimings(true);
+
+ if (bCompareFormerAndNewTimings)
+ {
+ doSpeedCompare(fTrans, aContent, maDestPixel, mrOutDev);
+ }
+ else
+#endif
+ // Note: this extra scope is needed due to 'clang plugin indentation'. It complains
+ // that lines 494 and (now) 539 are 'statement mis-aligned compared to neighbours'.
+ // That is true if SPEED_COMPARE is not defined. Not nice, but have to fix this.
+ {
+ // For the case we have a unified transparency value there is a former
+ // and new method to paint that which can be used. To decide on measurements,
+ // I added 'doSpeedCompare' above which can be activated by defining
+ // SPEED_COMPARE at the top of this file.
+ // I added the used Testdoc: blurplay3.odg as
+ // https://bugs.documentfoundation.org/attachment.cgi?id=182463
+ // I did measure on
+ //
+ // Linux Dbg:
+ // Former: 21 New: 32 It got WORSE (factor 1.523810) (av. last 500 Former/New is 0.968533, WorseTotal: 515, BetterTotal: 934)
+ //
+ // Linux Pro:
+ // Former: 27 New: 44 It got WORSE (factor 1.629630) (av. last 500 Former/New is 0.923256, WorseTotal: 433, BetterTotal: 337)
+ //
+ // Win Dbg:
+ // Former: 21 New: 78 It got WORSE (factor 3.714286) (av. last 500 Former/New is 1.007176, WorseTotal: 85, BetterTotal: 1428)
+ //
+ // Win Pro:
+ // Former: 3 New: 4 It got WORSE (factor 1.333333) (av. last 500 Former/New is 1.054167, WorseTotal: 143, BetterTotal: 3909)
+ //
+ // Note: I am aware that the Dbg are of limited usefulness, but include them here
+ // for reference.
+ //
+ // The important part is "av. last 500 Former/New is %ld" which describes the averaged factor from Former/New
+ // over the last 500 measurements. When < 1.0 Former is better (Linux), > 1.0 (Win) New is better. Since the
+ // factor on Win is still close to 1.0 what means we lose nearly nothing and Linux Former is better, I will
+ // use Former for now.
+ //
+ // To easily allow to change this (maybe system-dependent) I add a static switch here,
+ // also for eventually experimenting (hint: can be changed in the debugger).
+ static bool bUseNew(false);
+
+ if (bUseNew)
+ {
+ // New method using DrawTransformedBitmapEx & fTrans directly
+ mrOutDev.DrawTransformedBitmapEx(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aSizePixel.Width(), aSizePixel.Height(),
+ maDestPixel.TopLeft().X(),
+ maDestPixel.TopLeft().Y()),
+ BitmapEx(aContent), 1 - fTrans);
+ }
+ else
+ {
+ // "Former" method using a temporary AlphaMask & DrawBitmapEx
+ sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0)));
+ const AlphaMask aAlphaMask(aSizePixel, &nMaskValue);
+ mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aAlphaMask));
+ }
+ }
}
else
{
@@ -402,7 +579,7 @@ VirtualDevice& impBufferDevice::getTransparence()
"impBufferDevice: No content, check isVisible() before accessing (!)");
if (!mpAlpha)
{
- mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false);
+ mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize());
mpAlpha->SetMapMode(mpContent->GetMapMode());
// copy AA flag for new target; masking needs to be smooth
diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
index 3b5d30415cc2..618fb38209a5 100644
--- a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
+++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx
@@ -21,12 +21,73 @@
#include <vcl/virdev.hxx>
-namespace basegfx
-{
-class B2DRange;
-}
-
-// support methods for vcl direct gradient rendering
+// Helper class *exclusively* for VclProcessor2D. It should only
+// be used internally, see current four usages. It is used to
+// render something with mask or transparence (see MaskPrimitive2D,
+// UnifiedTransparencePrimitive2D and TransparencePrimitive2D) or
+// as tooling for preparing pixelized output in the renderer
+// (see PatternFillPrimitive2D) if that is faster.
+//
+// To do so, initializing this instance takes over a lot of work
+// from you:
+// - It initializes a 'Content' VDev which is buffered (since it had
+// shown that re-allocating all the time is slower). It checks
+// visibility and all usages initializing this should check for
+// isVisible() after construction.
+// - It pre-initializes the 'Content' VDev with blitting the content
+// of the target VDev.
+// - It offers to get a 'Transparence' VDev (also from the buffer) if
+// needed.
+//
+// If 'Transparence' is/was used, it combines as needed to paint
+// all buffered stuff to target VDev when calling paint().
+// Caution: It is designed to use *either* a fixed transparence in
+// the paint()-call *or* a fill TransparenceChannel using a
+// 'Transparence' VDev. It is *not* designed to use/combine
+// both - it's simply not needed for it's intended purpose/usage.
+//
+// Painting transparent works based on a simple principle: It first
+// blits the original content of the target VDev. Then the content
+// is painted on top of that, plus a Transparence/Mask prepared.
+// The combination always works since unchanged pixels will not
+// change, independent of the transparence value [0..255] it gets
+// mixed with. Or the other way around: Only pixels changed on the
+// Content *can* be changed by transparence values.
+//
+// This is 2.5 times faster than first painting to a
+// 'combined' VDev that supports transparency, as is used by the
+// presentation engine.
+// For the mentioned factor refer to:
+// Patch to demonstrate former and now repaint differences
+// https://gerrit.libreoffice.org/c/core/+/129301
+// git fetch https://git.libreoffice.org/core refs/changes/01/129301/3 && git cherry-pick FETCH_HEAD
+//
+// Note: This principle only works when the target is RGB, so
+// useful for EditViews like for PrimitiveRenderers where this is
+// the case. For usage with GBA targets the starting conditions
+// would have to be modified to not blend against the initial color
+// of 'Content' (usually COL_WHITE).
+//
+// After having finished the rework of ShadowPrimitive2D,
+// SoftEdgePrimitive2D and GlowPrimitive2D (see commits:)
+// e735ad1c57cddaf17d6ffc0cf15b5e14fa63c4ad
+// 707b0c328a282d993fa33b618083d20b6c521de6
+// c2d1458723c66c2fd717a112f89f773226adc841
+// which used the impBufferDevice in such a mode combined with
+// mentioned 'combined' transparence VDev it is now possible
+// to return to this former, much faster method.
+//
+// Please do *not* hack/use this helper class, better create
+// a new one fitting your/the intended purpose. I do not want
+// to see losing performance by this getting modified again.
+//
+// Note: Using that 'combined' transparence VDev is not really
+// recommended, it may vanish anytime. That it works with
+// PrimitiveRenderers *at all* is neither designed nor tested
+// or recommended - it's pure coincidence.
+//
+// I hope that for the future all this will vanish by getting to
+// fully RGBA-capable devices - what is planned and makes sense.
namespace drawinglayer
{
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index b679768c9c3b..d93d98fef51a 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -24,27 +24,30 @@
#include <rtl/ustring.hxx>
#include <tools/gen.hxx>
#include <tools/stream.hxx>
-#include <tools/diagnose_ex.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <comphelper/flagguard.hxx>
#include <comphelper/processfactory.hxx>
#include <config_global.h>
#include <basegfx/polygon/b2dpolygonclipper.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/utils/gradienttools.hxx>
#include <vcl/virdev.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/gradient.hxx>
#include <vcl/graphictools.hxx>
#include <vcl/metaact.hxx>
#include <vcl/graph.hxx> // for PDFExtOutDevData Graphic support
-#include <toolkit/helper/formpdfexport.hxx> // for PDFExtOutDevData Graphic support
+#include <vcl/formpdfexport.hxx> // for PDFExtOutDevData Graphic support
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
@@ -61,6 +64,9 @@
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
#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>
@@ -220,8 +226,10 @@ VclMetafileProcessor2D::impDumpToMetaFile(const primitive2d::Primitive2DContaine
aPrimitiveRange.transform(maCurrentTransformation);
const tools::Rectangle aPrimitiveRectangle(
- basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()),
- basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY()));
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMinX()),
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMinY()),
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMaxX()),
+ basegfx::fround<tools::Long>(aPrimitiveRange.getMaxY()));
ScopedVclPtrInstance<VirtualDevice> aContentVDev;
MapMode aNewMapMode(pLastOutputDevice->GetMapMode());
@@ -256,19 +264,20 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt,
bool bIsTransparenceGradient) const
{
+ const basegfx::BColor aStartColor(rFiGrAtt.getColorStops().front().getStopColor());
+ const basegfx::BColor aEndColor(rFiGrAtt.getColorStops().back().getStopColor());
+
if (bIsTransparenceGradient)
{
// it's about transparence channel intensities (black/white), do not use color modifier
- o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor()));
- o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor()));
+ o_rVCLGradient.SetStartColor(Color(aStartColor));
+ o_rVCLGradient.SetEndColor(Color(aEndColor));
}
else
{
// use color modifier to influence start/end color of gradient
- o_rVCLGradient.SetStartColor(
- Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor())));
- o_rVCLGradient.SetEndColor(
- Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor())));
+ o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(aStartColor)));
+ o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(aEndColor)));
}
o_rVCLGradient.SetAngle(
@@ -281,40 +290,7 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
// defaults for intensity; those were computed into the start/end colors already
o_rVCLGradient.SetStartIntensity(100);
o_rVCLGradient.SetEndIntensity(100);
-
- switch (rFiGrAtt.getStyle())
- {
- default: // attribute::GradientStyle::Linear :
- {
- o_rVCLGradient.SetStyle(GradientStyle::Linear);
- break;
- }
- case attribute::GradientStyle::Axial:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Axial);
- break;
- }
- case attribute::GradientStyle::Radial:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Radial);
- break;
- }
- case attribute::GradientStyle::Elliptical:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Elliptical);
- break;
- }
- case attribute::GradientStyle::Square:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Square);
- break;
- }
- case attribute::GradientStyle::Rect:
- {
- o_rVCLGradient.SetStyle(GradientStyle::Rect);
- break;
- }
- }
+ o_rVCLGradient.SetStyle(rFiGrAtt.getStyle());
}
void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill)
@@ -325,7 +301,7 @@ void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGr
WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill);
mpMetaFile->AddAction(new MetaCommentAction(
- "XPATHFILL_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ "XPATHFILL_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
aMemStm.TellEnd()));
mnSvtGraphicFillCount++;
}
@@ -336,7 +312,7 @@ void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGrap
if (pSvtGraphicFill && mnSvtGraphicFillCount)
{
mnSvtGraphicFillCount--;
- mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"_ostr));
}
}
@@ -509,7 +485,7 @@ void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const* pS
WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke);
mpMetaFile->AddAction(new MetaCommentAction(
- "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
+ "XPATHSTROKE_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()),
aMemStm.TellEnd()));
mnSvtGraphicStrokeCount++;
}
@@ -520,7 +496,7 @@ void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const* pSvt
if (pSvtGraphicStroke && mnSvtGraphicStrokeCount)
{
mnSvtGraphicStrokeCount--;
- mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"_ostr));
}
}
@@ -546,7 +522,8 @@ void VclMetafileProcessor2D::popList()
}
// init static break iterator
-uno::Reference<css::i18n::XBreakIterator> VclMetafileProcessor2D::mxBreakIterator;
+vcl::DeleteOnDeinit<uno::Reference<css::i18n::XBreakIterator>>
+ VclMetafileProcessor2D::mxBreakIterator;
VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation,
OutputDevice& rOutDev)
@@ -718,17 +695,7 @@ VclMetafileProcessor2D::~VclMetafileProcessor2D()
- UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
- This may be added in primitive MetaFile renderer.
- Adding support...
- OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
- svxform. Have to talk to FS if this has to be like that. Especially since
- vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
- Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
- that stuff to somewhere else, maybe tools or svtools ?!? We will see...
- Moved to toolkit, so I have to link against it. I tried VCL first, but it did
- not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other than the name
- may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
- the lowest movement plane is toolkit.
+ This was added in primitive MetaFile renderer.
Checked form control export, it works well. Done.
- In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
@@ -836,8 +803,17 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
}
case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
{
- processPolyPolygonGraphicPrimitive2D(
- static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
+ if (maBColorModifierStack.count())
+ {
+ // tdf#151104 unfortunately processPolyPolygonGraphicPrimitive2D below
+ // does not support an active BColorModifierStack, so use the default
+ process(rCandidate);
+ }
+ else
+ {
+ processPolyPolygonGraphicPrimitive2D(
+ static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate));
+ }
break;
}
case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D:
@@ -923,17 +899,10 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
}
case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
{
- RenderObjectInfoPrimitive2D(
+ processObjectInfoPrimitive2D(
static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
break;
}
- case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
- case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
- case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
- {
- processPrimitive2DOnPixelProcessor(rCandidate);
- break;
- }
default:
{
// process recursively
@@ -943,6 +912,51 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
}
}
+void VclMetafileProcessor2D::processObjectInfoPrimitive2D(
+ primitive2d::ObjectInfoPrimitive2D const& rObjectInfoPrimitive2D)
+{
+ // tdf#154982 process content first, so this object overrides any nested one
+ process(rObjectInfoPrimitive2D.getChildren());
+
+ // currently StructureTagPrimitive2D is only used for SdrObjects - have to
+ // avoid adding Alt text if the SdrObject is not actually tagged, as it
+ // would then end up on an unrelated structure element.
+ if (mpCurrentStructureTag && mpCurrentStructureTag->isTaggedSdrObject())
+ {
+ // Create image alternative description from ObjectInfoPrimitive2D info
+ // for PDF export, for the currently active SdrObject's structure element
+ if (mpPDFExtOutDevData->GetIsExportTaggedPDF())
+ {
+ OUString aAlternateDescription;
+
+ if (!rObjectInfoPrimitive2D.getTitle().isEmpty())
+ {
+ aAlternateDescription += rObjectInfoPrimitive2D.getTitle();
+ }
+
+ if (!rObjectInfoPrimitive2D.getDesc().isEmpty())
+ {
+ if (!aAlternateDescription.isEmpty())
+ {
+ aAlternateDescription += " - ";
+ }
+
+ aAlternateDescription += rObjectInfoPrimitive2D.getDesc();
+ }
+
+ // Use SetAlternateText to set it. This will work as long as some
+ // structure is used (see PDFWriterImpl::setAlternateText and
+ // m_nCurrentStructElement - tagged PDF export works with this in
+ // Draw/Impress/Writer, but not in Calc due to too less structure...)
+ //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
+ if (!aAlternateDescription.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
+ }
+ }
+ }
+}
+
void VclMetafileProcessor2D::processGraphicPrimitive2D(
const primitive2d::GraphicPrimitive2D& rGraphicPrimitive)
{
@@ -1034,38 +1048,6 @@ void VclMetafileProcessor2D::processGraphicPrimitive2D(
sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
}
- // Create image alternative description from ObjectInfoPrimitive2D info
- // for PDF export
- if (mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D())
- {
- OUString aAlternateDescription;
-
- if (!getObjectInfoPrimitive2D()->getTitle().isEmpty())
- {
- aAlternateDescription += getObjectInfoPrimitive2D()->getTitle();
- }
-
- if (!getObjectInfoPrimitive2D()->getDesc().isEmpty())
- {
- if (!aAlternateDescription.isEmpty())
- {
- aAlternateDescription += " - ";
- }
-
- aAlternateDescription += getObjectInfoPrimitive2D()->getDesc();
- }
-
- // Use SetAlternateText to set it. This will work as long as some
- // structure is used (see PDFWriterImpl::setAlternateText and
- // m_nCurrentStructElement - tagged PDF export works with this in
- // Draw/Impress/Writer, but not in Calc due to too less structure...)
- //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..?
- if (!aAlternateDescription.isEmpty())
- {
- mpPDFExtOutDevData->SetAlternateText(aAlternateDescription);
- }
- }
-
// #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
// object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
// uncropped region. Thus, correct order is aCropRect, aCurrentRect
@@ -1089,7 +1071,7 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
uno::Reference<beans::XPropertySetInfo> xPropertyInfo(
xModelProperties.is() ? xModelProperties->getPropertySetInfo()
: uno::Reference<beans::XPropertySetInfo>());
- static const OUStringLiteral sPrintablePropertyName(u"Printable");
+ static constexpr OUString sPrintablePropertyName(u"Printable"_ustr);
if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
{
@@ -1108,14 +1090,25 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
if (!bIsPrintableControl)
return;
+ ::std::optional<sal_Int32> oAnchorParent;
+ if (mpPDFExtOutDevData)
+ {
+ if (rControlPrimitive.GetAnchorStructureElementKey())
+ {
+ sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(
+ rControlPrimitive.GetAnchorStructureElementKey());
+ oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement());
+ mpPDFExtOutDevData->SetCurrentStructureElement(id);
+ }
+ }
+
const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
bool bDoProcessRecursively(true);
+ bool bDecorative = (mpCurrentStructureTag && mpCurrentStructureTag->isDecorative());
- if (bPDFExport)
+ if (bPDFExport && !bDecorative)
{
// PDF export. Emulate data handling from UnoControlPDFExportContact
- // I have now moved describePDFControl to toolkit, thus i can implement the PDF
- // form control support now as follows
std::unique_ptr<vcl::PDFWriter::AnyWidget> pPDFControl(
::toolkitform::describePDFControl(rXControl, *mpPDFExtOutDevData));
@@ -1135,9 +1128,37 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
mpOutputDevice->GetMapMode());
pPDFControl->TextFont.SetFontSize(aFontSize);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
+ vcl::PDFWriter::StructAttributeValue role;
+ switch (pPDFControl->Type)
+ {
+ case vcl::PDFWriter::PushButton:
+ role = vcl::PDFWriter::Pb;
+ break;
+ case vcl::PDFWriter::RadioButton:
+ role = vcl::PDFWriter::Rb;
+ break;
+ case vcl::PDFWriter::CheckBox:
+ role = vcl::PDFWriter::Cb;
+ break;
+ default: // there is a paucity of roles, tv is the catch-all one
+ role = vcl::PDFWriter::Tv;
+ break;
+ }
+ // ISO 14289-1:2014, Clause: 7.18.4
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Role, role);
+ // ISO 14289-1:2014, Clause: 7.18.1
+ OUString const& rAltText(rControlPrimitive.GetAltText());
+ if (!rAltText.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(rAltText);
+ }
mpPDFExtOutDevData->CreateControl(*pPDFControl);
mpPDFExtOutDevData->EndStructureElement();
+ if (oAnchorParent)
+ {
+ mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent);
+ }
// no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
// do not process recursively
@@ -1151,6 +1172,31 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
}
}
+ if (!bDoProcessRecursively)
+ {
+ return;
+ }
+
+ if (mpPDFExtOutDevData)
+ { // no corresponding PDF Form, use Figure instead
+ if (!bDecorative)
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Figure);
+ else
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement, vcl::PDFWriter::Block);
+ auto const range(rControlPrimitive.getB2DRange(getViewInformation2D()));
+ tools::Rectangle const aLogicRect(basegfx::fround<tools::Long>(range.getMinX()),
+ basegfx::fround<tools::Long>(range.getMinY()),
+ basegfx::fround<tools::Long>(range.getMaxX()),
+ basegfx::fround<tools::Long>(range.getMaxY()));
+ mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect);
+ OUString const& rAltText(rControlPrimitive.GetAltText());
+ if (!rAltText.isEmpty() && !bDecorative)
+ {
+ mpPDFExtOutDevData->SetAlternateText(rAltText);
+ }
+ }
+
// #i93169# used flag the wrong way; true means that nothing was done yet
if (bDoProcessRecursively)
{
@@ -1195,6 +1241,15 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
{
process(rControlPrimitive);
}
+
+ if (mpPDFExtOutDevData)
+ {
+ mpPDFExtOutDevData->EndStructureElement();
+ if (oAnchorParent)
+ {
+ mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent);
+ }
+ }
}
void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
@@ -1202,7 +1257,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
{
// support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
// thus do the MetafileAction embedding stuff but just handle recursively.
- const OString aCommentStringCommon("FIELD_SEQ_BEGIN");
+ static constexpr OString aCommentStringCommon("FIELD_SEQ_BEGIN"_ostr);
OUString aURL;
switch (rFieldPrimitive.getType())
@@ -1214,7 +1269,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
}
case drawinglayer::primitive2d::FIELD_TYPE_PAGE:
{
- mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"));
+ mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"_ostr));
break;
}
case drawinglayer::primitive2d::FIELD_TYPE_URL:
@@ -1238,7 +1293,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
process(rContent);
// for the end comment the type is not relevant yet, they are all the same. Just add.
- mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"_ostr));
if (!(mpPDFExtOutDevData
&& drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType()))
@@ -1251,7 +1306,8 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
vcl::PDFExtOutDevBookmarkEntry aBookmark;
- aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
+ OUString const content(rFieldPrimitive.getValue("Representation"));
+ aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic, content);
aBookmark.aBookmark = aURL;
std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
rBookmarks.push_back(aBookmark);
@@ -1262,7 +1318,7 @@ void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D(
{
// process recursively and add MetaFile comment
process(rLinePrimitive);
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"_ostr));
}
void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
@@ -1272,14 +1328,14 @@ void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
if (mbInListItem)
{
maListElements.push(vcl::PDFWriter::LILabel);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::LILabel);
}
// process recursively and add MetaFile comment
process(rBulletPrimitive);
// in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
// "XTEXT_EOC" is used, use here, too.
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"_ostr));
if (mbInListItem)
{
@@ -1295,7 +1351,7 @@ void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive)
{
- const OString aCommentString("XTEXT_EOP");
+ static constexpr OString aCommentString("XTEXT_EOP"_ostr);
static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore
if (nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport)
@@ -1311,7 +1367,7 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
{
// No Tagged PDF -> Dump as Paragraph
// Emulate data handling from old ImpEditEngine::Paint
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
// Process recursively and add MetaFile comment
process(rParagraphPrimitive);
@@ -1337,7 +1393,7 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a)
{
maListElements.push(vcl::PDFWriter::List);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::List);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::List);
}
}
else // if(nNewOutlineLevel < mnCurrentOutlineLevel)
@@ -1368,13 +1424,13 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
{
// Dump as ListItem
maListElements.push(vcl::PDFWriter::ListItem);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::ListItem);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::ListItem);
mbInListItem = true;
}
else
{
// Dump as Paragraph
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
}
// Process recursively and add MetaFile comment
@@ -1391,7 +1447,7 @@ void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D(
const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive)
{
// add MetaFile comment, process recursively and add MetaFile comment
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"_ostr));
process(rBlockPrimitive);
if (mnCurrentOutlineLevel >= 0)
@@ -1403,7 +1459,7 @@ void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D(
}
}
- mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"));
+ mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"_ostr));
}
void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
@@ -1418,7 +1474,7 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
if (mbInListItem && mbBulletPresent)
{
maListElements.push(vcl::PDFWriter::LIBody);
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::LIBody);
}
// directdraw of text simple portion; use default processing
@@ -1433,12 +1489,13 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
// #i101169# if(pTextDecoratedCandidate)
{
// support for TEXT_ MetaFile actions only for decorated texts
- if (!mxBreakIterator.is())
+ if (!mxBreakIterator.get() || !mxBreakIterator.get()->get())
{
uno::Reference<uno::XComponentContext> xContext(
::comphelper::getProcessComponentContext());
- mxBreakIterator = i18n::BreakIterator::create(xContext);
+ mxBreakIterator.set(i18n::BreakIterator::create(xContext));
}
+ auto& rBreakIterator = *mxBreakIterator.get()->get();
const OUString& rTxt = rTextCandidate.getText();
const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
@@ -1449,16 +1506,16 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
sal_Int32 nDone;
- sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(
+ sal_Int32 nNextCellBreak(rBreakIterator.nextCharacters(
rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0,
nDone));
- css::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(
+ css::i18n::Boundary nNextWordBoundary(rBreakIterator.getWordBoundary(
rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true));
sal_Int32 nNextSentenceBreak(
- mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
- const OString aCommentStringA("XTEXT_EOC");
- const OString aCommentStringB("XTEXT_EOW");
- const OString aCommentStringC("XTEXT_EOS");
+ rBreakIterator.endOfSentence(rTxt, nTextPosition, rLocale));
+ static constexpr OStringLiteral aCommentStringA("XTEXT_EOC");
+ static constexpr OStringLiteral aCommentStringB("XTEXT_EOW");
+ static constexpr OStringLiteral aCommentStringC("XTEXT_EOS");
for (sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
{
@@ -1467,21 +1524,21 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
{
mpMetaFile->AddAction(
new MetaCommentAction(aCommentStringA, i - nTextPosition));
- nNextCellBreak = mxBreakIterator->nextCharacters(
+ nNextCellBreak = rBreakIterator.nextCharacters(
rTxt, i, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
}
if (i == nNextWordBoundary.endPos)
{
mpMetaFile->AddAction(
new MetaCommentAction(aCommentStringB, i - nTextPosition));
- nNextWordBoundary = mxBreakIterator->getWordBoundary(
+ nNextWordBoundary = rBreakIterator.getWordBoundary(
rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true);
}
if (i == nNextSentenceBreak)
{
mpMetaFile->AddAction(
new MetaCommentAction(aCommentStringC, i - nTextPosition));
- nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale);
+ nNextSentenceBreak = rBreakIterator.endOfSentence(rTxt, i + 1, rLocale);
}
}
}
@@ -1500,9 +1557,11 @@ void VclMetafileProcessor2D::processPolygonHairlinePrimitive2D(
basegfx::B2DPolygon aLeft, aRight;
splitLinePolygon(rBasePolygon, aLeft, aRight);
rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPLeft(
- new primitive2d::PolygonHairlinePrimitive2D(aLeft, rHairlinePrimitive.getBColor()));
+ new primitive2d::PolygonHairlinePrimitive2D(std::move(aLeft),
+ rHairlinePrimitive.getBColor()));
rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPRight(
- new primitive2d::PolygonHairlinePrimitive2D(aRight, rHairlinePrimitive.getBColor()));
+ new primitive2d::PolygonHairlinePrimitive2D(std::move(aRight),
+ rHairlinePrimitive.getBColor()));
processBasePrimitive2D(*xPLeft);
processBasePrimitive2D(*xPRight);
@@ -1550,10 +1609,12 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
basegfx::B2DPolygon aLeft, aRight;
splitLinePolygon(rBasePolygon, aLeft, aRight);
rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPLeft(
- new primitive2d::PolygonStrokePrimitive2D(aLeft, rStrokePrimitive.getLineAttribute(),
+ new primitive2d::PolygonStrokePrimitive2D(std::move(aLeft),
+ rStrokePrimitive.getLineAttribute(),
rStrokePrimitive.getStrokeAttribute()));
rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPRight(
- new primitive2d::PolygonStrokePrimitive2D(aRight, rStrokePrimitive.getLineAttribute(),
+ new primitive2d::PolygonStrokePrimitive2D(std::move(aRight),
+ rStrokePrimitive.getLineAttribute(),
rStrokePrimitive.getStrokeAttribute()));
processBasePrimitive2D(*xPLeft);
@@ -1572,7 +1633,7 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
// create MetaPolyLineActions, but without LineStyle::Dash
- if (basegfx::fTools::more(rLine.getWidth(), 0.0))
+ if (rLine.getWidth() > 0.0)
{
const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
@@ -1583,7 +1644,7 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D(
// use the transformed line width
LineInfo aLineInfo(LineStyle::Solid,
- basegfx::fround(getTransformedLineWidth(rLine.getWidth())));
+ std::round(getTransformedLineWidth(rLine.getWidth())));
aLineInfo.SetLineJoin(rLine.getLineJoin());
aLineInfo.SetLineCap(rLine.getLineCap());
@@ -1753,6 +1814,16 @@ void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D(
// need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
+ if (!rBitmapCandidate.getDefinitionRange().isEmpty()
+ && aLocalPolyPolygon.getB2DRange() != rBitmapCandidate.getDefinitionRange())
+ {
+ // The range which defines the bitmap fill is defined and different from the
+ // range of the defining geometry (e.g. used for FillStyle UseSlideBackground).
+ // This cannot be done calling vcl, thus use decomposition here directly
+ process(rBitmapCandidate);
+ return;
+ }
+
fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon);
std::unique_ptr<SvtGraphicFill> pSvtGraphicFill;
@@ -1903,7 +1974,7 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D(
aToolsPolyPolygon,
Hatch(aHatchStyle,
Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())),
- basegfx::fround(rFillHatchAttribute.getDistance()),
+ basegfx::fround<tools::Long>(rFillHatchAttribute.getDistance()),
Degree10(basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttribute.getAngle())))));
impEndSvtGraphicFill(pSvtGraphicFill.get());
@@ -1912,42 +1983,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 (!useDecompose)
+ {
+ 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;
+ }
+ }
- if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
+ // tdf#150551 for PDF export, use the decomposition for better gradient visualization
+ if (!useDecompose && nullptr != mpPDFExtOutDevData)
{
- // #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;
+ 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
+ useDecompose = true;
+ }
+
+ 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"_ostr, 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 'compromise' 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"_ostr));
+ }
+
return;
}
@@ -1959,8 +2114,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,
@@ -1981,16 +2135,16 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D(
switch (aVCLGradient.GetStyle())
{
- default: // GradientStyle::Linear:
- case GradientStyle::Axial:
+ default: // css::awt::GradientStyle_LINEAR:
+ case css::awt::GradientStyle_AXIAL:
eGrad = SvtGraphicFill::GradientType::Linear;
break;
- case GradientStyle::Radial:
- case GradientStyle::Elliptical:
+ case css::awt::GradientStyle_RADIAL:
+ case css::awt::GradientStyle_ELLIPTICAL:
eGrad = SvtGraphicFill::GradientType::Radial;
break;
- case GradientStyle::Square:
- case GradientStyle::Rect:
+ case css::awt::GradientStyle_SQUARE:
+ case css::awt::GradientStyle_RECT:
eGrad = SvtGraphicFill::GradientType::Rectangular;
break;
}
@@ -2115,7 +2269,7 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
if (!bForceToMetafile && 1 == rContent.size())
{
- const primitive2d::Primitive2DReference xReference(rContent[0]);
+ const primitive2d::Primitive2DReference xReference(rContent.front());
pPoPoColor = dynamic_cast<const primitive2d::PolyPolygonColorPrimitive2D*>(
xReference.get());
}
@@ -2159,6 +2313,11 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
// various content, create content-metafile
GDIMetaFile aContentMetafile;
+
+ // tdf#155479 always forward propagate SVG flag for sub-content,
+ // it may contain cannotBeHandledByVCL gradients or transparencyGradients
+ aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG());
+
const tools::Rectangle aPrimitiveRectangle(
impDumpToMetaFile(rContent, aContentMetafile));
@@ -2172,7 +2331,7 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D(
basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0)));
const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
- aVCLGradient.SetStyle(GradientStyle::Linear);
+ aVCLGradient.SetStyle(css::awt::GradientStyle_LINEAR);
aVCLGradient.SetStartColor(aTransColor);
aVCLGradient.SetEndColor(aTransColor);
aVCLGradient.SetAngle(0_deg10);
@@ -2202,29 +2361,82 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
// FillGradientPrimitive2D and reconstruct the gradient.
// If that detection goes wrong, I have to create a transparence-blended bitmap. Eventually
// do that in stripes, else RenderTransparencePrimitive2D may just be used
- const primitive2d::Primitive2DContainer& rContent = rTransparenceCandidate.getChildren();
- const primitive2d::Primitive2DContainer& rTransparence
- = rTransparenceCandidate.getTransparence();
+ const primitive2d::Primitive2DContainer& rContent(rTransparenceCandidate.getChildren());
+ const primitive2d::Primitive2DContainer& rTransparence(
+ rTransparenceCandidate.getTransparence());
if (rContent.empty() || rTransparence.empty())
return;
// try to identify a single FillGradientPrimitive2D in the
- // transparence part of the primitive
- const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr;
+ // transparence part of the primitive. The hope is to handle
+ // the more specific case in a better way than the general
+ // TransparencePrimitive2D which has strongly separated
+ // definitions for transparency and content, both completely
+ // free definable by primitives
+ const primitive2d::FillGradientPrimitive2D* pFiGradient(nullptr);
static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore
+ // check for single FillGradientPrimitive2D
if (!bForceToBigTransparentVDev && 1 == rTransparence.size())
{
- const primitive2d::Primitive2DReference xReference(rTransparence[0]);
- pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(xReference.get());
+ pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(
+ rTransparence.front().get());
+
+ // check also for correct ID to exclude derived implementations
+ if (pFiGradient
+ && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D != pFiGradient->getPrimitive2DID())
+ pFiGradient = nullptr;
}
- // Check also for correct ID to exclude derived implementations
- if (pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID())
+ // 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())
{
- // various content, create content-metafile
+ // If not, reset the pointer and do not make use of this special case.
+ // Adding a gradient in incomplete state that can not be handled by vcl
+ // makes no sense and will knowingly lead to errors, especially with
+ // MCGR extended possibilities. I checked what happens with the
+ // MetaFloatTransparentAction added by OutputDevice::DrawTransparent, but
+ // in most cases it gets converted to bitmap or even ignored, see e.g.
+ // - vcl/source/gdi/pdfwriter_impl2.cxx for PDF export
+ // - vcl/source/filter/wmf/wmfwr.cxx -> does ignore TransparenceGradient completely
+ // - vcl/source/filter/wmf/emfwr.cxx -> same
+ // - vcl/source/filter/eps/eps.cxx -> same
+ // NOTE: Theoretically it would be possible to make the new extended Gradient data
+ // 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.
+
+ // tdf#155479 Yepp, as already mentioned above we need to add
+ // some MCGR infos in case of SVG export, prepare that here
+ if (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)
+ {
+ // this combination of Gradient can be expressed/handled by
+ // vcl/metafile, so add it directly. various content, create content-metafile
GDIMetaFile aContentMetafile;
+
+ // tdf#155479 always forward propagate SVG flag for sub-content,
+ // it may contain cannotBeHandledByVCL gradients or transparencyGradients
+ aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG());
+
const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
// re-create a VCL-gradient from FillGradientPrimitive2D
@@ -2232,126 +2444,192 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D(
impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(),
true);
- // render it to VCL
- mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
- aPrimitiveRectangle.GetSize(), aVCLGradient);
- }
- else
- {
- // sub-transparence group. Draw to VDev first.
- // this may get refined to tiling when resolution is too big here
-
- // need to avoid switching off MapMode stuff here; maybe need another
- // tooling class, cannot just do the same as with the pixel renderer.
- // Need to experiment...
-
- // Okay, basic implementation finished and tested. The DPI stuff was hard
- // and not easy to find out that it's needed.
- // Since this will not yet happen normally (as long as no one constructs
- // transparence primitives with non-trivial transparence content) i will for now not
- // refine to tiling here.
-
- basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D()));
- aViewRange.transform(maCurrentTransformation);
- const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())),
- static_cast<sal_Int32>(floor(aViewRange.getMinY())),
- static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
- static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
- const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
- Size aSizePixel(aRectPixel.GetSize());
- ScopedVclPtrInstance<VirtualDevice> aBufferDevice;
- const sal_uInt32 nMaxSquarePixels(500000);
- const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight());
- double fReduceFactor(1.0);
-
- if (nViewVisibleArea > nMaxSquarePixels)
- {
- // reduce render size
- fReduceFactor = sqrt(double(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
- aSizePixel = Size(
- basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * fReduceFactor),
- basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * fReduceFactor));
- }
-
- if (aBufferDevice->SetOutputSizePixel(aSizePixel))
- {
- // create and set MapModes for target devices
- MapMode aNewMapMode(mpOutputDevice->GetMapMode());
- aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top()));
- aBufferDevice->SetMapMode(aNewMapMode);
-
- // prepare view transformation for target renderers
- // ATTENTION! Need to apply another scaling because of the potential DPI differences
- // between Printer and VDev (mpOutputDevice and aBufferDevice here).
- // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used.
- basegfx::B2DHomMatrix aViewTransform(aBufferDevice->GetViewTransformation());
- const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
- / static_cast<double>(aDPINew.getWidth()));
- const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
- / static_cast<double>(aDPINew.getHeight()));
-
- if (!basegfx::fTools::equal(fDPIXChange, 1.0)
- || !basegfx::fTools::equal(fDPIYChange, 1.0))
- {
- aViewTransform.scale(fDPIXChange, fDPIYChange);
- }
-
- // also take scaling from Size reduction into account
- if (!basegfx::fTools::equal(fReduceFactor, 1.0))
- {
- aViewTransform.scale(fReduceFactor, fReduceFactor);
- }
-
- // create view information and pixel renderer. Reuse known ViewInformation
- // except new transformation and range
- const geometry::ViewInformation2D aViewInfo(
- getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange,
- getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
-
- VclPixelProcessor2D aBufferProcessor(aViewInfo, *aBufferDevice);
-
- // draw content using pixel renderer
- const Point aEmptyPoint;
- aBufferProcessor.process(rContent);
- const Bitmap aBmContent(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel));
-
- // draw transparence using pixel renderer
- aBufferDevice->Erase();
- aBufferProcessor.process(rTransparence);
- const AlphaMask aBmAlpha(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel));
+ 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));
- // paint
- mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(),
- BitmapEx(aBmContent, aBmAlpha));
+ pAction->addSVGTransparencyColorStops(aSVGTransparencyColorStops);
+ pMetaFile->AddAction(pAction);
}
+ else
+ {
+ // render it to VCL (creates MetaFloatTransparentAction)
+ mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(),
+ aPrimitiveRectangle.GetSize(), aVCLGradient);
+ }
+ return;
}
+
+ // Here we need to create a correct replacement visualization for the
+ // TransparencePrimitive2D for the target metafile.
+ // I replaced the n'th iteration to convert-to-bitmap which was
+ // used here by using the existing tooling. The orig here was also producing
+ // transparency errors with test-file from tdf#155437 on the right part of the
+ // image.
+ // Just rely on existing tooling doing the right thing in one place, so also
+ // corrections/optimizations can be in one single place
+
+ // Start by getting logic range of content, transform object-to-world, then world-to-view
+ // to get to discrete values ('pixels'). Matrix multiplication is right-to-left (and not
+ // commutative)
+ basegfx::B2DRange aLogicRange(rTransparenceCandidate.getB2DRange(getViewInformation2D()));
+ aLogicRange.transform(mpOutputDevice->GetViewTransformation() * maCurrentTransformation);
+
+ // expand in discrete coordinates to next-bigger 'pixel' boundaries and remember
+ // created discrete range
+ aLogicRange.expand(
+ basegfx::B2DPoint(floor(aLogicRange.getMinX()), floor(aLogicRange.getMinY())));
+ aLogicRange.expand(basegfx::B2DPoint(ceil(aLogicRange.getMaxX()), ceil(aLogicRange.getMaxY())));
+ const basegfx::B2DRange aDiscreteRange(aLogicRange);
+
+ // transform back from discrete to world coordinates: this creates the
+ // pixel-boundaries extended logic range we need to cover all content
+ // reliably
+ aLogicRange.transform(mpOutputDevice->GetInverseViewTransformation());
+
+ // create transform embedding for renderer. Goal is to translate what we
+ // want to paint to top/left 0/0 and the calculated discrete size
+ basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+ -aLogicRange.getMinX(), -aLogicRange.getMinY()));
+ const double fLogicWidth(
+ basegfx::fTools::equalZero(aLogicRange.getWidth()) ? 1.0 : aLogicRange.getWidth());
+ const double fLogicHeight(
+ basegfx::fTools::equalZero(aLogicRange.getHeight()) ? 1.0 : aLogicRange.getHeight());
+ aEmbedding.scale(aDiscreteRange.getWidth() / fLogicWidth,
+ aDiscreteRange.getHeight() / fLogicHeight);
+
+ // use the whole TransparencePrimitive2D as input (no need to create a new
+ // one with the sub-contents, these are ref-counted) and add to embedding
+ // primitive2d::TransparencePrimitive2D& rTrCand();
+ primitive2d::Primitive2DContainer xEmbedSeq{ &const_cast<primitive2d::TransparencePrimitive2D&>(
+ rTransparenceCandidate) };
+
+ // tdf#158743 when embedding, do not forget to 1st apply the evtl. used
+ // CurrentTransformation (right-to-left, apply that 1st)
+ xEmbedSeq = primitive2d::Primitive2DContainer{ new primitive2d::TransformPrimitive2D(
+ aEmbedding * maCurrentTransformation, std::move(xEmbedSeq)) };
+
+ // use empty ViewInformation & a useful MaximumQuadraticPixels
+ // limitation to paint the content
+ const auto aViewInformation2D(geometry::createViewInformation2D({}));
+ const sal_uInt32 nMaximumQuadraticPixels(500000);
+ const BitmapEx aBitmapEx(convertToBitmapEx(
+ std::move(xEmbedSeq), aViewInformation2D, basegfx::fround(aDiscreteRange.getWidth()),
+ basegfx::fround(aDiscreteRange.getHeight()), nMaximumQuadraticPixels));
+
+ // add to target metafile (will create MetaFloatTransparentAction)
+ mpOutputDevice->DrawBitmapEx(Point(basegfx::fround<tools::Long>(aLogicRange.getMinX()),
+ basegfx::fround<tools::Long>(aLogicRange.getMinY())),
+ Size(basegfx::fround<tools::Long>(aLogicRange.getWidth()),
+ basegfx::fround<tools::Long>(aLogicRange.getHeight())),
+ aBitmapEx);
}
void VclMetafileProcessor2D::processStructureTagPrimitive2D(
const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate)
{
+ ::comphelper::ValueRestorationGuard const g(mpCurrentStructureTag, &rStructureTagCandidate);
+
// structured tag primitive
const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement));
+ ::std::optional<sal_Int32> oAnchorParent;
+
+ if (!rStructureTagCandidate.isTaggedSdrObject())
+ {
+ bTagUsed = false;
+ }
if (mpPDFExtOutDevData && bTagUsed)
{
// foreground object: tag as regular structure element
if (!rStructureTagCandidate.isBackground())
{
- mpPDFExtOutDevData->BeginStructureElement(rTagElement);
+ if (rStructureTagCandidate.GetAnchorStructureElementKey() != nullptr)
+ {
+ sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement(
+ rStructureTagCandidate.GetAnchorStructureElementKey());
+ oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement());
+ mpPDFExtOutDevData->SetCurrentStructureElement(id);
+ }
+ mpPDFExtOutDevData->WrapBeginStructureElement(rTagElement);
+ switch (rTagElement)
+ {
+ case vcl::PDFWriter::H1:
+ case vcl::PDFWriter::H2:
+ case vcl::PDFWriter::H3:
+ case vcl::PDFWriter::H4:
+ case vcl::PDFWriter::H5:
+ case vcl::PDFWriter::H6:
+ case vcl::PDFWriter::Paragraph:
+ case vcl::PDFWriter::Heading:
+ case vcl::PDFWriter::Caption:
+ case vcl::PDFWriter::BlockQuote:
+ case vcl::PDFWriter::Table:
+ case vcl::PDFWriter::TableRow:
+ case vcl::PDFWriter::Formula:
+ case vcl::PDFWriter::Figure:
+ case vcl::PDFWriter::Annot:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement,
+ vcl::PDFWriter::Block);
+ break;
+ case vcl::PDFWriter::TableData:
+ case vcl::PDFWriter::TableHeader:
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement,
+ vcl::PDFWriter::Inline);
+ break;
+ default:
+ break;
+ }
+ switch (rTagElement)
+ {
+ case vcl::PDFWriter::Table:
+ case vcl::PDFWriter::Formula:
+ case vcl::PDFWriter::Figure:
+ case vcl::PDFWriter::Annot:
+ {
+ auto const range(rStructureTagCandidate.getB2DRange(getViewInformation2D()));
+ tools::Rectangle const aLogicRect(
+ basegfx::fround<tools::Long>(range.getMinX()),
+ basegfx::fround<tools::Long>(range.getMinY()),
+ basegfx::fround<tools::Long>(range.getMaxX()),
+ basegfx::fround<tools::Long>(range.getMaxY()));
+ mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect);
+ break;
+ }
+ default:
+ break;
+ }
+ if (rTagElement == vcl::PDFWriter::Annot)
+ {
+ mpPDFExtOutDevData->SetStructureAnnotIds(rStructureTagCandidate.GetAnnotIds());
+ }
+ if (rTagElement == vcl::PDFWriter::TableHeader)
+ {
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope,
+ vcl::PDFWriter::Column);
+ }
}
// background object
else
{
// background image: tag as artifact
if (rStructureTagCandidate.isImage())
- mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement);
+ mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
// any other background object: do not tag
else
- bTagUsed = false;
+ assert(false);
}
}
@@ -2362,104 +2640,11 @@ void VclMetafileProcessor2D::processStructureTagPrimitive2D(
{
// write end tag
mpPDFExtOutDevData->EndStructureElement();
- }
-}
-
-VclPtr<VirtualDevice>
-VclMetafileProcessor2D::CreateBufferDevice(const basegfx::B2DRange& rCandidateRange,
- geometry::ViewInformation2D& rViewInfo,
- tools::Rectangle& rRectLogic, Size& rSizePixel) const
-{
- constexpr double fMaxSquarePixels = 500000;
- basegfx::B2DRange aViewRange(rCandidateRange);
- aViewRange.transform(maCurrentTransformation);
- rRectLogic = tools::Rectangle(static_cast<tools::Long>(std::floor(aViewRange.getMinX())),
- static_cast<tools::Long>(std::floor(aViewRange.getMinY())),
- static_cast<tools::Long>(std::ceil(aViewRange.getMaxX())),
- static_cast<tools::Long>(std::ceil(aViewRange.getMaxY())));
- const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(rRectLogic));
- rSizePixel = aRectPixel.GetSize();
- const double fViewVisibleArea(rSizePixel.getWidth() * rSizePixel.getHeight());
- double fReduceFactor(1.0);
-
- if (fViewVisibleArea > fMaxSquarePixels)
- {
- // reduce render size
- fReduceFactor = sqrt(fMaxSquarePixels / fViewVisibleArea);
- rSizePixel = Size(basegfx::fround(rSizePixel.getWidth() * fReduceFactor),
- basegfx::fround(rSizePixel.getHeight() * fReduceFactor));
- }
-
- VclPtrInstance<VirtualDevice> pBufferDevice(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT);
- if (pBufferDevice->SetOutputSizePixel(rSizePixel))
- {
- // create and set MapModes for target devices
- MapMode aNewMapMode(mpOutputDevice->GetMapMode());
- aNewMapMode.SetOrigin(Point(-rRectLogic.Left(), -rRectLogic.Top()));
- pBufferDevice->SetMapMode(aNewMapMode);
-
- // prepare view transformation for target renderers
- // ATTENTION! Need to apply another scaling because of the potential DPI differences
- // between Printer and VDev (mpOutputDevice and pBufferDevice here).
- // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used.
- basegfx::B2DHomMatrix aViewTransform(pBufferDevice->GetViewTransformation());
- const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const Size aDPINew(pBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
- const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
- / static_cast<double>(aDPINew.getWidth()));
- const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
- / static_cast<double>(aDPINew.getHeight()));
-
- if (!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0))
- {
- aViewTransform.scale(fDPIXChange, fDPIYChange);
- }
-
- // also take scaling from Size reduction into account
- if (!basegfx::fTools::equal(fReduceFactor, 1.0))
+ if (oAnchorParent)
{
- aViewTransform.scale(fReduceFactor, fReduceFactor);
+ mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent);
}
-
- // create view information and pixel renderer. Reuse known ViewInformation
- // except new transformation and range
- rViewInfo = geometry::ViewInformation2D(
- getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange,
- getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
- }
- else
- pBufferDevice.disposeAndClear();
-
-#if HAVE_P1155R3
- return pBufferDevice;
-#else
- return std::move(pBufferDevice);
-#endif
-}
-
-void VclMetafileProcessor2D::processPrimitive2DOnPixelProcessor(
- const primitive2d::BasePrimitive2D& rCandidate)
-{
- basegfx::B2DRange aViewRange(rCandidate.getB2DRange(getViewInformation2D()));
- geometry::ViewInformation2D aViewInfo;
- tools::Rectangle aRectLogic;
- Size aSizePixel;
- auto pBufferDevice(CreateBufferDevice(aViewRange, aViewInfo, aRectLogic, aSizePixel));
- if (pBufferDevice)
- {
- VclPixelProcessor2D aBufferProcessor(aViewInfo, *pBufferDevice, maBColorModifierStack);
-
- // draw content using pixel renderer
- primitive2d::Primitive2DReference aRef(
- &const_cast<primitive2d::BasePrimitive2D&>(rCandidate));
- aBufferProcessor.process({ aRef });
- const BitmapEx aBmContent(pBufferDevice->GetBitmapEx(Point(), aSizePixel));
- mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(), aBmContent);
-
- // aBufferProcessor dtor pops state off pBufferDevice pushed on by its ctor, let
- // pBufferDevice live past aBufferProcessor scope to avoid warnings
}
- pBufferDevice.disposeAndClear();
}
} // end of namespace
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
index 06fd61e18309..c315281ebf61 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx
@@ -25,6 +25,7 @@
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <vcl/pdfextoutdevdata.hxx> // vcl::PDFExtOutDevData support
+#include <vcl/lazydelete.hxx>
class GDIMetaFile;
namespace tools
@@ -63,6 +64,7 @@ class PolyPolygonColorPrimitive2D;
class MaskPrimitive2D;
class UnifiedTransparencePrimitive2D;
class TransparencePrimitive2D;
+class ObjectInfoPrimitive2D;
class StructureTagPrimitive2D;
}
@@ -142,12 +144,10 @@ private:
const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate);
void processTransparencePrimitive2D(
const primitive2d::TransparencePrimitive2D& rTransparenceCandidate);
+ void
+ processObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D);
void processStructureTagPrimitive2D(
const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate);
- void processPrimitive2DOnPixelProcessor(const primitive2d::BasePrimitive2D& rCandidate);
- VclPtr<VirtualDevice> CreateBufferDevice(const basegfx::B2DRange& rCandidateRange,
- geometry::ViewInformation2D& rViewInfo,
- tools::Rectangle& rRectLogic, Size& rSizePixel) const;
/// Convert the fWidth to the same space as its coordinates.
double getTransformedLineWidth(double fWidth) const;
@@ -176,7 +176,7 @@ private:
constructed VclMetafileProcessor2D. It's still incarnated on demand,
but exists for OOo runtime now by purpose.
*/
- static css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator;
+ static vcl::DeleteOnDeinit<css::uno::Reference<css::i18n::XBreakIterator>> mxBreakIterator;
/* vcl::PDFExtOutDevData support
For the first step, some extra actions at vcl::PDFExtOutDevData need to
@@ -195,6 +195,8 @@ private:
std::stack<vcl::PDFWriter::StructElement> maListElements;
+ primitive2d::StructureTagPrimitive2D const* mpCurrentStructureTag = nullptr;
+
protected:
/* the local processor for BasePrimitive2D-Implementation based primitives,
called from the common process()-implementation
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 139bb0ba080a..754b8cef2592 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -23,21 +23,21 @@
#include <comphelper/lok.hxx>
#include <sal/log.hxx>
-#include <vcl/BitmapBasicMorphologyFilter.hxx>
-#include <vcl/BitmapFilterStackBlur.hxx>
#include <vcl/outdev.hxx>
#include <vcl/hatch.hxx>
#include <vcl/canvastools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/utils/gradienttools.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/Tools.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
@@ -57,35 +57,28 @@
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
#include <drawinglayer/primitive2d/epsprimitive2d.hxx>
-#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
#include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
#include <com/sun/star/awt/XWindow2.hpp>
#include <com/sun/star/awt/XControl.hpp>
-#include <svtools/optionsdrawinglayer.hxx>
+#include <officecfg/Office/Common.hxx>
#include <vcl/gradient.hxx>
using namespace com::sun::star;
namespace drawinglayer::processor2d
{
-struct VclPixelProcessor2D::Impl
-{
- AntialiasingFlags m_nOrigAntiAliasing;
-
- explicit Impl(OutputDevice const& rOutDev)
- : m_nOrigAntiAliasing(rOutDev.GetAntialiasing())
- {
- }
-};
-
VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation,
OutputDevice& rOutDev,
const basegfx::BColorModifierStack& rInitStack)
: VclProcessor2D(rViewInformation, rOutDev, rInitStack)
- , m_pImpl(new Impl(rOutDev))
+ , m_nOrigAntiAliasing(rOutDev.GetAntialiasing())
+ , m_bRenderSimpleTextDirect(
+ officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get())
+ , m_bRenderDecoratedTextDirect(
+ officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get())
{
// prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels
maCurrentTransformation = rViewInformation.getObjectToViewTransformation();
@@ -95,13 +88,13 @@ VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rVie
mpOutputDevice->SetMapMode();
// react on AntiAliasing settings
- if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ if (rViewInformation.getUseAntiAliasing())
{
- mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing | AntialiasingFlags::Enable);
+ mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing | AntialiasingFlags::Enable);
}
else
{
- mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing & ~AntialiasingFlags::Enable);
+ mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing & ~AntialiasingFlags::Enable);
}
}
@@ -111,7 +104,7 @@ VclPixelProcessor2D::~VclPixelProcessor2D()
mpOutputDevice->Pop();
// restore AntiAliasing
- mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing);
+ mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing);
}
void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(
@@ -126,7 +119,11 @@ void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(
const basegfx::BColor aPolygonColor(
maBColorModifierStack.getModifiedColor(rSource.getBColor()));
- mpOutputDevice->SetFillColor(Color(aPolygonColor));
+ if (comphelper::LibreOfficeKit::isActive() && aPolygonColor.isAutomatic())
+ mpOutputDevice->SetFillColor(getViewInformation2D().getAutoColor());
+ else
+ mpOutputDevice->SetFillColor(Color(aPolygonColor));
+
mpOutputDevice->SetLineColor();
mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(),
fTransparency);
@@ -196,32 +193,6 @@ bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(
rSource.getLineAttribute().getMiterMinimumAngle());
}
-namespace
-{
-GradientStyle convertGradientStyle(drawinglayer::attribute::GradientStyle eGradientStyle)
-{
- switch (eGradientStyle)
- {
- case drawinglayer::attribute::GradientStyle::Axial:
- return GradientStyle::Axial;
- case drawinglayer::attribute::GradientStyle::Radial:
- return GradientStyle::Radial;
- case drawinglayer::attribute::GradientStyle::Elliptical:
- return GradientStyle::Elliptical;
- case drawinglayer::attribute::GradientStyle::Square:
- return GradientStyle::Square;
- case drawinglayer::attribute::GradientStyle::Rect:
- return GradientStyle::Rect;
- case drawinglayer::attribute::GradientStyle::Linear:
- return GradientStyle::Linear;
- default:
- assert(false);
- return GradientStyle::Linear;
- }
-}
-
-} // end anonymous namespace
-
void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
switch (rCandidate.getPrimitive2DID())
@@ -367,17 +338,6 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
break;
}
- case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D:
- {
- // #i97628#
- // This primitive means that the content is derived from an active text edit,
- // not from model data itself. Some renderers need to suppress this content, e.g.
- // the pixel renderer used for displaying the edit view (like this one). It's
- // not to be suppressed by the MetaFile renderers, so that the edited text is
- // part of the MetaFile, e.g. needed for presentation previews.
- // Action: Ignore here, do nothing.
- break;
- }
case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
{
processInvertPrimitive2D(rCandidate);
@@ -406,24 +366,6 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate));
break;
}
- case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
- {
- processGlowPrimitive2D(
- static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate));
- break;
- }
- case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
- {
- processSoftEdgePrimitive2D(
- static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate));
- break;
- }
- case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
- {
- processShadowPrimitive2D(
- static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate));
- break;
- }
case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
{
processFillGradientPrimitive2D(
@@ -465,7 +407,7 @@ void VclPixelProcessor2D::processTextSimplePortionPrimitive2D(
const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
adaptTextToFillDrawMode();
- if (SvtOptionsDrawinglayer::IsRenderSimpleTextDirect())
+ if (SAL_LIKELY(m_bRenderSimpleTextDirect))
{
RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate);
}
@@ -485,7 +427,7 @@ void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D(
const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
adaptTextToFillDrawMode();
- if (SvtOptionsDrawinglayer::IsRenderDecoratedTextDirect())
+ if (SAL_LIKELY(m_bRenderDecoratedTextDirect))
{
RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate);
}
@@ -537,15 +479,31 @@ void VclPixelProcessor2D::processBitmapPrimitive2D(
void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D(
const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate)
{
- // direct draw of gradient
- const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient());
- basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor()));
- basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor()));
basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
+ // no geometry, no need to render, done
if (!aLocalPolyPolygon.count())
return;
+ // *try* direct draw (AKA using old VCL stuff) to render gradient
+ const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient());
+
+ // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL
+ // so use decomposition
+ // NOTE: There may be even more reasons to detect, e.g. a ViewTransformation
+ // that uses shear/rotate/mirror (what VCL cannot handle at all), see
+ // other checks already in processFillGradientPrimitive2D
+ if (rGradient.cannotBeHandledByVCL())
+ {
+ process(rPolygonCandidate);
+ return;
+ }
+
+ basegfx::BColor aStartColor(
+ maBColorModifierStack.getModifiedColor(rGradient.getColorStops().front().getStopColor()));
+ basegfx::BColor aEndColor(
+ maBColorModifierStack.getModifiedColor(rGradient.getColorStops().back().getStopColor()));
+
if (aStartColor == aEndColor)
{
// no gradient at all, draw as polygon in AA and non-AA case
@@ -553,12 +511,11 @@ void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D(
mpOutputDevice->SetLineColor();
mpOutputDevice->SetFillColor(Color(aStartColor));
mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
+ return;
}
- else
- {
- // use the primitive decomposition of the metafile
- process(rPolygonCandidate);
- }
+
+ // use the primitive decomposition
+ process(rPolygonCandidate);
}
void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D(
@@ -573,7 +530,7 @@ void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D(
// when AA is on and this filled polygons are the result of stroked line geometry,
// draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons
// Caution: This is needed in both cases (!)
- if (!(mnPolygonStrokePrimitive2D && SvtOptionsDrawinglayer::IsAntiAliasing()
+ if (!(mnPolygonStrokePrimitive2D && getViewInformation2D().getUseAntiAliasing()
&& (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::Enable)))
return;
@@ -619,9 +576,7 @@ void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D(
if (1 == rContent.size())
{
- const primitive2d::Primitive2DReference xReference(rContent[0]);
- const primitive2d::BasePrimitive2D* pBasePrimitive
- = static_cast<const primitive2d::BasePrimitive2D*>(xReference.get());
+ const primitive2d::BasePrimitive2D* pBasePrimitive = rContent.front().get();
switch (pBasePrimitive->getPrimitive2DID())
{
@@ -790,7 +745,7 @@ void VclPixelProcessor2D::processPolygonStrokePrimitive2D(
void VclPixelProcessor2D::processFillHatchPrimitive2D(
const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive)
{
- if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ if (getViewInformation2D().getUseAntiAliasing())
{
// if AA is used (or ignore smoothing is on), there is no need to smooth
// hatch painting, use decomposition
@@ -957,8 +912,7 @@ void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimit
void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
// #i98289#
- const bool bForceLineSnap(SvtOptionsDrawinglayer::IsAntiAliasing()
- && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete());
+ const bool bForceLineSnap(getViewInformation2D().getPixelSnapHairline());
const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing());
if (bForceLineSnap)
@@ -974,247 +928,116 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrim
}
}
-namespace
+void VclPixelProcessor2D::processFillGradientPrimitive2D(
+ const primitive2d::FillGradientPrimitive2D& rPrimitive)
{
-/* Returns 8-bit alpha mask created from passed mask.
+ const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient();
+ bool useDecompose(false);
- Negative fErodeDilateRadius values mean erode, positive - dilate.
- nTransparency defines minimal transparency level.
-*/
-AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius,
- double fBlurRadius, sal_uInt8 nTransparency)
-{
- // Only completely white pixels on the initial mask must be considered for transparency. Any
- // other color must be treated as black. This creates 1-bit B&W bitmap.
- BitmapEx mask(rMask.CreateMask(COL_WHITE));
-
- // Scaling down increases performance without noticeable quality loss. Additionally,
- // current blur implementation can only handle blur radius between 2 and 254.
- Size aSize = mask.GetSizePixel();
- double fScale = 1.0;
- while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000)
+ // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL
+ // so use decomposition
+ if (rFillGradient.cannotBeHandledByVCL())
{
- fScale /= 2;
- fBlurRadius /= 2;
- fErodeDilateRadius /= 2;
- aSize.setHeight(aSize.Height() / 2);
- aSize.setWidth(aSize.Width() / 2);
+ useDecompose = true;
}
- // BmpScaleFlag::Fast is important for following color replacement
- mask.Scale(fScale, fScale, BmpScaleFlag::Fast);
-
- if (fErodeDilateRadius > 0)
- BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius));
- else if (fErodeDilateRadius < 0)
- BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF));
-
- if (nTransparency)
+ // tdf#149754 VCL gradient draw is not capable to handle all primitive gradient definitions,
+ // what should be clear due to being developed to extend/replace them in
+ // capabilities & precision.
+ // It is e.g. not capable to correctly paint if the OutputRange is not completely
+ // inside the DefinitionRange, thus forcing to paint gradient parts *outside* the
+ // DefinitionRange.
+ // This happens for Writer with Frames anchored in Frames (and was broken due to
+ // falling back to VCL Gradient paint here), and for the new SlideBackgroundFill
+ // Fill mode for objects using it that reach outside the page (which is the
+ // DefinitionRange in that case).
+ // I see no real reason to fallback here to OutputDevice::DrawGradient and VCL
+ // gradient paint at all (system-dependent renderers wouldn't in the future), but
+ // will for convenience only add that needed additional correcting case
+ if (!useDecompose && !rPrimitive.getDefinitionRange().isInside(rPrimitive.getOutputRange()))
{
- const Color aTransparency(nTransparency, nTransparency, nTransparency);
- mask.Replace(COL_BLACK, aTransparency);
+ useDecompose = true;
}
- // We need 8-bit grey mask for blurring
- mask.Convert(BmpConversion::N8BitGreys);
-
- // calculate blurry effect
- BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius));
-
- mask.Scale(rMask.GetSizePixel());
-
- return AlphaMask(mask.GetBitmap());
-}
-}
-
-void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate)
-{
- basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
- aRange.transform(maCurrentTransformation);
- basegfx::B2DVector aGlowRadiusVector(rCandidate.getGlowRadius(), 0);
- // Calculate the pixel size of glow radius in current transformation
- aGlowRadiusVector *= maCurrentTransformation;
- // Glow radius is the size of the halo from each side of the object. The halo is the
- // border of glow color that fades from glow transparency level to fully transparent
- // When blurring a sharp boundary (our case), it gets 50% of original intensity, and
- // fades to both sides by the blur radius; thus blur radius is half of glow radius.
- const double fBlurRadius = aGlowRadiusVector.getLength() / 2;
- // Consider glow transparency (initial transparency near the object edge)
- const sal_uInt8 nAlpha = rCandidate.getGlowColor().GetAlpha();
-
- impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
- if (aBufferDevice.isVisible())
+ // tdf#151081 need to use regular primitive decomposition when the gradient
+ // is transformed in any other way then just translate & scale
+ if (!useDecompose)
{
- // remember last OutDev and set to content
- OutputDevice* pLastOutputDevice = mpOutputDevice;
- mpOutputDevice = &aBufferDevice.getContent();
- // We don't need antialiased mask here, which would only make effect thicker
- const auto aPrevAA = mpOutputDevice->GetAntialiasing();
- mpOutputDevice->SetAntialiasing(AntialiasingFlags::NONE);
- process(rCandidate);
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
- // Limit the bitmap size to the visible area.
- basegfx::B2DRange viewRange(getViewInformation2D().getDiscreteViewport());
- basegfx::B2DRange bitmapRange(aRange);
- bitmapRange.intersect(viewRange);
- if (!bitmapRange.isEmpty())
- {
- const tools::Rectangle aRect(
- static_cast<tools::Long>(std::floor(bitmapRange.getMinX())),
- static_cast<tools::Long>(std::floor(bitmapRange.getMinY())),
- static_cast<tools::Long>(std::ceil(bitmapRange.getMaxX())),
- static_cast<tools::Long>(std::ceil(bitmapRange.getMaxY())));
- BitmapEx bmpEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
- mpOutputDevice->SetAntialiasing(aPrevAA);
-
- AlphaMask mask
- = ProcessAndBlurAlphaMask(bmpEx.GetAlpha(), fBlurRadius, fBlurRadius, 255 - nAlpha);
-
- // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask
- const basegfx::BColor aGlowColor(
- maBColorModifierStack.getModifiedColor(rCandidate.getGlowColor().getBColor()));
- Bitmap bmp = bmpEx.GetBitmap();
- bmp.Erase(Color(aGlowColor));
- BitmapEx result(bmp, mask);
-
- // back to old OutDev
- mpOutputDevice = pLastOutputDevice;
- mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
- }
- else
+ 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)
{
- mpOutputDevice = pLastOutputDevice;
+ useDecompose = true;
}
}
- else
- SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
-}
-void VclPixelProcessor2D::processSoftEdgePrimitive2D(
- const primitive2d::SoftEdgePrimitive2D& rCandidate)
-{
- // TODO: don't limit the object at view range. This is needed to not blur objects at window
- // borders, where they don't end. Ideally, process the full object once at maximal reasonable
- // resolution, and store the resulting alpha mask in primitive's cache; then reuse it later,
- // applying the transform.
- basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
- aRange.transform(maCurrentTransformation);
- basegfx::B2DVector aRadiusVector(rCandidate.getRadius(), 0);
- // Calculate the pixel size of soft edge radius in current transformation
- aRadiusVector *= maCurrentTransformation;
- // Blur radius is equal to soft edge radius
- const double fBlurRadius = aRadiusVector.getLength();
-
- impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
- if (aBufferDevice.isVisible())
+ if (useDecompose)
{
- // remember last OutDev and set to content
- OutputDevice* pLastOutputDevice = mpOutputDevice;
- mpOutputDevice = &aBufferDevice.getContent();
- // Since the effect converts all children to bitmap, we can't disable antialiasing here,
- // because it would result in poor quality in areas not affected by the effect
- process(rCandidate);
+ // default is to use the direct render below. For security,
+ // keep the (simple) fallback to decompose in place here
+ static bool bTryDirectRender(true);
- // Limit the bitmap size to the visible area.
- basegfx::B2DRange viewRange(getViewInformation2D().getDiscreteViewport());
- basegfx::B2DRange bitmapRange(aRange);
- bitmapRange.intersect(viewRange);
- if (!bitmapRange.isEmpty())
+ if (bTryDirectRender)
{
- const tools::Rectangle aRect(
- static_cast<tools::Long>(std::floor(bitmapRange.getMinX())),
- static_cast<tools::Long>(std::floor(bitmapRange.getMinY())),
- static_cast<tools::Long>(std::ceil(bitmapRange.getMaxX())),
- static_cast<tools::Long>(std::ceil(bitmapRange.getMaxY())));
- BitmapEx bitmap = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
-
- AlphaMask aMask = bitmap.GetAlpha();
- AlphaMask blurMask = ProcessAndBlurAlphaMask(aMask, -fBlurRadius, fBlurRadius, 0);
-
- aMask.BlendWith(blurMask);
-
- // The end result is the original bitmap with blurred 8-bit alpha mask
- BitmapEx result(bitmap.GetBitmap(), aMask);
-
- // back to old OutDev
- mpOutputDevice = pLastOutputDevice;
- mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
+ // MCGR: Avoid one level of primitive creation, use FillGradientPrimitive2D
+ // tooling to directly create needed geometry & color for getting better
+ // performance (partially compensate for potentially more expensive multi
+ // color gradients).
+ // To handle a primitive that needs paint, either use decompose, or - when you
+ // do not want that for any reason, e.g. extra primitives created - implement
+ // a direct handling in your primitive renderer. This is always possible
+ // since primitives by definition are self-contained what means they have all
+ // needed data locally available to do so.
+ // The question is the complexity to invest - the implemented decompose
+ // is always a good hint of what is needed to do this. In this case I decided
+ // to add some tooling methods to the primitive itself to support this. These
+ // are used in decompose and can be used - as here now - for direct handling,
+ // too. This is always a possibility in primitive handling - you can, but do not
+ // have to.
+ mpOutputDevice->SetFillColor(
+ Color(maBColorModifierStack.getModifiedColor(rPrimitive.getOuterColor())));
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->DrawTransparent(
+ maCurrentTransformation,
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(rPrimitive.getOutputRange())),
+ 0.0);
+
+ // paint solid fill steps by providing callback as lambda
+ auto aCallback([&rPrimitive, this](const basegfx::B2DHomMatrix& rMatrix,
+ const basegfx::BColor& rColor) {
+ // create part polygon
+ basegfx::B2DPolygon aNewPoly(rPrimitive.getUnitPolygon());
+ aNewPoly.transform(rMatrix);
+
+ // create solid fill
+ mpOutputDevice->SetFillColor(Color(maBColorModifierStack.getModifiedColor(rColor)));
+ mpOutputDevice->DrawTransparent(maCurrentTransformation,
+ basegfx::B2DPolyPolygon(aNewPoly), 0.0);
+ });
+
+ // call value generator to trigger callbacks
+ rPrimitive.generateMatricesAndColors(aCallback);
}
else
{
- mpOutputDevice = pLastOutputDevice;
+ // use the decompose
+ process(rPrimitive);
}
- }
- else
- SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
-}
-
-void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate)
-{
- if (rCandidate.getShadowBlur() == 0)
- {
- process(rCandidate);
- return;
- }
-
- basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
- aRange.transform(maCurrentTransformation);
- basegfx::B2DVector aBlurRadiusVector(rCandidate.getShadowBlur(), 0);
- aBlurRadiusVector *= maCurrentTransformation;
- const double fBlurRadius = aBlurRadiusVector.getLength();
-
- impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
- if (aBufferDevice.isVisible() && !aRange.isEmpty())
- {
- OutputDevice* pLastOutputDevice = mpOutputDevice;
- mpOutputDevice = &aBufferDevice.getContent();
-
- process(rCandidate);
- const tools::Rectangle aRect(static_cast<tools::Long>(std::floor(aRange.getMinX())),
- static_cast<tools::Long>(std::floor(aRange.getMinY())),
- static_cast<tools::Long>(std::ceil(aRange.getMaxX())),
- static_cast<tools::Long>(std::ceil(aRange.getMaxY())));
-
- BitmapEx bitmapEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize());
-
- AlphaMask mask = ProcessAndBlurAlphaMask(bitmapEx.GetAlpha(), 0, fBlurRadius, 0);
-
- const basegfx::BColor aShadowColor(
- maBColorModifierStack.getModifiedColor(rCandidate.getShadowColor()));
-
- Bitmap bitmap = bitmapEx.GetBitmap();
- bitmap.Erase(Color(aShadowColor));
- BitmapEx result(bitmap, mask);
-
- mpOutputDevice = pLastOutputDevice;
- mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result);
- }
- else
- SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
-}
-
-void VclPixelProcessor2D::processFillGradientPrimitive2D(
- const primitive2d::FillGradientPrimitive2D& rPrimitive)
-{
- const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient();
-
- // VCL should be able to handle all styles, but for tdf#133477 the VCL result
- // is different from processing the gradient manually by drawinglayer
- // (and the Writer unittest for it fails). Keep using the drawinglayer code
- // until somebody founds out what's wrong and fixes it.
- if (rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Linear
- && rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Axial
- && rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Radial)
- {
- process(rPrimitive);
return;
}
- GradientStyle eGradientStyle = convertGradientStyle(rFillGradient.getStyle());
-
- Gradient aGradient(eGradientStyle, Color(rFillGradient.getStartColor()),
- Color(rFillGradient.getEndColor()));
+ // try to use vcl - since vcl uses the old gradient paint mechanisms this may
+ // create wrong geometries. If so, add another case above for useDecompose
+ Gradient aGradient(rFillGradient.getStyle(),
+ Color(rFillGradient.getColorStops().front().getStopColor()),
+ Color(rFillGradient.getColorStops().back().getStopColor()));
aGradient.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg<10>(rFillGradient.getAngle()))));
aGradient.SetBorder(rFillGradient.getBorder() * 100);
@@ -1263,11 +1086,13 @@ void VclPixelProcessor2D::processPatternFillPrimitive2D(
tools::Rectangle aMaskRect = vcl::unotools::rectangleFromB2DRectangle(aMaskRange);
// Unless smooth edges are needed, simply use clipping.
- if (basegfx::utils::isRectangle(aMask) || !SvtOptionsDrawinglayer::IsAntiAliasing())
+ if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing())
{
mpOutputDevice->Push(vcl::PushFlags::CLIPREGION);
mpOutputDevice->IntersectClipRegion(vcl::Region(aMask));
- mpOutputDevice->DrawWallpaper(aMaskRect, Wallpaper(aTileImage));
+ Wallpaper aWallpaper(aTileImage);
+ aWallpaper.SetColor(COL_TRANSPARENT);
+ mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper);
mpOutputDevice->Pop();
return;
}
@@ -1290,7 +1115,11 @@ void VclPixelProcessor2D::processPatternFillPrimitive2D(
mpOutputDevice->DrawRect(aMaskRect);
}
else
- mpOutputDevice->DrawWallpaper(aMaskRect, Wallpaper(aTileImage));
+ {
+ Wallpaper aWallpaper(aTileImage);
+ aWallpaper.SetColor(COL_TRANSPARENT);
+ mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper);
+ }
// back to old OutDev
mpOutputDevice = pLastOutputDevice;
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
index eaf212c8e5b1..185ed52ed897 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
@@ -39,9 +39,6 @@ class PolygonStrokePrimitive2D;
class FillHatchPrimitive2D;
class BackgroundColorPrimitive2D;
class BorderLinePrimitive2D;
-class GlowPrimitive2D;
-class ShadowPrimitive2D;
-class SoftEdgePrimitive2D;
class FillGradientPrimitive2D;
class PatternFillPrimitive2D;
}
@@ -56,8 +53,10 @@ namespace drawinglayer::processor2d
*/
class VclPixelProcessor2D final : public VclProcessor2D
{
- struct Impl;
- std::unique_ptr<Impl> m_pImpl;
+ AntialiasingFlags m_nOrigAntiAliasing;
+
+ bool m_bRenderSimpleTextDirect;
+ bool m_bRenderDecoratedTextDirect;
/* the local processor for BasePrimitive2D-Implementation based primitives,
called from the common process()-implementation
@@ -98,9 +97,6 @@ class VclPixelProcessor2D final : public VclProcessor2D
processBorderLinePrimitive2D(const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder);
void processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
void processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate);
- void processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate);
- void processSoftEdgePrimitive2D(const primitive2d::SoftEdgePrimitive2D& rCandidate);
- void processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate);
void processFillGradientPrimitive2D(const primitive2d::FillGradientPrimitive2D& rPrimitive);
void processPatternFillPrimitive2D(const primitive2d::PatternFillPrimitive2D& rPrimitive);
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index 5d3f68d29a4a..3cfec4af8b8d 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -23,9 +23,14 @@
#include "vclhelperbufferdevice.hxx"
#include <cmath>
#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
#include <svtools/optionsdrawinglayer.hxx>
#include <tools/debug.hxx>
+#include <tools/fract.hxx>
+#include <utility>
+#include <vcl/glyphitemcache.hxx>
#include <vcl/graph.hxx>
+#include <vcl/kernarray.hxx>
#include <vcl/outdev.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
@@ -38,9 +43,10 @@
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
@@ -89,6 +95,29 @@ sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA,
}
}
+namespace
+{
+/** helper to convert a MapMode to a transformation */
+basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
+{
+ basegfx::B2DHomMatrix aMapping;
+ const Fraction aNoScale(1, 1);
+ const Point& rOrigin(rMapMode.GetOrigin());
+
+ if (0 != rOrigin.X() || 0 != rOrigin.Y())
+ {
+ aMapping.translate(rOrigin.X(), rOrigin.Y());
+ }
+
+ if (rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
+ {
+ aMapping.scale(double(rMapMode.GetScaleX()), double(rMapMode.GetScaleY()));
+ }
+
+ return aMapping;
+}
+}
+
namespace drawinglayer::processor2d
{
// rendering support
@@ -106,14 +135,14 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
basegfx::B2DVector aFontScaling, aTranslate;
double fRotate, fShearX;
aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX);
+
bool bPrimitiveAccepted(false);
// tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored,
// especially if the effect is less than a pixel.
if (std::abs(aFontScaling.getY() * fShearX) < 1)
{
- if (basegfx::fTools::less(aFontScaling.getX(), 0.0)
- && basegfx::fTools::less(aFontScaling.getY(), 0.0))
+ if (aFontScaling.getX() < 0.0 && aFontScaling.getY() < 0.0)
{
// handle special case: If scale is negative in (x,y) (3rd quadrant), it can
// be expressed as rotation by PI. Use this since the Font rendering will not
@@ -122,25 +151,43 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
fRotate += M_PI;
}
- if (basegfx::fTools::more(aFontScaling.getX(), 0.0)
- && basegfx::fTools::more(aFontScaling.getY(), 0.0))
+ if (aFontScaling.getX() > 0.0 && aFontScaling.getY() > 0.0)
{
- // Get the VCL font (use FontHeight as FontWidth)
- vcl::Font aFont(primitive2d::getVclFontFromFontAttribute(
- rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(),
- fRotate, rTextCandidate.getLocale()));
+ double fIgnoreRotate, fIgnoreShearX;
+
+ basegfx::B2DVector aFontSize, aTextTranslate;
+ rTextCandidate.getTextTransform().decompose(aFontSize, aTextTranslate, fIgnoreRotate,
+ fIgnoreShearX);
+
+ // tdf#153092 Ideally we don't have to scale the font and dxarray, but we might have
+ // to nevertheless if dealing with non integer sizes
+ const bool bScaleFont(aFontSize.getY() != std::round(aFontSize.getY())
+ || comphelper::LibreOfficeKit::isActive());
+ vcl::Font aFont;
+
+ // Get the VCL font
+ if (!bScaleFont)
+ {
+ aFont = primitive2d::getVclFontFromFontAttribute(
+ rTextCandidate.getFontAttribute(), aFontSize.getX(), aFontSize.getY(), fRotate,
+ rTextCandidate.getLocale());
+ }
+ else
+ {
+ aFont = primitive2d::getVclFontFromFontAttribute(
+ rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(),
+ fRotate, rTextCandidate.getLocale());
+ }
// Don't draw fonts without height
- if (aFont.GetFontHeight() <= 0)
+ Size aResultFontSize = aFont.GetFontSize();
+ if (aResultFontSize.Height() <= 0)
return;
// set FillColor Attribute
const Color aFillColor(rTextCandidate.getTextFillColor());
- if (aFillColor != COL_TRANSPARENT)
- {
- aFont.SetFillColor(aFillColor);
- aFont.SetTransparent(false);
- }
+ aFont.SetTransparent(aFillColor.IsTransparent());
+ aFont.SetFillColor(aFillColor);
// handle additional font attributes
const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = nullptr;
@@ -247,27 +294,27 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
aFont.SetShadow(true);
}
- // create transformed integer DXArray in view coordinate system
- std::vector<sal_Int32> aTransformedDXArray;
+ // create integer DXArray
+ KernArray aDXArray;
if (!rTextCandidate.getDXArray().empty())
{
- aTransformedDXArray.reserve(rTextCandidate.getDXArray().size());
- const basegfx::B2DVector aPixelVector(maCurrentTransformation
- * basegfx::B2DVector(1.0, 0.0));
- const double fPixelVectorFactor(aPixelVector.getLength());
-
- for (auto const& elem : rTextCandidate.getDXArray())
+ double fPixelVectorFactor(1.0);
+ if (bScaleFont)
{
- aTransformedDXArray.push_back(basegfx::fround(elem * fPixelVectorFactor));
+ const basegfx::B2DVector aPixelVector(maCurrentTransformation
+ * basegfx::B2DVector(1.0, 0.0));
+ fPixelVectorFactor = aPixelVector.getLength();
}
+
+ aDXArray.reserve(rTextCandidate.getDXArray().size());
+ for (auto const& elem : rTextCandidate.getDXArray())
+ aDXArray.push_back(basegfx::fround(elem * fPixelVectorFactor));
}
// set parameters and paint text snippet
const basegfx::BColor aRGBFontColor(
maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
- const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0));
- const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY()));
const vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode());
if (rTextCandidate.getFontAttribute().getRTL())
@@ -279,47 +326,126 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
mpOutputDevice->SetLayoutMode(nRTLLayoutMode);
}
- mpOutputDevice->SetFont(aFont);
- mpOutputDevice->SetTextColor(Color(aRGBFontColor));
-
OUString aText(rTextCandidate.getText());
sal_Int32 nPos = rTextCandidate.getTextPosition();
sal_Int32 nLen = rTextCandidate.getTextLength();
+ // this contraption is used in editeng, with format paragraph used to
+ // set a tab with a tab-fill character
if (rTextCandidate.isFilled())
{
- basegfx::B2DVector aOldFontScaling, aOldTranslate;
- double fOldRotate, fOldShearX;
- rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate,
- fOldRotate, fOldShearX);
+ tools::Long nWidthToFill = rTextCandidate.getWidthToFill();
- tools::Long nWidthToFill = static_cast<tools::Long>(
- rTextCandidate.getWidthToFill() * aFontScaling.getX() / aOldFontScaling.getX());
-
- tools::Long nWidth = mpOutputDevice->GetTextArray(rTextCandidate.getText(),
- &aTransformedDXArray, 0, 1);
- tools::Long nChars = 2;
+ tools::Long nWidth = basegfx::fround<tools::Long>(
+ mpOutputDevice->GetTextArray(rTextCandidate.getText(), &aDXArray, 0, 1));
+ sal_Int32 nChars = 2;
if (nWidth)
nChars = nWidthToFill / nWidth;
- OUStringBuffer aFilled;
+ OUStringBuffer aFilled(nChars);
comphelper::string::padToLength(aFilled, nChars, aText[0]);
aText = aFilled.makeStringAndClear();
nPos = 0;
nLen = nChars;
- if (!aTransformedDXArray.empty())
+ if (!aDXArray.empty())
{
- sal_Int32 nDX = aTransformedDXArray[0];
- aTransformedDXArray.resize(nLen);
+ sal_Int32 nDX = aDXArray[0];
+ aDXArray.resize(nLen);
for (sal_Int32 i = 1; i < nLen; ++i)
- aTransformedDXArray[i] = aTransformedDXArray[i - 1] + nDX;
+ aDXArray.set(i, aDXArray[i - 1] + nDX);
}
}
- if (!aTransformedDXArray.empty())
+ Point aStartPoint;
+ bool bChangeMapMode(false);
+ if (!bScaleFont)
{
- mpOutputDevice->DrawTextArray(aStartPoint, aText, aTransformedDXArray, nPos, nLen);
+ basegfx::B2DHomMatrix aCombinedTransform(
+ getTransformFromMapMode(mpOutputDevice->GetMapMode())
+ * maCurrentTransformation);
+
+ basegfx::B2DVector aCurrentScaling, aCurrentTranslate;
+ double fCurrentRotate;
+ aCombinedTransform.decompose(aCurrentScaling, aCurrentTranslate, fCurrentRotate,
+ fIgnoreShearX);
+
+ const Point aOrigin(
+ basegfx::fround<tools::Long>(aCurrentTranslate.getX() / aCurrentScaling.getX()),
+ basegfx::fround<tools::Long>(aCurrentTranslate.getY()
+ / aCurrentScaling.getY()));
+
+ Fraction aScaleX(aCurrentScaling.getX());
+ if (!aScaleX.IsValid())
+ {
+ SAL_WARN("drawinglayer", "invalid X Scale");
+ return;
+ }
+
+ Fraction aScaleY(aCurrentScaling.getY());
+ if (!aScaleY.IsValid())
+ {
+ SAL_WARN("drawinglayer", "invalid Y Scale");
+ return;
+ }
+
+ MapMode aMapMode(mpOutputDevice->GetMapMode().GetMapUnit(), aOrigin, aScaleX,
+ aScaleY);
+
+ if (fCurrentRotate)
+ aTextTranslate *= basegfx::utils::createRotateB2DHomMatrix(fCurrentRotate);
+ aStartPoint = Point(basegfx::fround<tools::Long>(aTextTranslate.getX()),
+ basegfx::fround<tools::Long>(aTextTranslate.getY()));
+
+ bChangeMapMode = aMapMode != mpOutputDevice->GetMapMode();
+ if (bChangeMapMode)
+ {
+ mpOutputDevice->Push(vcl::PushFlags::MAPMODE);
+ mpOutputDevice->SetRelativeMapMode(aMapMode);
+ }
+ }
+ else
+ {
+ const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0));
+ double aPointX = aPoint.getX(), aPointY = aPoint.getY();
+
+ // aFont has an integer size; we must scale a bit for precision
+ double nFontScalingFixY = aFontScaling.getY() / aResultFontSize.Height();
+ double nFontScalingFixX = aFontScaling.getX()
+ / (aResultFontSize.Width() ? aResultFontSize.Width()
+ : aResultFontSize.Height());
+
+ if (!rtl_math_approxEqual(nFontScalingFixY, 1.0)
+ || !rtl_math_approxEqual(nFontScalingFixX, 1.0))
+ {
+ MapMode aMapMode = mpOutputDevice->GetMapMode();
+ aMapMode.SetScaleX(aMapMode.GetScaleX() * nFontScalingFixX);
+ aMapMode.SetScaleY(aMapMode.GetScaleY() * nFontScalingFixY);
+
+ mpOutputDevice->Push(vcl::PushFlags::MAPMODE);
+ mpOutputDevice->SetRelativeMapMode(aMapMode);
+ bChangeMapMode = true;
+
+ aPointX /= nFontScalingFixX;
+ aPointY /= nFontScalingFixY;
+ }
+
+ aStartPoint = Point(basegfx::fround<tools::Long>(aPointX),
+ basegfx::fround<tools::Long>(aPointY));
+ }
+
+ // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired
+ // font size
+ mpOutputDevice->SetFont(aFont);
+ mpOutputDevice->SetTextColor(Color(aRGBFontColor));
+
+ if (!aDXArray.empty())
+ {
+ const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
+ mpOutputDevice, aText, nPos, nLen);
+ mpOutputDevice->DrawTextArray(aStartPoint, aText, aDXArray,
+ rTextCandidate.getKashidaArray(), nPos, nLen,
+ SalLayoutFlags::NONE, pGlyphs);
}
else
{
@@ -331,6 +457,9 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
mpOutputDevice->SetLayoutMode(nOldLayoutMode);
}
+ if (bChangeMapMode)
+ mpOutputDevice->Pop();
+
bPrimitiveAccepted = true;
}
}
@@ -354,8 +483,7 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D(
basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
aLocalPolygon.transform(maCurrentTransformation);
- if (bPixelBased && SvtOptionsDrawinglayer::IsAntiAliasing()
- && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete())
+ if (bPixelBased && getViewInformation2D().getPixelSnapHairline())
{
// #i98289#
// when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
@@ -371,7 +499,7 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D(
// direct draw of transformed BitmapEx primitive
void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
{
- BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap()));
+ BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
* rBitmapCandidate.getTransform());
@@ -405,243 +533,224 @@ void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2
void VclProcessor2D::RenderFillGraphicPrimitive2D(
const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
{
+ bool bPrimitiveAccepted = RenderFillGraphicPrimitive2DImpl(rFillBitmapCandidate);
+
+ if (!bPrimitiveAccepted)
+ {
+ // do not accept, use decomposition
+ process(rFillBitmapCandidate);
+ }
+}
+
+bool VclProcessor2D::RenderFillGraphicPrimitive2DImpl(
+ const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
+{
const attribute::FillGraphicAttribute& rFillGraphicAttribute(
rFillBitmapCandidate.getFillGraphic());
- bool bPrimitiveAccepted(false);
// #121194# when tiling is used and content is bitmap-based, do direct tiling in the
// renderer on pixel base to ensure tight fitting. Do not do this when
// the fill is rotated or sheared.
- if (rFillGraphicAttribute.getTiling())
- {
- // content is bitmap(ex)
- //
- // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use
- // the primitive representation of the vector data directly.
- //
- // when graphic is animated, force decomposition to use the correct graphic, else
- // fill style will not be animated
- if (GraphicType::Bitmap == rFillGraphicAttribute.getGraphic().GetType()
- && !rFillGraphicAttribute.getGraphic().getVectorGraphicData()
- && !rFillGraphicAttribute.getGraphic().IsAnimated())
- {
- // decompose matrix to check for shear, rotate and mirroring
- basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
- * rFillBitmapCandidate.getTransformation());
- basegfx::B2DVector aScale, aTranslate;
- double fRotate, fShearX;
- aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
-
- // when nopt rotated/sheared
- if (basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX))
- {
- // no shear or rotate, draw direct in pixel coordinates
- bPrimitiveAccepted = true;
+ if (!rFillGraphicAttribute.getTiling())
+ return false;
+
+ // content is bitmap(ex)
+ //
+ // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use
+ // the primitive representation of the vector data directly.
+ //
+ // when graphic is animated, force decomposition to use the correct graphic, else
+ // fill style will not be animated
+ if (GraphicType::Bitmap != rFillGraphicAttribute.getGraphic().GetType()
+ || rFillGraphicAttribute.getGraphic().getVectorGraphicData()
+ || rFillGraphicAttribute.getGraphic().IsAnimated())
+ return false;
+
+ // decompose matrix to check for shear, rotate and mirroring
+ basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation
+ * rFillBitmapCandidate.getTransformation());
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
- // transform object range to device coordinates (pixels). Use
- // the device transformation for better accuracy
- basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
- aObjectRange.transform(mpOutputDevice->GetViewTransformation());
+ // when nopt rotated/sheared
+ if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
+ return false;
- // extract discrete size of object
- const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth()));
- const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight()));
+ // no shear or rotate, draw direct in pixel coordinates
- // only do something when object has a size in discrete units
- if (nOWidth > 0 && nOHeight > 0)
- {
- // transform graphic range to device coordinates (pixels). Use
- // the device transformation for better accuracy
- basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
- aGraphicRange.transform(mpOutputDevice->GetViewTransformation()
- * aLocalTransform);
-
- // extract discrete size of graphic
- // caution: when getting to zero, nothing would be painted; thus, do not allow this
- const sal_Int32 nBWidth(
- std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth())));
- const sal_Int32 nBHeight(
- std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
-
- // only do something when bitmap fill has a size in discrete units
- if (nBWidth > 0 && nBHeight > 0)
- {
- // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it
- // in vcl many times, create a size-optimized version
- const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
- BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
- const bool bPreScaled(nBWidth * nBHeight < (250 * 250));
-
- // ... but only up to a maximum size, else it gets too expensive
- if (bPreScaled)
- {
- // if color depth is below 24bit, expand before scaling for better quality.
- // This is even needed for low colors, else the scale will produce
- // a bitmap in gray or Black/White (!)
- if (isPalettePixelFormat(aBitmapEx.getPixelFormat()))
- {
- aBitmapEx.Convert(BmpConversion::N24Bit);
- }
+ // transform object range to device coordinates (pixels). Use
+ // the device transformation for better accuracy
+ basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
+ aObjectRange.transform(mpOutputDevice->GetViewTransformation());
- aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate);
- }
+ // extract discrete size of object
+ const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth()));
+ const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight()));
- bool bPainted(false);
+ // only do something when object has a size in discrete units
+ if (nOWidth <= 0 || nOHeight <= 0)
+ return true;
- if (maBColorModifierStack.count())
- {
- // when color modifier, apply to bitmap
- aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
+ // transform graphic range to device coordinates (pixels). Use
+ // the device transformation for better accuracy
+ basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
+ aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform);
- // impModifyBitmapEx uses empty bitmap as sign to return that
- // the content will be completely replaced to mono color, use shortcut
- if (aBitmapEx.IsEmpty())
- {
- // color gets completely replaced, get it
- const basegfx::BColor aModifiedColor(
- maBColorModifierStack.getModifiedColor(basegfx::BColor()));
- basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
- aPolygon.transform(aLocalTransform);
+ // extract discrete size of graphic
+ // caution: when getting to zero, nothing would be painted; thus, do not allow this
+ const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth())));
+ const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
- mpOutputDevice->SetFillColor(Color(aModifiedColor));
- mpOutputDevice->SetLineColor();
- mpOutputDevice->DrawPolygon(aPolygon);
+ // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it
+ // in vcl many times, create a size-optimized version
+ const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
+ BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
+ const bool bPreScaled(nBWidth * nBHeight < (250 * 250));
- bPainted = true;
- }
- }
+ // ... but only up to a maximum size, else it gets too expensive
+ if (bPreScaled)
+ {
+ // if color depth is below 24bit, expand before scaling for better quality.
+ // This is even needed for low colors, else the scale will produce
+ // a bitmap in gray or Black/White (!)
+ if (isPalettePixelFormat(aBitmapEx.getPixelFormat()))
+ {
+ aBitmapEx.Convert(BmpConversion::N24Bit);
+ }
- if (!bPainted)
- {
- sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX()));
- sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY()));
- const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX()));
- const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY()));
- sal_Int32 nPosX(0);
- sal_Int32 nPosY(0);
-
- if (nBLeft > nOLeft)
- {
- const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
+ aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate);
+ }
- nPosX -= nDiff;
- nBLeft -= nDiff * nBWidth;
- }
+ if (maBColorModifierStack.count())
+ {
+ // when color modifier, apply to bitmap
+ aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
- if (nBLeft + nBWidth <= nOLeft)
- {
- const sal_Int32 nDiff(-nBLeft / nBWidth);
+ // ModifyBitmapEx uses empty bitmap as sign to return that
+ // the content will be completely replaced to mono color, use shortcut
+ if (aBitmapEx.IsEmpty())
+ {
+ // color gets completely replaced, get it
+ const basegfx::BColor aModifiedColor(
+ maBColorModifierStack.getModifiedColor(basegfx::BColor()));
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(aLocalTransform);
- nPosX += nDiff;
- nBLeft += nDiff * nBWidth;
- }
+ mpOutputDevice->SetFillColor(Color(aModifiedColor));
+ mpOutputDevice->SetLineColor();
+ mpOutputDevice->DrawPolygon(aPolygon);
- if (nBTop > nOTop)
- {
- const sal_Int32 nDiff((nBTop / nBHeight) + 1);
+ return true;
+ }
+ }
- nPosY -= nDiff;
- nBTop -= nDiff * nBHeight;
- }
+ sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX()));
+ sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY()));
+ const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX()));
+ const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY()));
+ sal_Int32 nPosX(0);
+ sal_Int32 nPosY(0);
- if (nBTop + nBHeight <= nOTop)
- {
- const sal_Int32 nDiff(-nBTop / nBHeight);
+ if (nBLeft > nOLeft)
+ {
+ const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
- nPosY += nDiff;
- nBTop += nDiff * nBHeight;
- }
+ nPosX -= nDiff;
+ nBLeft -= nDiff * nBWidth;
+ }
- // prepare OutDev
- const Point aEmptyPoint(0, 0);
- const ::tools::Rectangle aVisiblePixel(
- aEmptyPoint, mpOutputDevice->GetOutputSizePixel());
- const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
- mpOutputDevice->EnableMapMode(false);
+ if (nBLeft + nBWidth <= nOLeft)
+ {
+ const sal_Int32 nDiff(-nBLeft / nBWidth);
- // check if offset is used
- const sal_Int32 nOffsetX(
- basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth));
+ nPosX += nDiff;
+ nBLeft += nDiff * nBWidth;
+ }
- if (nOffsetX)
- {
- // offset in X, so iterate over Y first and draw lines
- for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight;
- nYPos += nBHeight, nPosY++)
- {
- for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX
- : nBLeft);
- nXPos < nOLeft + nOWidth; nXPos += nBWidth)
- {
- const ::tools::Rectangle aOutRectPixel(
- Point(nXPos, nYPos), aNeededBitmapSizePixel);
-
- if (aOutRectPixel.Overlaps(aVisiblePixel))
- {
- if (bPreScaled)
- {
- mpOutputDevice->DrawBitmapEx(
- aOutRectPixel.TopLeft(), aBitmapEx);
- }
- else
- {
- mpOutputDevice->DrawBitmapEx(
- aOutRectPixel.TopLeft(), aNeededBitmapSizePixel,
- aBitmapEx);
- }
- }
- }
- }
- }
- else
- {
- // check if offset is used
- const sal_Int32 nOffsetY(
- basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
+ if (nBTop > nOTop)
+ {
+ const sal_Int32 nDiff((nBTop / nBHeight) + 1);
- // possible offset in Y, so iterate over X first and draw columns
- for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth;
- nXPos += nBWidth, nPosX++)
- {
- for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY
- : nBTop);
- nYPos < nOTop + nOHeight; nYPos += nBHeight)
- {
- const ::tools::Rectangle aOutRectPixel(
- Point(nXPos, nYPos), aNeededBitmapSizePixel);
-
- if (aOutRectPixel.Overlaps(aVisiblePixel))
- {
- if (bPreScaled)
- {
- mpOutputDevice->DrawBitmapEx(
- aOutRectPixel.TopLeft(), aBitmapEx);
- }
- else
- {
- mpOutputDevice->DrawBitmapEx(
- aOutRectPixel.TopLeft(), aNeededBitmapSizePixel,
- aBitmapEx);
- }
- }
- }
- }
- }
+ nPosY -= nDiff;
+ nBTop -= nDiff * nBHeight;
+ }
- // restore OutDev
- mpOutputDevice->EnableMapMode(bWasEnabled);
- }
+ if (nBTop + nBHeight <= nOTop)
+ {
+ const sal_Int32 nDiff(-nBTop / nBHeight);
+
+ nPosY += nDiff;
+ nBTop += nDiff * nBHeight;
+ }
+
+ // prepare OutDev
+ const Point aEmptyPoint(0, 0);
+ // the visible rect, in pixels
+ const ::tools::Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel());
+ const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
+ mpOutputDevice->EnableMapMode(false);
+
+ // check if offset is used
+ const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth));
+
+ if (nOffsetX)
+ {
+ // offset in X, so iterate over Y first and draw lines
+ for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++)
+ {
+ for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX : nBLeft);
+ nXPos < nOLeft + nOWidth; nXPos += nBWidth)
+ {
+ const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
+
+ if (aOutRectPixel.Overlaps(aVisiblePixel))
+ {
+ if (bPreScaled)
+ {
+ mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
+ }
+ else
+ {
+ mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(),
+ aNeededBitmapSizePixel, aBitmapEx);
}
}
}
}
}
-
- if (!bPrimitiveAccepted)
+ else
{
- // do not accept, use decomposition
- process(rFillBitmapCandidate);
+ // check if offset is used
+ const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
+
+ // possible offset in Y, so iterate over X first and draw columns
+ for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++)
+ {
+ for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY : nBTop);
+ nYPos < nOTop + nOHeight; nYPos += nBHeight)
+ {
+ const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
+
+ if (aOutRectPixel.Overlaps(aVisiblePixel))
+ {
+ if (bPreScaled)
+ {
+ mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
+ }
+ else
+ {
+ mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(),
+ aNeededBitmapSizePixel, aBitmapEx);
+ }
+ }
+ }
+ }
}
+
+ // restore OutDev
+ mpOutputDevice->EnableMapMode(bWasEnabled);
+ return true;
}
// direct draw of Graphic
@@ -773,7 +882,7 @@ void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive
aMask.transform(maCurrentTransformation);
// Unless smooth edges are needed, simply use clipping.
- if (basegfx::utils::isRectangle(aMask) || !SvtOptionsDrawinglayer::IsAntiAliasing())
+ if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing())
{
mpOutputDevice->Push(vcl::PushFlags::CLIPREGION);
mpOutputDevice->IntersectClipRegion(vcl::Region(aMask));
@@ -889,7 +998,7 @@ void VclProcessor2D::RenderTransparencePrimitive2D(
process(rTransCandidate.getTransparence());
// back to old color stack
- maBColorModifierStack = aLastBColorModifierStack;
+ maBColorModifierStack = std::move(aLastBColorModifierStack);
// back to old OutDev
mpOutputDevice = pLastOutputDevice;
@@ -909,10 +1018,9 @@ void VclProcessor2D::RenderTransformPrimitive2D(
// create new transformations for CurrentTransformation
// and for local ViewInformation2D
maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation();
- const geometry::ViewInformation2D aViewInformation2D(
- getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
- getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(),
- getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime());
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
+ * rTransformCandidate.getTransformation());
updateViewInformation(aViewInformation2D);
// process content
@@ -931,10 +1039,8 @@ void VclProcessor2D::RenderPagePreviewPrimitive2D(
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
// create new local ViewInformation2D
- const geometry::ViewInformation2D aViewInformation2D(
- getViewInformation2D().getObjectTransformation(),
- getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(),
- rPagePreviewCandidate.getXDrawPage(), getViewInformation2D().getViewTime());
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setVisualizedPage(rPagePreviewCandidate.getXDrawPage());
updateViewInformation(aViewInformation2D);
// process decomposed content
@@ -980,8 +1086,8 @@ void VclProcessor2D::RenderMarkerArrayPrimitive2D(
{
const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * pos)
- aDiscreteHalfSize);
- const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()),
- basegfx::fround(aDiscreteTopLeft.getY()));
+ const Point aDiscretePoint(basegfx::fround<tools::Long>(aDiscreteTopLeft.getX()),
+ basegfx::fround<tools::Long>(aDiscreteTopLeft.getY()));
mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker);
}
@@ -1001,8 +1107,8 @@ void VclProcessor2D::RenderPointArrayPrimitive2D(
for (auto const& pos : rPositions)
{
const basegfx::B2DPoint aViewPosition(maCurrentTransformation * pos);
- const Point aPos(basegfx::fround(aViewPosition.getX()),
- basegfx::fround(aViewPosition.getY()));
+ const Point aPos(basegfx::fround<tools::Long>(aViewPosition.getX()),
+ basegfx::fround<tools::Long>(aViewPosition.getY()));
mpOutputDevice->DrawPixel(aPos, aVCLColor);
}
@@ -1017,7 +1123,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D(
const double fLineWidth(rLineAttribute.getWidth());
bool bDone(false);
- if (basegfx::fTools::more(fLineWidth, 0.0))
+ if (fLineWidth > 0.0)
{
const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation
* basegfx::B2DVector(fLineWidth, 0.0));
@@ -1048,7 +1154,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D(
if (nCount)
{
- const bool bAntiAliased(SvtOptionsDrawinglayer::IsAntiAliasing());
+ const bool bAntiAliased(getViewInformation2D().getUseAntiAliasing());
aHairlinePolyPolygon.transform(maCurrentTransformation);
if (bAntiAliased)
@@ -1261,26 +1367,12 @@ void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEp
}
}
-void VclProcessor2D::RenderObjectInfoPrimitive2D(
- const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D)
-{
- // remember current ObjectInfoPrimitive2D and set new current one (build a stack - push)
- const primitive2d::ObjectInfoPrimitive2D* pLast(getObjectInfoPrimitive2D());
- mpObjectInfoPrimitive2D = &rObjectInfoPrimitive2D;
-
- // process content
- process(rObjectInfoPrimitive2D.getChildren());
-
- // restore current ObjectInfoPrimitive2D (pop)
- mpObjectInfoPrimitive2D = pLast;
-}
-
void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(
const primitive2d::SvgLinearAtomPrimitive2D& rCandidate)
{
const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA());
- if (!basegfx::fTools::more(fDelta, 0.0))
+ if (fDelta <= 0.0)
return;
const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
@@ -1290,7 +1382,7 @@ void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(
const basegfx::B2DVector aDiscreteVector(
getViewInformation2D().getInverseObjectToViewTransformation()
* basegfx::B2DVector(1.0, 1.0));
- const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373));
+ const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
// use color distance and discrete lengths to calculate step count
const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit));
@@ -1324,7 +1416,7 @@ void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(
{
const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA());
- if (!basegfx::fTools::more(fDeltaScale, 0.0))
+ if (fDeltaScale <= 0.0)
return;
const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
@@ -1334,7 +1426,7 @@ void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(
const basegfx::B2DVector aDiscreteVector(
getViewInformation2D().getInverseObjectToViewTransformation()
* basegfx::B2DVector(1.0, 1.0));
- const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373));
+ const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2));
// use color distance and discrete lengths to calculate step count
const sal_uInt32 nSteps(
@@ -1475,13 +1567,11 @@ void VclProcessor2D::adaptTextToFillDrawMode() const
// process support
VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformation,
- OutputDevice& rOutDev,
- const basegfx::BColorModifierStack& rInitStack)
+ OutputDevice& rOutDev, basegfx::BColorModifierStack aInitStack)
: BaseProcessor2D(rViewInformation)
, mpOutputDevice(&rOutDev)
- , maBColorModifierStack(rInitStack)
+ , maBColorModifierStack(std::move(aInitStack))
, mnPolygonStrokePrimitive2D(0)
- , mpObjectInfoPrimitive2D(nullptr)
{
// set digit language, derived from SvtCTLOptions to have the correct
// number display for arabic/hindi numerals
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.hxx b/drawinglayer/source/processor2d/vclprocessor2d.hxx
index 7e251aa0ca13..d2f0a69157fb 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.hxx
@@ -46,7 +46,6 @@ class PolygonStrokePrimitive2D;
class ControlPrimitive2D;
class PagePreviewPrimitive2D;
class EpsPrimitive2D;
-class ObjectInfoPrimitive2D;
class SvgLinearAtomPrimitive2D;
class SvgRadialAtomPrimitive2D;
}
@@ -76,9 +75,6 @@ protected:
// PolygonStrokePrimitive2D's decompositions (normally only one)
sal_uInt32 mnPolygonStrokePrimitive2D;
- // currently used ObjectInfoPrimitive2D
- const primitive2d::ObjectInfoPrimitive2D* mpObjectInfoPrimitive2D;
-
// common VCL rendering support
void RenderTextSimpleOrDecoratedPortionPrimitive2D(
const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate);
@@ -105,8 +101,6 @@ protected:
void RenderPolygonStrokePrimitive2D(
const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate);
void RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D);
- void
- RenderObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D);
void RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate);
void RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate);
@@ -117,14 +111,12 @@ protected:
public:
// constructor/destructor
VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev,
- const basegfx::BColorModifierStack& rInitStack = basegfx::BColorModifierStack());
+ basegfx::BColorModifierStack aInitStack = basegfx::BColorModifierStack());
virtual ~VclProcessor2D() override;
- // access to currently used ObjectInfoPrimitive2D
- const primitive2d::ObjectInfoPrimitive2D* getObjectInfoPrimitive2D() const
- {
- return mpObjectInfoPrimitive2D;
- }
+private:
+ bool RenderFillGraphicPrimitive2DImpl(
+ const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate);
};
} // end of namespace drawinglayer::processor2d
diff --git a/drawinglayer/source/processor3d/baseprocessor3d.cxx b/drawinglayer/source/processor3d/baseprocessor3d.cxx
index 0420f02ca37d..a9638dc92bb4 100644
--- a/drawinglayer/source/processor3d/baseprocessor3d.cxx
+++ b/drawinglayer/source/processor3d/baseprocessor3d.cxx
@@ -18,7 +18,7 @@
*/
#include <drawinglayer/processor3d/baseprocessor3d.hxx>
-#include <comphelper/sequence.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -30,8 +30,8 @@ namespace drawinglayer::processor3d
{
}
- BaseProcessor3D::BaseProcessor3D(const geometry::ViewInformation3D& rViewInformation)
- : maViewInformation3D(rViewInformation)
+ BaseProcessor3D::BaseProcessor3D(geometry::ViewInformation3D aViewInformation)
+ : maViewInformation3D(std::move(aViewInformation))
{
}
diff --git a/drawinglayer/source/processor3d/defaultprocessor3d.cxx b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
index b9159c46c73f..498cc93d86cc 100644
--- a/drawinglayer/source/processor3d/defaultprocessor3d.cxx
+++ b/drawinglayer/source/processor3d/defaultprocessor3d.cxx
@@ -59,106 +59,82 @@ namespace drawinglayer::processor3d
// create texture
const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getGradient();
const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY());
- const attribute::GradientStyle aGradientStyle(rFillGradient.getStyle());
- sal_uInt32 nSteps(rFillGradient.getSteps());
- const basegfx::BColor& aStart(rFillGradient.getStartColor());
- const basegfx::BColor& aEnd(rFillGradient.getEndColor());
- const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
+ const css::awt::GradientStyle aGradientStyle(rFillGradient.getStyle());
std::shared_ptr< texture::GeoTexSvx > pNewTex;
+ basegfx::BColor aSingleColor;
- if(nMaxSteps)
+ if (!rFillGradient.getColorStops().isSingleColor(aSingleColor))
{
- // there IS a color distance
- if(nSteps == 0)
- {
- nSteps = nMaxSteps;
- }
-
- if(nSteps < 2)
- {
- nSteps = 2;
- }
-
- if(nSteps > nMaxSteps)
- {
- nSteps = nMaxSteps;
- }
-
switch(aGradientStyle)
{
- case attribute::GradientStyle::Linear:
+ default: // GradientStyle_MAKE_FIXED_SIZE
+ case css::awt::GradientStyle_LINEAR:
{
pNewTex = std::make_shared<texture::GeoTexSvxGradientLinear>(
aOutlineRange,
aOutlineRange,
- aStart,
- aEnd,
- nSteps,
+ rFillGradient.getSteps(),
+ rFillGradient.getColorStops(),
rFillGradient.getBorder(),
rFillGradient.getAngle());
break;
}
- case attribute::GradientStyle::Axial:
+ case css::awt::GradientStyle_AXIAL:
{
pNewTex = std::make_shared<texture::GeoTexSvxGradientAxial>(
aOutlineRange,
aOutlineRange,
- aStart,
- aEnd,
- nSteps,
+ rFillGradient.getSteps(),
+ rFillGradient.getColorStops(),
rFillGradient.getBorder(),
rFillGradient.getAngle());
break;
}
- case attribute::GradientStyle::Radial:
+ case css::awt::GradientStyle_RADIAL:
{
pNewTex =
std::make_shared<texture::GeoTexSvxGradientRadial>(
aOutlineRange,
- aStart,
- aEnd,
- nSteps,
+ rFillGradient.getSteps(),
+ rFillGradient.getColorStops(),
rFillGradient.getBorder(),
rFillGradient.getOffsetX(),
rFillGradient.getOffsetY());
break;
}
- case attribute::GradientStyle::Elliptical:
+ case css::awt::GradientStyle_ELLIPTICAL:
{
pNewTex =
std::make_shared<texture::GeoTexSvxGradientElliptical>(
aOutlineRange,
- aStart,
- aEnd,
- nSteps,
+ rFillGradient.getSteps(),
+ rFillGradient.getColorStops(),
rFillGradient.getBorder(),
rFillGradient.getOffsetX(),
rFillGradient.getOffsetY(),
rFillGradient.getAngle());
break;
}
- case attribute::GradientStyle::Square:
+ case css::awt::GradientStyle_SQUARE:
{
pNewTex =
std::make_shared<texture::GeoTexSvxGradientSquare>(
aOutlineRange,
- aStart,
- aEnd,
- nSteps,
+ rFillGradient.getSteps(),
+ rFillGradient.getColorStops(),
rFillGradient.getBorder(),
rFillGradient.getOffsetX(),
rFillGradient.getOffsetY(),
rFillGradient.getAngle());
break;
}
- case attribute::GradientStyle::Rect:
+ case css::awt::GradientStyle_RECT:
{
pNewTex =
std::make_shared<texture::GeoTexSvxGradientRect>(
aOutlineRange,
- aStart,
- aEnd,
- nSteps,
+ rFillGradient.getSteps(),
+ rFillGradient.getColorStops(),
rFillGradient.getBorder(),
rFillGradient.getOffsetX(),
rFillGradient.getOffsetY(),
@@ -171,8 +147,8 @@ namespace drawinglayer::processor3d
}
else
{
- // no color distance -> same color, use simple texture
- pNewTex = std::make_shared<texture::GeoTexSvxMono>(aStart, 1.0 - aStart.luminance());
+ // only one color, so no real gradient -> use simple texture
+ pNewTex = std::make_shared<texture::GeoTexSvxMono>(aSingleColor, 1.0 - aSingleColor.luminance());
mbSimpleTextureActive = true;
}
@@ -214,7 +190,7 @@ namespace drawinglayer::processor3d
// rescue values
const bool bOldModulate(getModulate()); mbModulate = rPrimitive.getModulate();
const bool bOldFilter(getFilter()); mbFilter = rPrimitive.getFilter();
- std::shared_ptr< texture::GeoTexSvx > pOldTex = mpGeoTexSvx;
+ std::shared_ptr<texture::GeoTexSvx> xOldTex(mpGeoTexSvx);
// calculate logic pixel size in object coordinates. Create transformation view
// to object by inverting ObjectToView
@@ -242,7 +218,7 @@ namespace drawinglayer::processor3d
// restore values
mbModulate = bOldModulate;
mbFilter = bOldFilter;
- mpGeoTexSvx = pOldTex;
+ mpGeoTexSvx = std::move(xOldTex);
}
void DefaultProcessor3D::impRenderBitmapTexturePrimitive3D(const primitive3d::BitmapTexturePrimitive3D& rPrimitive)
@@ -293,7 +269,7 @@ namespace drawinglayer::processor3d
// restore values
mbModulate = bOldModulate;
mbFilter = bOldFilter;
- mpGeoTexSvx = pOldTex;
+ mpGeoTexSvx = std::move(pOldTex);
}
void DefaultProcessor3D::impRenderModifiedColorPrimitive3D(const primitive3d::ModifiedColorPrimitive3D& rModifiedCandidate)
diff --git a/drawinglayer/source/processor3d/geometry2dextractor.cxx b/drawinglayer/source/processor3d/geometry2dextractor.cxx
index 3fb3d0d19f12..6959df6405e6 100644
--- a/drawinglayer/source/processor3d/geometry2dextractor.cxx
+++ b/drawinglayer/source/processor3d/geometry2dextractor.cxx
@@ -23,11 +23,12 @@
#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <primitive3d/textureprimitive3d.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -89,7 +90,7 @@ namespace drawinglayer::processor3d
{
a2DHairline.transform(getObjectTransformation());
const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(rPrimitive.getBColor()));
- const primitive2d::Primitive2DReference xRef(new primitive2d::PolygonHairlinePrimitive2D(a2DHairline, aModifiedColor));
+ const primitive2d::Primitive2DReference xRef(new primitive2d::PolygonHairlinePrimitive2D(std::move(a2DHairline), aModifiedColor));
maPrimitive2DSequence.push_back(xRef);
}
break;
@@ -104,7 +105,7 @@ namespace drawinglayer::processor3d
{
a2DFill.transform(getObjectTransformation());
const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(rPrimitive.getMaterial().getColor()));
- const primitive2d::Primitive2DReference xRef(new primitive2d::PolyPolygonColorPrimitive2D(a2DFill, aModifiedColor));
+ const primitive2d::Primitive2DReference xRef(new primitive2d::PolyPolygonColorPrimitive2D(std::move(a2DFill), aModifiedColor));
maPrimitive2DSequence.push_back(xRef);
}
break;
@@ -141,9 +142,9 @@ namespace drawinglayer::processor3d
Geometry2DExtractingProcessor::Geometry2DExtractingProcessor(
const geometry::ViewInformation3D& rViewInformation,
- const basegfx::B2DHomMatrix& rObjectTransformation)
+ basegfx::B2DHomMatrix aObjectTransformation)
: BaseProcessor3D(rViewInformation),
- maObjectTransformation(rObjectTransformation),
+ maObjectTransformation(std::move(aObjectTransformation)),
maBColorModifierStack()
{
}
diff --git a/drawinglayer/source/processor3d/shadow3dextractor.cxx b/drawinglayer/source/processor3d/shadow3dextractor.cxx
index bed08b9688fb..c4c0e0ba1249 100644
--- a/drawinglayer/source/processor3d/shadow3dextractor.cxx
+++ b/drawinglayer/source/processor3d/shadow3dextractor.cxx
@@ -24,12 +24,14 @@
#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <osl/diagnose.h>
#include <rtl/ref.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -154,7 +156,7 @@ namespace drawinglayer::processor3d
a2DHairline.transform(getObjectTransformation());
mpPrimitive2DSequence->push_back(
new primitive2d::PolygonHairlinePrimitive2D(
- a2DHairline,
+ std::move(a2DHairline),
basegfx::BColor()));
}
}
@@ -185,7 +187,7 @@ namespace drawinglayer::processor3d
a2DFill.transform(getObjectTransformation());
mpPrimitive2DSequence->push_back(
new primitive2d::PolyPolygonColorPrimitive2D(
- a2DFill,
+ std::move(a2DFill),
basegfx::BColor()));
}
}
@@ -202,13 +204,13 @@ namespace drawinglayer::processor3d
Shadow3DExtractingProcessor::Shadow3DExtractingProcessor(
const geometry::ViewInformation3D& rViewInformation,
- const basegfx::B2DHomMatrix& rObjectTransformation,
+ basegfx::B2DHomMatrix aObjectTransformation,
const basegfx::B3DVector& rLightNormal,
double fShadowSlant,
const basegfx::B3DRange& rContained3DRange)
: BaseProcessor3D(rViewInformation),
mpPrimitive2DSequence(&maPrimitive2DSequence),
- maObjectTransformation(rObjectTransformation),
+ maObjectTransformation(std::move(aObjectTransformation)),
maLightNormal(rLightNormal),
mfLightPlaneScalar(0.0),
mbShadowProjectionIsValid(false),
diff --git a/drawinglayer/source/processor3d/zbufferprocessor3d.cxx b/drawinglayer/source/processor3d/zbufferprocessor3d.cxx
index fb74a233e283..b9cb8ffb7a1d 100644
--- a/drawinglayer/source/processor3d/zbufferprocessor3d.cxx
+++ b/drawinglayer/source/processor3d/zbufferprocessor3d.cxx
@@ -27,7 +27,9 @@
#include <basegfx/polygon/b3dpolygontools.hxx>
#include <basegfx/polygon/b3dpolypolygontools.hxx>
#include <drawinglayer/attribute/sdrlightingattribute3d.hxx>
+#include <o3tl/safeint.hxx>
#include <svtools/optionsdrawinglayer.hxx>
+#include <utility>
using namespace com::sun::star;
@@ -269,7 +271,7 @@ void ZBufferRasterConverter3D::processLineSpan(const basegfx::RasterConversionLi
if(nSpanCount & 0x0001)
return;
- if(nLine < 0 || nLine >= static_cast<sal_Int32>(mrBuffer.getHeight()))
+ if(nLine < 0 || o3tl::make_unsigned(nLine) >= mrBuffer.getHeight())
return;
sal_uInt32 nXA(std::min(mrBuffer.getWidth(), static_cast<sal_uInt32>(std::max(sal_Int32(0), basegfx::fround(rA.getX().getVal())))));
@@ -374,16 +376,16 @@ private:
public:
RasterPrimitive3D(
- const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& pGeoTexSvx,
- const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& pTransparenceGeoTexSvx,
+ std::shared_ptr< drawinglayer::texture::GeoTexSvx > pGeoTexSvx,
+ std::shared_ptr< drawinglayer::texture::GeoTexSvx > pTransparenceGeoTexSvx,
const drawinglayer::attribute::MaterialAttribute3D& rMaterial,
const basegfx::B3DPolyPolygon& rPolyPolygon,
bool bModulate,
bool bFilter,
bool bSimpleTextureActive,
bool bIsLine)
- : mpGeoTexSvx(pGeoTexSvx),
- mpTransparenceGeoTexSvx(pTransparenceGeoTexSvx),
+ : mpGeoTexSvx(std::move(pGeoTexSvx)),
+ mpTransparenceGeoTexSvx(std::move(pTransparenceGeoTexSvx)),
maMaterial(rMaterial),
maPolyPolygon(rPolyPolygon),
mfCenterZ(basegfx::utils::getRange(rPolyPolygon).getCenter().getZ()),
diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx
index b3477f642880..b965e0e4b933 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -20,6 +20,7 @@
#include <sal/config.h>
#include <algorithm>
+#include <limits>
#include <texture/texture.hxx>
#include <basegfx/numeric/ftools.hxx>
@@ -71,13 +72,14 @@ namespace drawinglayer::texture
GeoTexSvxGradient::GeoTexSvxGradient(
const basegfx::B2DRange& rDefinitionRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder)
- : maDefinitionRange(rDefinitionRange),
- maStart(rStart),
- maEnd(rEnd),
- mfBorder(fBorder)
+ : maDefinitionRange(rDefinitionRange)
+ , mnRequestedSteps(nRequestedSteps)
+ , mnColorStops(rColorStops)
+ , mfBorder(fBorder)
+ , maLastColorStopRange()
{
}
@@ -92,26 +94,26 @@ namespace drawinglayer::texture
return (pCompare
&& maGradientInfo == pCompare->maGradientInfo
&& maDefinitionRange == pCompare->maDefinitionRange
+ && mnRequestedSteps == pCompare->mnRequestedSteps
+ && mnColorStops == pCompare->mnColorStops
&& mfBorder == pCompare->mfBorder);
}
-
GeoTexSvxGradientLinear::GeoTexSvxGradientLinear(
const basegfx::B2DRange& rDefinitionRange,
const basegfx::B2DRange& rOutputRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
- sal_uInt32 nSteps,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder,
double fAngle)
- : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder),
- mfUnitMinX(0.0),
- mfUnitWidth(1.0),
- mfUnitMaxY(1.0)
+ : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
+ , mfUnitMinX(0.0)
+ , mfUnitWidth(1.0)
+ , mfUnitMaxY(1.0)
{
maGradientInfo = basegfx::utils::createLinearODFGradientInfo(
rDefinitionRange,
- nSteps,
+ nRequestedSteps,
fBorder,
fAngle);
@@ -131,16 +133,30 @@ namespace drawinglayer::texture
}
void GeoTexSvxGradientLinear::appendTransformationsAndColors(
- std::vector< B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor)
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
{
- rOuterColor = maStart;
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- if(!maGradientInfo.getSteps())
+ // only one color, done
+ if (mnColorStops.size() < 2)
return;
- const double fStripeWidth(1.0 / maGradientInfo.getSteps());
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // check if we need last-ColorStop-correction
+ const bool bPenultimateUsed(mnColorStops.checkPenultimate());
+
+ if (bPenultimateUsed)
+ {
+ // Here we need to temporarily add a ColorStop entry with the
+ // same color as the last entry to correctly 'close' the
+ // created gradient geometry.
+ // The simplest way is to temporarily add an entry to the local
+ // ColorStops for this at 1.0 (using same color)
+ mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
+ }
+
+ // prepare unit range transform
basegfx::B2DHomMatrix aPattern;
// bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
@@ -151,54 +167,106 @@ namespace drawinglayer::texture
aPattern.scale(mfUnitWidth, 1.0);
aPattern.translate(mfUnitMinX, 0.0);
- for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ // outer loop over ColorStops, each is from cs_l to cs_r
+ for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++)
{
- const double fPos(fStripeWidth * a);
- basegfx::B2DHomMatrix aNew(aPattern);
-
- // scale and translate in Y
- double fHeight(1.0 - fPos);
-
- if(a + 1 == maGradientInfo.getSteps() && mfUnitMaxY > 1.0)
+ // get offsets
+ const double fOffsetStart(cs_l->getStopOffset());
+ const double fOffsetEnd(cs_r->getStopOffset());
+
+ // same offset, empty BColorStopRange, continue with next step
+ if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
+ continue;
+
+ // get colors & calculate steps
+ const basegfx::BColor aCStart(cs_l->getStopColor());
+ const basegfx::BColor aCEnd(cs_r->getStopColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
+
+ // calculate StripeWidth
+ // nSteps is >= 1, see getRequestedSteps, so no check needed here
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
+
+ // for the 1st color range we do not need to create the 1st step
+ // since it will be equal to StartColor and thus OuterColor, so
+ // will be painted by the 1st, always-created background polygon
+ // colored using OuterColor.
+ // We *need* to create this though for all 'inner' color ranges
+ // to get a correct start
+ const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
{
- fHeight += mfUnitMaxY - 1.0;
+ // calculate pos in Y
+ const double fPos(fOffsetStart + (fStripeWidth * innerLoop));
+
+ // scale and translate in Y. For GradientLinear we always have
+ // the full height
+ double fHeight(1.0 - fPos);
+
+ if (mfUnitMaxY > 1.0)
+ {
+ // extend when difference between definition and OutputRange exists
+ fHeight += mfUnitMaxY - 1.0;
+ }
+
+ basegfx::B2DHomMatrix aNew(aPattern);
+ aNew.scale(1.0, fHeight);
+ aNew.translate(0.0, fPos);
+
+ // set and add at target
+ aCallback(
+ maGradientInfo.getTextureTransform() * aNew,
+ 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
}
+ }
- aNew.scale(1.0, fHeight);
- aNew.translate(0.0, fPos);
-
- // set at target
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew;
-
- // interpolate and set color
- aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
-
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ if (bPenultimateUsed)
+ {
+ // correct temporary change
+ mnColorStops.pop_back();
}
}
void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
- const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo));
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ // just single color, done
+ if (mnColorStops.size() < 2)
+ {
+ rBColor = mnColorStops.front().getStopColor();
+ return;
+ }
+
+ // texture-back-transform X/Y -> t [0.0..1.0] and determine color
+ const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo));
+ rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange);
}
GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
const basegfx::B2DRange& rDefinitionRange,
const basegfx::B2DRange& rOutputRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
- sal_uInt32 nSteps,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder,
double fAngle)
- : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder),
- mfUnitMinX(0.0),
- mfUnitWidth(1.0)
+ : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
+ , mfUnitMinX(0.0)
+ , mfUnitWidth(1.0)
{
+ // ARGH! GradientAxial uses the ColorStops in reverse order compared
+ // with the other gradients. Either stay 'thinking reverse' for the
+ // rest of time or adapt it here and go in same order as the other five,
+ // so unifications/tooling will be possible
+ mnColorStops.reverseColorStops();
+
maGradientInfo = basegfx::utils::createAxialODFGradientInfo(
rDefinitionRange,
- nSteps,
+ nRequestedSteps,
fBorder,
fAngle);
@@ -217,65 +285,118 @@ namespace drawinglayer::texture
}
void GeoTexSvxGradientAxial::appendTransformationsAndColors(
- std::vector< B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor)
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
{
- rOuterColor = maEnd;
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- if(!maGradientInfo.getSteps())
+ // only one color, done
+ if (mnColorStops.size() < 2)
return;
- const double fStripeWidth(1.0 / maGradientInfo.getSteps());
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // check if we need last-ColorStop-correction
+ const bool bPenultimateUsed(mnColorStops.checkPenultimate());
+
+ if (bPenultimateUsed)
+ {
+ // temporarily add a ColorStop entry
+ mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
+ }
+
+ // prepare unit range transform
+ basegfx::B2DHomMatrix aPattern;
+
+ // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
+ aPattern.scale(0.5, 1.0);
+ aPattern.translate(0.5, 0.0);
+
+ // scale/translate in X
+ aPattern.scale(mfUnitWidth, 1.0);
+ aPattern.translate(mfUnitMinX, 0.0);
- for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ // outer loop over ColorStops, each is from cs_l to cs_r
+ for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++)
{
- const double fPos(fStripeWidth * a);
- basegfx::B2DHomMatrix aNew;
+ // get offsets
+ const double fOffsetStart(cs_l->getStopOffset());
+ const double fOffsetEnd(cs_r->getStopOffset());
- // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
- aNew.scale(0.5, 1.0);
- aNew.translate(0.5, 0.0);
+ // same offset, empty BColorStopRange, continue with next step
+ if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
+ continue;
- // scale/translate in X
- aNew.scale(mfUnitWidth, 1.0);
- aNew.translate(mfUnitMinX, 0.0);
+ // get colors & calculate steps
+ const basegfx::BColor aCStart(cs_l->getStopColor());
+ const basegfx::BColor aCEnd(cs_r->getStopColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
- // already centered in Y on X-Axis, just scale in Y
- aNew.scale(1.0, 1.0 - fPos);
+ // calculate StripeWidth
+ // nSteps is >= 1, see getRequestedSteps, so no check needed here
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
- // set at target
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew;
+ // for the 1st color range we do not need to create the 1st step, see above
+ const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
+ {
+ // calculate pos in Y
+ const double fPos(fOffsetStart + (fStripeWidth * innerLoop));
- // interpolate and set color
- aB2DHomMatrixAndBColor.maBColor = interpolate(maEnd, maStart, double(a) / double(maGradientInfo.getSteps() - 1));
+ // already centered in Y on X-Axis, just scale in Y
+ basegfx::B2DHomMatrix aNew(aPattern);
+ aNew.scale(1.0, 1.0 - fPos);
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ // set and add at target
+ aCallback(
+ maGradientInfo.getTextureTransform() * aNew,
+ 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
+ }
+ }
+
+ if (bPenultimateUsed)
+ {
+ // correct temporary change
+ mnColorStops.pop_back();
}
}
void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
+
+ // just single color, done
+ if (mnColorStops.size() < 2)
+ {
+ // we use the reverse ColorSteps here, so use front value
+ rBColor = mnColorStops.front().getStopColor();
+ return;
+ }
+
+ // texture-back-transform X/Y -> t [0.0..1.0] and determine color
const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV, maGradientInfo));
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ // we use the reverse ColorSteps here, so mirror scaler value
+ rBColor = mnColorStops.getInterpolatedBColor(1.0 - fScaler, mnRequestedSteps, maLastColorStopRange);
}
GeoTexSvxGradientRadial::GeoTexSvxGradientRadial(
const basegfx::B2DRange& rDefinitionRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
- sal_uInt32 nSteps,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder,
double fOffsetX,
double fOffsetY)
- : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
{
maGradientInfo = basegfx::utils::createRadialODFGradientInfo(
rDefinitionRange,
basegfx::B2DVector(fOffsetX,fOffsetY),
- nSteps,
+ nRequestedSteps,
fBorder);
}
@@ -284,49 +405,100 @@ namespace drawinglayer::texture
}
void GeoTexSvxGradientRadial::appendTransformationsAndColors(
- std::vector< B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor)
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
{
- rOuterColor = maStart;
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- if(!maGradientInfo.getSteps())
+ // only one color, done
+ if (mnColorStops.size() < 2)
return;
- const double fStepSize(1.0 / maGradientInfo.getSteps());
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // check if we need last-ColorStop-correction
+ const bool bPenultimateUsed(mnColorStops.checkPenultimate());
+
+ if (bPenultimateUsed)
+ {
+ // temporarily add a ColorStop entry
+ mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
+ }
+
+ // outer loop over ColorStops, each is from cs_l to cs_r
+ for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++)
+ {
+ // get offsets
+ const double fOffsetStart(cs_l->getStopOffset());
+ const double fOffsetEnd(cs_r->getStopOffset());
+
+ // same offset, empty BColorStopRange, continue with next step
+ if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
+ continue;
+
+ // get colors & calculate steps
+ const basegfx::BColor aCStart(cs_l->getStopColor());
+ const basegfx::BColor aCEnd(cs_r->getStopColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
+
+ // calculate StripeWidth
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
+
+ // get correct start for inner loop (see above)
+ const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
+ {
+ // calculate size/radius
+ const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop)));
+
+ // set and add at target
+ aCallback(
+ maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize),
+ 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
+ }
+ }
- for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ if (bPenultimateUsed)
{
- const double fSize(1.0 - (fStepSize * a));
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize);
- aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ // correct temporary change
+ mnColorStops.pop_back();
}
}
void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
- const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo));
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
+
+ // just single color, done
+ if (mnColorStops.size() < 2)
+ {
+ rBColor = mnColorStops.front().getStopColor();
+ return;
+ }
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ // texture-back-transform X/Y -> t [0.0..1.0] and determine color
+ const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo));
+ rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange);
}
GeoTexSvxGradientElliptical::GeoTexSvxGradientElliptical(
const basegfx::B2DRange& rDefinitionRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
- sal_uInt32 nSteps,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder,
double fOffsetX,
double fOffsetY,
double fAngle)
- : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
{
maGradientInfo = basegfx::utils::createEllipticalODFGradientInfo(
rDefinitionRange,
basegfx::B2DVector(fOffsetX,fOffsetY),
- nSteps,
+ nRequestedSteps,
fBorder,
fAngle);
}
@@ -336,67 +508,107 @@ namespace drawinglayer::texture
}
void GeoTexSvxGradientElliptical::appendTransformationsAndColors(
- std::vector< B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor)
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
{
- rOuterColor = maStart;
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- if(!maGradientInfo.getSteps())
+ // only one color, done
+ if (mnColorStops.size() < 2)
return;
- double fWidth(1.0);
- double fHeight(1.0);
- double fIncrementX(0.0);
- double fIncrementY(0.0);
+ // check if we need last-ColorStop-correction
+ const bool bPenultimateUsed(mnColorStops.checkPenultimate());
- if(maGradientInfo.getAspectRatio() > 1.0)
+ if (bPenultimateUsed)
{
- fIncrementY = fHeight / maGradientInfo.getSteps();
- fIncrementX = fIncrementY / maGradientInfo.getAspectRatio();
- }
- else
- {
- fIncrementX = fWidth / maGradientInfo.getSteps();
- fIncrementY = fIncrementX * maGradientInfo.getAspectRatio();
+ // temporarily add a ColorStop entry
+ mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
}
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // prepare vars dependent on aspect ratio
+ const double fAR(maGradientInfo.getAspectRatio());
+ const bool bMTO(fAR > 1.0);
- for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ // outer loop over ColorStops, each is from cs_l to cs_r
+ for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++)
{
- // next step
- fWidth -= fIncrementX;
- fHeight -= fIncrementY;
+ // get offsets
+ const double fOffsetStart(cs_l->getStopOffset());
+ const double fOffsetEnd(cs_r->getStopOffset());
+
+ // same offset, empty BColorStopRange, continue with next step
+ if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
+ continue;
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight);
- aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ // get colors & calculate steps
+ const basegfx::BColor aCStart(cs_l->getStopColor());
+ const basegfx::BColor aCEnd(cs_r->getStopColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
+
+ // calculate StripeWidth
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
+
+ // get correct start for inner loop (see above)
+ const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
+ {
+ // calculate offset position for entry
+ const double fSize(fOffsetStart + (fStripeWidth * innerLoop));
+
+ // set and add at target
+ aCallback(
+ maGradientInfo.getTextureTransform()
+ * basegfx::utils::createScaleB2DHomMatrix(
+ 1.0 - (bMTO ? fSize / fAR : fSize),
+ 1.0 - (bMTO ? fSize : fSize * fAR)),
+ 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
+ }
+ }
+
+ if (bPenultimateUsed)
+ {
+ // correct temporary change
+ mnColorStops.pop_back();
}
}
void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
- const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo));
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ // just single color, done
+ if (mnColorStops.size() < 2)
+ {
+ rBColor = mnColorStops.front().getStopColor();
+ return;
+ }
+
+ // texture-back-transform X/Y -> t [0.0..1.0] and determine color
+ const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo));
+ rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange);
}
GeoTexSvxGradientSquare::GeoTexSvxGradientSquare(
const basegfx::B2DRange& rDefinitionRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
- sal_uInt32 nSteps,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder,
double fOffsetX,
double fOffsetY,
double fAngle)
- : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
{
maGradientInfo = basegfx::utils::createSquareODFGradientInfo(
rDefinitionRange,
basegfx::B2DVector(fOffsetX,fOffsetY),
- nSteps,
+ nRequestedSteps,
fBorder,
fAngle);
}
@@ -406,49 +618,100 @@ namespace drawinglayer::texture
}
void GeoTexSvxGradientSquare::appendTransformationsAndColors(
- std::vector< B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor)
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
{
- rOuterColor = maStart;
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- if(!maGradientInfo.getSteps())
+ // only one color, done
+ if (mnColorStops.size() < 2)
return;
- const double fStepSize(1.0 / maGradientInfo.getSteps());
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // check if we need last-ColorStop-correction
+ const bool bPenultimateUsed(mnColorStops.checkPenultimate());
- for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ if (bPenultimateUsed)
{
- const double fSize(1.0 - (fStepSize * a));
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize);
- aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ // temporarily add a ColorStop entry
+ mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
+ }
+
+ // outer loop over ColorStops, each is from cs_l to cs_r
+ for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++)
+ {
+ // get offsets
+ const double fOffsetStart(cs_l->getStopOffset());
+ const double fOffsetEnd(cs_r->getStopOffset());
+
+ // same offset, empty BColorStopRange, continue with next step
+ if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
+ continue;
+
+ // get colors & calculate steps
+ const basegfx::BColor aCStart(cs_l->getStopColor());
+ const basegfx::BColor aCEnd(cs_r->getStopColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
+
+ // calculate StripeWidth
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
+
+ // get correct start for inner loop (see above)
+ const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
+ {
+ // calculate size/radius
+ const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop)));
+
+ // set and add at target
+ aCallback(
+ maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize),
+ 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
+ }
+ }
+
+ if (bPenultimateUsed)
+ {
+ // correct temporary change
+ mnColorStops.pop_back();
}
}
void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
- const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo));
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
+
+ // just single color, done
+ if (mnColorStops.size() < 2)
+ {
+ rBColor = mnColorStops.front().getStopColor();
+ return;
+ }
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ // texture-back-transform X/Y -> t [0.0..1.0] and determine color
+ const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo));
+ rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange);
}
GeoTexSvxGradientRect::GeoTexSvxGradientRect(
const basegfx::B2DRange& rDefinitionRange,
- const basegfx::BColor& rStart,
- const basegfx::BColor& rEnd,
- sal_uInt32 nSteps,
+ sal_uInt32 nRequestedSteps,
+ const basegfx::BColorStops& rColorStops,
double fBorder,
double fOffsetX,
double fOffsetY,
double fAngle)
- : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder)
+ : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
{
maGradientInfo = basegfx::utils::createRectangularODFGradientInfo(
rDefinitionRange,
basegfx::B2DVector(fOffsetX,fOffsetY),
- nSteps,
+ nRequestedSteps,
fBorder,
fAngle);
}
@@ -458,49 +721,90 @@ namespace drawinglayer::texture
}
void GeoTexSvxGradientRect::appendTransformationsAndColors(
- std::vector< B2DHomMatrixAndBColor >& rEntries,
- basegfx::BColor& rOuterColor)
+ std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
{
- rOuterColor = maStart;
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- if(!maGradientInfo.getSteps())
+ // only one color, done
+ if (mnColorStops.size() < 2)
return;
- double fWidth(1.0);
- double fHeight(1.0);
- double fIncrementX(0.0);
- double fIncrementY(0.0);
+ // check if we need last-ColorStop-correction
+ const bool bPenultimateUsed(mnColorStops.checkPenultimate());
- if(maGradientInfo.getAspectRatio() > 1.0)
- {
- fIncrementY = fHeight / maGradientInfo.getSteps();
- fIncrementX = fIncrementY / maGradientInfo.getAspectRatio();
- }
- else
+ if (bPenultimateUsed)
{
- fIncrementX = fWidth / maGradientInfo.getSteps();
- fIncrementY = fIncrementX * maGradientInfo.getAspectRatio();
+ // temporarily add a ColorStop entry
+ mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
}
- B2DHomMatrixAndBColor aB2DHomMatrixAndBColor;
+ // prepare vars dependent on aspect ratio
+ const double fAR(maGradientInfo.getAspectRatio());
+ const bool bMTO(fAR > 1.0);
- for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++)
+ // outer loop over ColorStops, each is from cs_l to cs_r
+ for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++)
{
- // next step
- fWidth -= fIncrementX;
- fHeight -= fIncrementY;
+ // get offsets
+ const double fOffsetStart(cs_l->getStopOffset());
+ const double fOffsetEnd(cs_r->getStopOffset());
+
+ // same offset, empty BColorStopRange, continue with next step
+ if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
+ continue;
+
+ // get colors & calculate steps
+ const basegfx::BColor aCStart(cs_l->getStopColor());
+ const basegfx::BColor aCEnd(cs_r->getStopColor());
+ const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps(
+ maGradientInfo.getRequestedSteps(), aCStart, aCEnd));
+
+ // calculate StripeWidth
+ const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
- aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight);
- aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1));
- rEntries.push_back(aB2DHomMatrixAndBColor);
+ // get correct start for inner loop (see above)
+ const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
+
+ for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
+ {
+ // calculate offset position for entry
+ const double fSize(fOffsetStart + (fStripeWidth * innerLoop));
+
+ // set and add at target
+ aCallback(
+ maGradientInfo.getTextureTransform()
+ * basegfx::utils::createScaleB2DHomMatrix(
+ 1.0 - (bMTO ? fSize / fAR : fSize),
+ 1.0 - (bMTO ? fSize : fSize * fAR)),
+ 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
+ }
+ }
+
+ if (bPenultimateUsed)
+ {
+ // correct temporary change
+ mnColorStops.pop_back();
}
}
void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
{
- const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo));
+ // no color at all, done
+ if (mnColorStops.empty())
+ return;
- rBColor = basegfx::interpolate(maStart, maEnd, fScaler);
+ // just single color, done
+ if (mnColorStops.size() < 2)
+ {
+ rBColor = mnColorStops.front().getStopColor();
+ return;
+ }
+
+ // texture-back-transform X/Y -> t [0.0..1.0] and determine color
+ const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo));
+ rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange);
}
@@ -627,8 +931,20 @@ namespace drawinglayer::texture
double GeoTexSvxHatch::getDistanceToHatch(const basegfx::B2DPoint& rUV) const
{
- const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV);
- return fmod(aCoor.getY(), mfDistance);
+ // the below is an inlined and optimised version of
+ // const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV);
+ // return fmod(aCoor.getY(), mfDistance);
+
+ const basegfx::B2DHomMatrix& rMat = getBackTextureTransform();
+ double fX = rUV.getX();
+ double fY = rUV.getY();
+
+ double fTempY(
+ rMat.get(1, 0) * fX +
+ rMat.get(1, 1) * fY +
+ rMat.get(1, 2));
+
+ return fmod(fTempY, mfDistance);
}
const basegfx::B2DHomMatrix& GeoTexSvxHatch::getBackTextureTransform() const
@@ -708,7 +1024,7 @@ namespace drawinglayer::texture
sal_Int32 nPosX(0);
sal_Int32 nPosY(0);
- if(basegfx::fTools::more(fStartX, 0.0))
+ if(fStartX > 0.0)
{
const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartX / fWidth)) + 1);
@@ -716,7 +1032,7 @@ namespace drawinglayer::texture
fStartX -= nDiff * fWidth;
}
- if(basegfx::fTools::less(fStartX + fWidth, 0.0))
+ if((fStartX + fWidth) < 0.0)
{
const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartX / fWidth)));
@@ -724,7 +1040,7 @@ namespace drawinglayer::texture
fStartX += nDiff * fWidth;
}
- if(basegfx::fTools::more(fStartY, 0.0))
+ if(fStartY > 0.0)
{
const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartY / fHeight)) + 1);
@@ -732,7 +1048,7 @@ namespace drawinglayer::texture
fStartY -= nDiff * fHeight;
}
- if(basegfx::fTools::less(fStartY + fHeight, 0.0))
+ if((fStartY + fHeight) < 0.0)
{
const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartY / fHeight)));
diff --git a/drawinglayer/source/texture/texture3d.cxx b/drawinglayer/source/texture/texture3d.cxx
index 0a83937991d2..4cbcab9d1c5d 100644
--- a/drawinglayer/source/texture/texture3d.cxx
+++ b/drawinglayer/source/texture/texture3d.cxx
@@ -26,6 +26,7 @@
#include <vcl/BitmapTools.hxx>
#include <primitive3d/hatchtextureprimitive3d.hxx>
#include <sal/log.hxx>
+#include <osl/diagnose.h>
namespace drawinglayer::texture
{
@@ -74,12 +75,12 @@ namespace drawinglayer::texture
if(mbIsAlpha)
{
- maTransparence = rBitmapEx.GetAlpha().GetBitmap();
- mpReadTransparence = Bitmap::ScopedReadAccess(maTransparence);
+ maTransparence = rBitmapEx.GetAlphaMask().GetBitmap();
+ mpReadTransparence = maTransparence;
}
if (!maBitmap.IsEmpty())
- mpReadBitmap = Bitmap::ScopedReadAccess(maBitmap);
+ mpReadBitmap = maBitmap;
SAL_WARN_IF(!mpReadBitmap, "drawinglayer", "GeoTexSvxBitmapEx: Got no read access to Bitmap");
if (mpReadBitmap)
{
@@ -102,7 +103,7 @@ namespace drawinglayer::texture
{
}
- sal_uInt8 GeoTexSvxBitmapEx::impGetTransparence(sal_Int32 rX, sal_Int32 rY) const
+ sal_uInt8 GeoTexSvxBitmapEx::impGetAlpha(sal_Int32 rX, sal_Int32 rY) const
{
if(mbIsAlpha)
{
@@ -147,10 +148,10 @@ namespace drawinglayer::texture
if(mbIsAlpha)
{
- // when we have a transparence, make use of it
- const sal_uInt8 aLuminance(impGetTransparence(nX, nY));
+ // when we have alpha, make use of it
+ const sal_uInt8 aAlpha(impGetAlpha(nX, nY));
- rfOpacity = (static_cast<double>(0xff - aLuminance) * (1.0 / 255.0));
+ rfOpacity = (static_cast<double>(aAlpha) * (1.0 / 255.0));
}
else
{
@@ -172,8 +173,8 @@ namespace drawinglayer::texture
if(mbIsAlpha)
{
// this texture has an alpha part, use it
- const sal_uInt8 aLuminance(impGetTransparence(nX, nY));
- const double fNewOpacity(static_cast<double>(0xff - aLuminance) * (1.0 / 255.0));
+ const sal_uInt8 aAlpha(impGetAlpha(nX, nY));
+ const double fNewOpacity(static_cast<double>(aAlpha) * (1.0 / 255.0));
rfOpacity = 1.0 - ((1.0 - fNewOpacity) * (1.0 - rfOpacity));
}
diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx
index 382b81197526..cf339f8aaa20 100644
--- a/drawinglayer/source/tools/converters.cxx
+++ b/drawinglayer/source/tools/converters.cxx
@@ -23,153 +23,360 @@
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <vcl/svapp.hxx>
#include <vcl/virdev.hxx>
+#include <com/sun/star/geometry/RealRectangle2D.hpp>
+#include <comphelper/diagnose_ex.hxx>
#include <drawinglayer/converters.hxx>
#ifdef DBG_UTIL
#include <tools/stream.hxx>
-#include <vcl/pngwrite.hxx>
+// #include <vcl/filter/PngImageWriter.hxx>
+#include <vcl/dibtools.hxx>
#endif
+// #include <vcl/BitmapReadAccess.hxx>
+
+namespace
+{
+bool implPrepareConversion(drawinglayer::primitive2d::Primitive2DContainer& rSequence,
+ sal_uInt32& rnDiscreteWidth, sal_uInt32& rnDiscreteHeight,
+ const sal_uInt32 nMaxSquarePixels)
+{
+ if (rSequence.empty())
+ return false;
+
+ if (rnDiscreteWidth <= 0 || rnDiscreteHeight <= 0)
+ return false;
+
+ const sal_uInt32 nViewVisibleArea(rnDiscreteWidth * rnDiscreteHeight);
+
+ if (nViewVisibleArea > nMaxSquarePixels)
+ {
+ // reduce render size
+ double fReduceFactor
+ = sqrt(static_cast<double>(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
+ rnDiscreteWidth = basegfx::fround(static_cast<double>(rnDiscreteWidth) * fReduceFactor);
+ rnDiscreteHeight = basegfx::fround(static_cast<double>(rnDiscreteHeight) * fReduceFactor);
+
+ const drawinglayer::primitive2d::Primitive2DReference aEmbed(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor),
+ std::move(rSequence)));
+
+ rSequence = drawinglayer::primitive2d::Primitive2DContainer{ aEmbed };
+ }
+
+ return true;
+}
+
+AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& rSequence,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D,
+ const Size& rSizePixel, bool bUseLuminance)
+{
+ ScopedVclPtrInstance<VirtualDevice> pContent;
+
+ // prepare vdev
+ if (!pContent->SetOutputSizePixel(rSizePixel, false))
+ {
+ SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << rSizePixel.Width() << "x"
+ << rSizePixel.Height());
+ return AlphaMask();
+ }
+
+ // create pixel processor, also already takes care of AAing and
+ // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If
+ // not wanted, change after this call as needed
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pContentProcessor
+ = drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(*pContent,
+ rViewInformation2D);
+
+ // prepare for mask creation
+ pContent->SetMapMode(MapMode(MapUnit::MapPixel));
+
+ // set transparency to all white (fully transparent)
+ pContent->Erase();
+
+ basegfx::BColorModifierSharedPtr aBColorModifier;
+ if (bUseLuminance)
+ {
+ // new mode: bUseLuminance allows simple creation of alpha channels
+ // for any content (e.g. gradients)
+ aBColorModifier = std::make_shared<basegfx::BColorModifier_luminance_to_alpha>();
+ }
+ else
+ {
+ // Embed primitives to paint them black (fully opaque)
+ aBColorModifier
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0, 0.0, 0.0));
+ }
+ const drawinglayer::primitive2d::Primitive2DReference xRef(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(std::move(rSequence),
+ aBColorModifier));
+ const drawinglayer::primitive2d::Primitive2DContainer xSeq{ xRef };
+
+ // render
+ pContentProcessor->process(xSeq);
+ pContentProcessor.reset();
+
+ // get alpha channel from vdev
+ pContent->EnableMapMode(false);
+ const Point aEmptyPoint;
+
+ // Convert from transparency->alpha.
+ // FIXME in theory I should be able to directly construct alpha by using black as background
+ // and white as foreground, but that doesn't work for some reason.
+ Bitmap aContentBitmap = pContent->GetBitmap(aEmptyPoint, rSizePixel);
+ aContentBitmap.Invert();
+
+ return AlphaMask(aContentBitmap);
+}
+}
+
namespace drawinglayer
{
+AlphaMask createAlphaMask(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D,
+ sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
+ sal_uInt32 nMaxSquarePixels, bool bUseLuminance)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
- BitmapEx convertToBitmapEx(
- drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
- const geometry::ViewInformation2D& rViewInformation2D,
- sal_uInt32 nDiscreteWidth,
- sal_uInt32 nDiscreteHeight,
- sal_uInt32 nMaxSquarePixels)
+ if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels))
{
- BitmapEx aRetval;
+ return AlphaMask();
+ }
- if(!rSeq.empty() && nDiscreteWidth && nDiscreteHeight)
- {
- // get destination size in pixels
- const MapMode aMapModePixel(MapUnit::MapPixel);
- const sal_uInt32 nViewVisibleArea(nDiscreteWidth * nDiscreteHeight);
- drawinglayer::primitive2d::Primitive2DContainer aSequence;
-
- if(nViewVisibleArea > nMaxSquarePixels)
- {
- // reduce render size
- double fReduceFactor = sqrt(static_cast<double>(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea));
- nDiscreteWidth = basegfx::fround(static_cast<double>(nDiscreteWidth) * fReduceFactor);
- nDiscreteHeight = basegfx::fround(static_cast<double>(nDiscreteHeight) * fReduceFactor);
-
- const drawinglayer::primitive2d::Primitive2DReference aEmbed(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor),
- std::move(rSeq)));
-
- aSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed };
- }
- else
- aSequence = std::move(rSeq);
-
- const Point aEmptyPoint;
- const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
- ScopedVclPtrInstance< VirtualDevice > pContent;
-
- // prepare vdev
- pContent->SetOutputSizePixel(aSizePixel, false);
- pContent->SetMapMode(aMapModePixel);
-
- // set to all white
- pContent->SetBackground(Wallpaper(COL_WHITE));
- pContent->Erase();
-
- // create pixel processor, also already takes care of AAing and
- // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If
- // not wanted, change after this call as needed
- std::unique_ptr<processor2d::BaseProcessor2D> pContentProcessor = processor2d::createPixelProcessor2DFromOutputDevice(
- *pContent,
- rViewInformation2D);
+ const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
-#ifdef DBG_UTIL
- static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
-#endif
- // render content
- pContentProcessor->process(aSequence);
+ return implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, bUseLuminance);
+}
- // get content
- pContent->EnableMapMode(false);
- const Bitmap aContent(pContent->GetBitmap(aEmptyPoint, aSizePixel));
+BitmapEx convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSeq,
+ const geometry::ViewInformation2D& rViewInformation2D,
+ sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight,
+ sal_uInt32 nMaxSquarePixels, bool bForceAlphaMaskCreation)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq));
+
+ if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels))
+ {
+ return BitmapEx();
+ }
+
+ const Point aEmptyPoint;
+ const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
+
+ // Create target VirtualDevice. Go back to using a simple RGB
+ // target version (compared with former version, see history).
+ // Reasons are manyfold:
+ // - Avoid the RGBA mode for VirtualDevice (two VDevs)
+ // - It's not suggested to be used outside presentation engine
+ // - It only works *by chance* with VCLPrimitiveRenderer
+ // - Usage of two-VDev alpha-VDev avoided alpha blending against
+ // COL_WHITE in the 1st layer of targets (not in buffers below)
+ // but is kind of a 'hack' doing so
+ // - Other renderers (system-dependent PrimitiveRenderers, other
+ // than the VCL-based ones) will probably not support splitted
+ // VDevs for content/alpha, so require a method that works with
+ // RGB targeting (for now)
+ // - Less resource usage, better speed (no 2 VDevs, no merge of
+ // AlphaChannels)
+ // As long as not all our mechanisms are changed to RGBA completely,
+ // mixing these is just too dangerous and expensive and may to wrong
+ // or deliver bad quality results.
+ // Nonetheless we need a RGBA result here. Luckily we are able to
+ // create a complete and valid AlphaChannel using 'createAlphaMask'
+ // above.
+ // When we know the content (RGB result from renderer), alpha
+ // (result from createAlphaMask) and the start condition (content
+ // rendered against COL_WHITE), it is possible to calculate back
+ // the content, quasi 'remove' that initial blending against
+ // COL_WHITE.
+ // That is what the helper Bitmap::RemoveBlendedStartColor does.
+ // Luckily we only need it for this 'convertToBitmapEx', not in
+ // any other rendering. It could be further optimized, too.
+ // This gives good results, it is in principle comparable with
+ // the results using pre-multiplied alpha tooling, also reducing
+ // the range of values where high alpha values are used.
+ ScopedVclPtrInstance<VirtualDevice> pContent(*Application::GetDefaultDevice());
+
+ // prepare vdev
+ if (!pContent->SetOutputSizePixel(aSizePixel, false))
+ {
+ SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << aSizePixel.Width() << "x"
+ << aSizePixel.Height());
+ return BitmapEx();
+ }
+
+ // We map to pixel, use that MapMode. Init by erasing.
+ pContent->SetMapMode(MapMode(MapUnit::MapPixel));
+ pContent->Erase();
+
+ // create pixel processor, also already takes care of AAing and
+ // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If
+ // not wanted, change after this call as needed
+ std::unique_ptr<processor2d::BaseProcessor2D> pContentProcessor
+ = processor2d::createPixelProcessor2DFromOutputDevice(*pContent, rViewInformation2D);
+
+ // render content
+ pContentProcessor->process(aSequence);
+
+ // create final BitmapEx result (content)
+ Bitmap aRetval(pContent->GetBitmap(aEmptyPoint, aSizePixel));
#ifdef DBG_UTIL
- if(bDoSaveForVisualControl)
- {
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\test_content.png"
-#else
- "~/test_content.png"
-#endif
- , StreamMode::WRITE|StreamMode::TRUNC);
- BitmapEx aContentEx(aContent);
- vcl::PNGWriter aPNGWriter(aContentEx);
- aPNGWriter.Write(aNew);
- }
-#endif
- // prepare for mask creation
- pContent->SetMapMode(aMapModePixel);
-
- // set alpha to all white (fully transparent)
- pContent->Erase();
-
- // embed primitives to paint them black
- const primitive2d::Primitive2DReference xRef(
- new primitive2d::ModifiedColorPrimitive2D(
- std::move(aSequence),
- std::make_shared<basegfx::BColorModifier_replace>(
- basegfx::BColor(0.0, 0.0, 0.0))));
- const primitive2d::Primitive2DContainer xSeq { xRef };
-
- // render
- pContentProcessor->process(xSeq);
- pContentProcessor.reset();
-
- // get alpha channel from vdev
- pContent->EnableMapMode(false);
- const Bitmap aAlpha(pContent->GetBitmap(aEmptyPoint, aSizePixel));
-#ifdef DBG_UTIL
- if(bDoSaveForVisualControl)
- {
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\test_alpha.png"
-#else
- "~/test_alpha.png"
-#endif
- , StreamMode::WRITE|StreamMode::TRUNC);
- BitmapEx aAlphaEx(aAlpha);
- vcl::PNGWriter aPNGWriter(aAlphaEx);
- aPNGWriter.Write(aNew);
- }
+ static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_content.bmp",
+ StreamMode::WRITE | StreamMode::TRUNC);
+ WriteDIB(aRetval, aNew, false, true);
+ }
+ }
#endif
- // create BitmapEx result
- aRetval = BitmapEx(aContent, AlphaMask(aAlpha));
+ // Create the AlphaMask using a method that does this always correct (also used
+ // now in GlowPrimitive2D and ShadowPrimitive2D which both only need the
+ // AlphaMask to do their job, so speeding that up, too).
+ AlphaMask aAlpha(implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, false));
+
#ifdef DBG_UTIL
- if(bDoSaveForVisualControl)
- {
- SvFileStream aNew(
-#ifdef _WIN32
- "c:\\test_combined.png"
-#else
- "~/test_combined.png"
-#endif
- , StreamMode::WRITE|StreamMode::TRUNC);
- vcl::PNGWriter aPNGWriter(aRetval);
- aPNGWriter.Write(aNew);
- }
+ if (bDoSaveForVisualControl)
+ {
+ // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
+ static const OUString sDumpPath(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ if (!sDumpPath.isEmpty())
+ {
+ SvFileStream aNew(sDumpPath + "test_alpha.bmp", StreamMode::WRITE | StreamMode::TRUNC);
+ WriteDIB(aAlpha.GetBitmap(), aNew, false, true);
+ }
+ }
#endif
+
+ if (bForceAlphaMaskCreation || aAlpha.hasAlpha())
+ {
+ // Need to correct content using known alpha to get to background-free
+ // RGBA result, usable e.g. in PNG export(s) or convert-to-bitmap.
+ // Now that vcl supports bitmaps with an alpha channel, only apply
+ // this correction to bitmaps without an alpha channel.
+ if (pContent->GetBitCount() < 32)
+ {
+ aRetval.RemoveBlendedStartColor(COL_BLACK, aAlpha);
+ }
+ else
+ {
+ // tdf#157558 invert and remove blended white color
+ // Before commit 81994cb2b8b32453a92bcb011830fcb884f22ff3,
+ // RemoveBlendedStartColor(COL_BLACK, aAlpha) would darken
+ // the bitmap when running a slideshow, printing, or exporting
+ // to PDF. To get the same effect, the alpha mask must be
+ // inverted, RemoveBlendedStartColor(COL_WHITE, aAlpha)
+ // called, and the alpha mask uninverted.
+ aAlpha.Invert();
+ aRetval.RemoveBlendedStartColor(COL_WHITE, aAlpha);
+ aAlpha.Invert();
}
+ // return combined result
+ return BitmapEx(aRetval, aAlpha);
+ }
+ else
+ return BitmapEx(aRetval);
+}
+
+BitmapEx convertPrimitive2DContainerToBitmapEx(primitive2d::Primitive2DContainer&& rSequence,
+ const basegfx::B2DRange& rTargetRange,
+ sal_uInt32 nMaximumQuadraticPixels,
+ const o3tl::Length eTargetUnit,
+ const std::optional<Size>& rTargetDPI)
+{
+ if (rSequence.empty())
+ return BitmapEx();
- return aRetval;
+ try
+ {
+ css::geometry::RealRectangle2D aRealRect;
+ aRealRect.X1 = rTargetRange.getMinX();
+ aRealRect.Y1 = rTargetRange.getMinY();
+ aRealRect.X2 = rTargetRange.getMaxX();
+ aRealRect.Y2 = rTargetRange.getMaxY();
+
+ // get system DPI
+ Size aDPI(
+ Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)));
+ if (rTargetDPI.has_value())
+ {
+ aDPI = *rTargetDPI;
+ }
+
+ ::sal_uInt32 DPI_X = aDPI.getWidth();
+ ::sal_uInt32 DPI_Y = aDPI.getHeight();
+ const basegfx::B2DRange aRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2);
+ const double fWidth(aRange.getWidth());
+ const double fHeight(aRange.getHeight());
+
+ if (!(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0)))
+ return BitmapEx();
+
+ if (0 == DPI_X)
+ {
+ DPI_X = 75;
+ }
+
+ if (0 == DPI_Y)
+ {
+ DPI_Y = 75;
+ }
+
+ if (0 == nMaximumQuadraticPixels)
+ {
+ nMaximumQuadraticPixels = 500000;
+ }
+
+ const auto aViewInformation2D = geometry::createViewInformation2D({});
+ const sal_uInt32 nDiscreteWidth(
+ basegfx::fround(o3tl::convert(fWidth, eTargetUnit, o3tl::Length::in) * DPI_X));
+ const sal_uInt32 nDiscreteHeight(
+ basegfx::fround(o3tl::convert(fHeight, eTargetUnit, o3tl::Length::in) * DPI_Y));
+
+ basegfx::B2DHomMatrix aEmbedding(
+ basegfx::utils::createTranslateB2DHomMatrix(-aRange.getMinX(), -aRange.getMinY()));
+
+ aEmbedding.scale(nDiscreteWidth / fWidth, nDiscreteHeight / fHeight);
+
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(aEmbedding, std::move(rSequence)));
+ primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef };
+
+ BitmapEx aBitmapEx(convertToBitmapEx(std::move(xEmbedSeq), aViewInformation2D,
+ nDiscreteWidth, nDiscreteHeight,
+ nMaximumQuadraticPixels));
+
+ if (aBitmapEx.IsEmpty())
+ return BitmapEx();
+ aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aBitmapEx.SetPrefSize(Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight)));
+
+ return aBitmapEx;
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!");
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what());
}
+ return BitmapEx();
+}
} // end of namespace drawinglayer
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfpbrush.cxx b/drawinglayer/source/tools/emfpbrush.cxx
index 4acc311345a8..c79b0ded0748 100644
--- a/drawinglayer/source/tools/emfpbrush.cxx
+++ b/drawinglayer/source/tools/emfpbrush.cxx
@@ -113,17 +113,12 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tCenter color: 0x" << std::hex << color << std::dec);
s.ReadFloat(firstPointX).ReadFloat(firstPointY);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tCenter point: " << firstPointX << "," << firstPointY);
- s.ReadInt32(surroundColorsNumber);
+ s.ReadUInt32(surroundColorsNumber);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t number of surround colors: " << surroundColorsNumber);
- if (surroundColorsNumber<0 || o3tl::make_unsigned(surroundColorsNumber)>SAL_MAX_INT32 / sizeof(::Color))
- {
- surroundColorsNumber = SAL_MAX_INT32 / sizeof(::Color);
- }
-
surroundColors.reset( new ::Color[surroundColorsNumber] );
- for (int i = 0; i < surroundColorsNumber; i++)
+ for (sal_uInt32 i = 0; i < surroundColorsNumber; i++)
{
s.ReadUInt32(color);
surroundColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
@@ -132,7 +127,7 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tSurround color[" << i << "]: 0x" << std::hex << color << std::dec);
}
- if (additionalFlags & 0x01)
+ if (additionalFlags & 0x01) // BrushDataPath
{
sal_Int32 pathLength;
@@ -180,60 +175,53 @@ namespace emfplushelper
<< aBounds.getWidth() << "x" << aBounds.getHeight());
}
- if (additionalFlags & 0x02)
+ if (additionalFlags & 0x02) // BrushDataTransform
{
EmfPlusHelperData::readXForm(s, brush_transformation);
hasTransformation = true;
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation);
}
- if (additionalFlags & 0x08)
+ // BrushDataPresetColors and BrushDataBlendFactorsH
+ if ((additionalFlags & 0x04) && (additionalFlags & 0x08))
{
- s.ReadInt32(blendPoints);
+ SAL_WARN("drawinglayer.emf", "EMF+\t Brush must not contain both BrushDataPresetColors and BrushDataBlendFactorsH");
+ return;
+ }
+ if (additionalFlags & 0x08) // BrushDataBlendFactorsH
+ {
+ s.ReadUInt32(blendPoints);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tuse blend, points: " << blendPoints);
- if (blendPoints<0 || o3tl::make_unsigned(blendPoints)>SAL_MAX_INT32 / (2 * sizeof(float)))
- blendPoints = SAL_MAX_INT32 / (2 * sizeof(float));
blendPositions.reset( new float[2 * blendPoints] );
blendFactors = blendPositions.get() + blendPoints;
- for (int i = 0; i < blendPoints; i++)
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
{
s.ReadFloat(blendPositions[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tposition[" << i << "]: " << blendPositions[i]);
}
- for (int i = 0; i < blendPoints; i++)
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
{
s.ReadFloat(blendFactors[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]);
}
}
- if (additionalFlags & 0x04)
+ if (additionalFlags & 0x04) // BrushDataPresetColors
{
- s.ReadInt32(colorblendPoints);
+ s.ReadUInt32(colorblendPoints);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints);
-
- if (colorblendPoints<0 || o3tl::make_unsigned(colorblendPoints)>SAL_MAX_INT32 / sizeof(float))
- {
- colorblendPoints = SAL_MAX_INT32 / sizeof(float);
- }
-
- if (o3tl::make_unsigned(colorblendPoints) > SAL_MAX_INT32 / sizeof(::Color))
- {
- colorblendPoints = SAL_MAX_INT32 / sizeof(::Color);
- }
-
colorblendPositions.reset( new float[colorblendPoints] );
colorblendColors.reset( new ::Color[colorblendPoints] );
- for (int i = 0; i < colorblendPoints; i++)
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
{
s.ReadFloat(colorblendPositions[i]);
SAL_INFO("drawinglayer.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions[i]);
}
- for (int i = 0; i < colorblendPoints; i++)
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
{
s.ReadUInt32(color);
colorblendColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
@@ -262,29 +250,33 @@ namespace emfplushelper
s.ReadUInt32(color);
s.ReadUInt32(color);
- if (additionalFlags & 0x02)
+ if (additionalFlags & 0x02) //BrushDataTransform
{
EmfPlusHelperData::readXForm(s, brush_transformation);
hasTransformation = true;
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation);
}
+ // BrushDataPresetColors and BrushDataBlendFactorsH
+ if ((additionalFlags & 0x04) && (additionalFlags & 0x08))
+ {
+ SAL_WARN("drawinglayer.emf", "EMF+\t Brush must not contain both BrushDataPresetColors and BrushDataBlendFactorsH");
+ return;
+ }
- if (additionalFlags & 0x08)
+ if (additionalFlags & 0x08) // BrushDataBlendFactorsH
{
- s.ReadInt32(blendPoints);
+ s.ReadUInt32(blendPoints);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse blend, points: " << blendPoints);
- if (blendPoints<0 || o3tl::make_unsigned(blendPoints)>SAL_MAX_INT32 / (2 * sizeof(float)))
- blendPoints = SAL_MAX_INT32 / (2 * sizeof(float));
blendPositions.reset( new float[2 * blendPoints] );
blendFactors = blendPositions.get() + blendPoints;
- for (int i = 0; i < blendPoints; i++)
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
{
s.ReadFloat(blendPositions[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPosition[" << i << "]: " << blendPositions[i]);
}
- for (int i = 0; i < blendPoints; i++)
+ for (sal_uInt32 i = 0; i < blendPoints; i++)
{
s.ReadFloat(blendFactors[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]);
@@ -293,29 +285,18 @@ namespace emfplushelper
if (additionalFlags & 0x04)
{
- s.ReadInt32(colorblendPoints);
+ s.ReadUInt32(colorblendPoints);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints);
-
- if (colorblendPoints<0 || o3tl::make_unsigned(colorblendPoints)>SAL_MAX_INT32 / sizeof(float))
- {
- colorblendPoints = SAL_MAX_INT32 / sizeof(float);
- }
-
- if (o3tl::make_unsigned(colorblendPoints) > SAL_MAX_INT32 / sizeof(::Color))
- {
- colorblendPoints = sal_uInt32(SAL_MAX_INT32) / sizeof(::Color);
- }
-
colorblendPositions.reset( new float[colorblendPoints] );
colorblendColors.reset( new ::Color[colorblendPoints] );
- for (int i = 0; i < colorblendPoints; i++)
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
{
s.ReadFloat(colorblendPositions[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPosition[" << i << "]: " << colorblendPositions[i]);
}
- for (int i = 0; i < colorblendPoints; i++)
+ for (sal_uInt32 i = 0; i < colorblendPoints; i++)
{
s.ReadUInt32(color);
colorblendColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
diff --git a/drawinglayer/source/tools/emfpbrush.hxx b/drawinglayer/source/tools/emfpbrush.hxx
index ec4ab11732f2..aee3fe02f60e 100644
--- a/drawinglayer/source/tools/emfpbrush.hxx
+++ b/drawinglayer/source/tools/emfpbrush.hxx
@@ -90,7 +90,7 @@ namespace emfplushelper
BrushTypeLinearGradient = 0x00000004
};
- struct EMFPPath;
+ class EMFPPath;
struct EMFPBrush : public EMFPObject
{
@@ -104,13 +104,13 @@ namespace emfplushelper
::Color secondColor; // first color is stored in solidColor;
basegfx::B2DHomMatrix brush_transformation;
bool hasTransformation;
- sal_Int32 blendPoints;
+ sal_uInt32 blendPoints;
std::unique_ptr<float[]> blendPositions;
float* blendFactors;
- sal_Int32 colorblendPoints;
+ sal_uInt32 colorblendPoints;
std::unique_ptr<float[]> colorblendPositions;
std::unique_ptr<::Color[]> colorblendColors;
- sal_Int32 surroundColorsNumber;
+ sal_uInt32 surroundColorsNumber;
std::unique_ptr<::Color[]> surroundColors;
std::unique_ptr<EMFPPath> path;
EmfPlusHatchStyle hatchStyle;
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.cxx b/drawinglayer/source/tools/emfpcustomlinecap.cxx
index e24cbcc32cb1..e457a36cc2c4 100644
--- a/drawinglayer/source/tools/emfpcustomlinecap.cxx
+++ b/drawinglayer/source/tools/emfpcustomlinecap.cxx
@@ -21,6 +21,7 @@
#include "emfpcustomlinecap.hxx"
#include "emfppath.hxx"
#include "emfppen.hxx"
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
using namespace ::com::sun::star;
using namespace ::basegfx;
@@ -39,19 +40,11 @@ namespace emfplushelper
, strokeEndCap(0)
, strokeJoin(0)
, miterLimit(0.0)
+ , widthScale(0.0)
, mbIsFilled(false)
{
}
- void EMFPCustomLineCap::SetAttributes(rendering::StrokeAttributes& aAttributes)
- {
- aAttributes.StartCapType = EMFPPen::lcl_convertStrokeCap(strokeStartCap);
- aAttributes.EndCapType = EMFPPen::lcl_convertStrokeCap(strokeEndCap);
- aAttributes.JoinType = EMFPPen::lcl_convertLineJoinType(strokeJoin);
-
- aAttributes.MiterLimit = miterLimit;
- }
-
void EMFPCustomLineCap::ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill)
{
sal_Int32 pathLength;
@@ -66,6 +59,8 @@ namespace emfplushelper
EMFPPath path(pathPoints);
path.Read(s, pathFlags);
polygon = path.GetPolygon(rR, false);
+ // rotate polygon by 180 degrees
+ polygon.transform(basegfx::utils::createRotateB2DHomMatrix(M_PI));
mbIsFilled = bFill;
}
@@ -80,7 +75,6 @@ namespace emfplushelper
{
sal_uInt32 customLineCapDataFlags, baseCap;
float baseInset;
- float widthScale;
float fillHotSpotX, fillHotSpotY, strokeHotSpotX, strokeHotSpotY;
s.ReadUInt32(customLineCapDataFlags).ReadUInt32(baseCap).ReadFloat(baseInset)
@@ -91,11 +85,6 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags);
SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseInset: " << baseInset);
- SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
- SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
- SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
- SAL_INFO("drawinglayer.emf", "EMF+\t\tmiterLimit: " << miterLimit);
- SAL_INFO("drawinglayer.emf", "EMF+\t\twidthScale: " << widthScale);
if (customLineCapDataFlags & EmfPlusCustomLineCapDataFillPath)
{
@@ -112,16 +101,20 @@ namespace emfplushelper
// TODO only reads the data, does not use them [I've had
// no test document to be able to implement it]
- sal_Int32 width, height, middleInset, fillState, lineStartCap;
- sal_Int32 lineEndCap, lineJoin, widthScale;
- float fillHotSpotX, fillHotSpotY, lineHotSpotX, lineHotSpotY;
+ sal_Int32 fillState;
+ float width, height, middleInset, unusedHotSpot;
- s.ReadInt32(width).ReadInt32(height).ReadInt32(middleInset).ReadInt32(fillState).ReadInt32(lineStartCap)
- .ReadInt32(lineEndCap).ReadInt32(lineJoin).ReadFloat(miterLimit).ReadInt32(widthScale)
- .ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(lineHotSpotX).ReadFloat(lineHotSpotY);
+ s.ReadFloat(width).ReadFloat(height).ReadFloat(middleInset).ReadInt32(fillState).ReadUInt32(strokeStartCap)
+ .ReadUInt32(strokeEndCap).ReadUInt32(strokeJoin).ReadFloat(miterLimit).ReadFloat(widthScale)
+ .ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot);
SAL_INFO("drawinglayer.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)");
}
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\tmiterLimit: " << miterLimit);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\twidthScale: " << widthScale);
}
}
diff --git a/drawinglayer/source/tools/emfpcustomlinecap.hxx b/drawinglayer/source/tools/emfpcustomlinecap.hxx
index a42e0ab4ef46..22ed6be6dd4d 100644
--- a/drawinglayer/source/tools/emfpcustomlinecap.hxx
+++ b/drawinglayer/source/tools/emfpcustomlinecap.hxx
@@ -19,7 +19,6 @@
#pragma once
-#include <com/sun/star/rendering/StrokeAttributes.hpp>
#include "emfphelperdata.hxx"
namespace emfplushelper
@@ -28,13 +27,12 @@ namespace emfplushelper
{
sal_uInt32 type;
sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin;
- float miterLimit;
+ float miterLimit, widthScale;
basegfx::B2DPolyPolygon polygon;
bool mbIsFilled;
EMFPCustomLineCap();
- void SetAttributes(com::sun::star::rendering::StrokeAttributes& aAttributes);
void ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill);
void Read(SvStream& s, EmfPlusHelperData const & rR);
};
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
index d2d082424493..1d95f0a8f38a 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -29,6 +29,7 @@
#include "emfpstringformat.hxx"
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <wmfemfhelper.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
@@ -79,6 +80,9 @@ namespace emfplushelper
case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
+ case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
+ case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
+ case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve";
case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
@@ -225,33 +229,33 @@ namespace emfplushelper
{
}
- float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI)
+ double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d)
{
- switch (aUnitType)
+ switch (static_cast<UnitType>(aUnitType))
{
case UnitTypePixel:
- return 1.0f;
+ return n;
case UnitTypePoint:
- return aDPI / 72.0;
+ return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d);
case UnitTypeInch:
- return aDPI;
+ return n * DPI(d);
case UnitTypeMillimeter:
- return aDPI / 25.4;
+ return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d);
case UnitTypeDocument:
- return aDPI / 300.0;
+ return n * DPI(d) / 300.0;
case UnitTypeWorld:
case UnitTypeDisplay:
SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
- return 1.0f;
+ return n;
default:
SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
- return 1.0f;
+ return n;
}
}
@@ -277,7 +281,7 @@ namespace emfplushelper
EMFPPen *pen = new EMFPPen();
maEMFPObjects[index].reset(pen);
pen->Read(rObjectStream, *this);
- pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI);
+ pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal);
break;
}
case EmfPlusObjectTypePath:
@@ -321,7 +325,7 @@ namespace emfplushelper
font->fontFlags = 0;
font->Read(rObjectStream);
// tdf#113624 Convert unit to Pixels
- font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI);
+ font->emSize = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal);
break;
}
@@ -442,6 +446,10 @@ namespace emfplushelper
maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
double(-mnFrameLeft), double(-mnFrameTop));
maMapTransform *= maBaseTransform;
+
+ // Used only for performance optimization, to do not calculate it every line draw
+ mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
+ mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
}
::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
@@ -459,7 +467,7 @@ namespace emfplushelper
}
else // we use a brush
{
- const EMFPBrush* brush = static_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
+ const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
if (brush)
{
color = brush->GetColor();
@@ -486,203 +494,175 @@ namespace emfplushelper
map[ index ] = state;
}
- void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState)
+ void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
{
- GraphicStateMap::iterator iter = map.find( index );
+ GraphicStateMap::iterator iter = map.find(index);
- if ( iter != map.end() )
+ if (iter != map.end())
{
wmfemfhelper::PropertyHolder state = iter->second;
maWorldTransform = state.getTransformation();
- rState.setClipPolyPolygon( state.getClipPolyPolygon() );
+ if (state.getClipPolyPolygonActive())
+ {
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Restore clipping region to saved in index: " << index);
+ wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
+ wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
mappingChanged();
- SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found, maWorldTransform: " << maWorldTransform);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t\tStack index: " << index
+ << " found, maWorldTransform: " << maWorldTransform);
}
}
- void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex)
+ drawinglayer::attribute::LineStartEndAttribute
+ EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
{
- const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
- SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
-
- if (!(pen && polygon.count()))
- return;
-
- // we need a line join attribute
- basegfx::B2DLineJoin lineJoin = basegfx::B2DLineJoin::Round;
- if (pen->penDataFlags & EmfPlusPenDataJoin) // additional line join information
+ const double pw = mdExtractedYScale * aPenWidth;
+ if (aCap == LineCapTypeSquare)
{
- lineJoin = static_cast<basegfx::B2DLineJoin>(EMFPPen::lcl_convertLineJoinType(pen->lineJoin));
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
}
-
- // we need a line cap attribute
- css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT;
- if (pen->penDataFlags & EmfPlusPenDataStartCap) // additional line cap information
+ else if (aCap == LineCapTypeRound)
{
- lineCap = static_cast<css::drawing::LineCap>(EMFPPen::lcl_convertStrokeCap(pen->startCap));
- SAL_WARN_IF(pen->startCap != pen->endCap, "drawinglayer.emf", "emf+ pen uses different start and end cap");
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
+ {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
+ {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
}
-
- const double transformedPenWidth = maMapTransform.get(0, 0) * pen->penWidth;
- drawinglayer::attribute::LineAttribute lineAttribute(pen->GetColor().getBColor(),
- transformedPenWidth,
- lineJoin,
- lineCap);
-
- drawinglayer::attribute::StrokeAttribute aStrokeAttribute;
- if (pen->penDataFlags & EmfPlusPenDataLineStyle && pen->dashStyle != EmfPlusLineStyleCustom) // pen has a predefined line style
+ else if (aCap == LineCapTypeTriangle)
{
- // short writing
- const double pw = maMapTransform.get(1, 1) * pen->penWidth;
- // taken from the old cppcanvas implementation and multiplied with pen width
- const std::vector<double> dash = { 3*pw, 3*pw };
- const std::vector<double> dot = { pw, 3*pw };
- const std::vector<double> dashdot = { 3*pw, 3*pw, pw, 3*pw };
- const std::vector<double> dashdotdot = { 3*pw, 3*pw, pw, 3*pw, pw, 3*pw };
-
- switch (pen->dashStyle)
- {
- case EmfPlusLineStyleSolid: // do nothing special, use default stroke attribute
- break;
- case EmfPlusLineStyleDash:
- aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dash));
- break;
- case EmfPlusLineStyleDot:
- aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dot));
- break;
- case EmfPlusLineStyleDashDot:
- aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dashdot));
- break;
- case EmfPlusLineStyleDashDotDot:
- aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::vector(dashdotdot));
- break;
- }
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
}
- else if (pen->penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line
+ else if (aCap == LineCapTypeSquareAnchor)
{
- // StrokeAttribute needs a double vector while the pen provides a float vector
- std::vector<double> aPattern(pen->dashPattern.size());
- for (size_t i=0; i<aPattern.size(); i++)
- {
- // convert from float to double and multiply with the adjusted pen width
- aPattern[i] = maMapTransform.get(1, 1) * pen->penWidth * pen->dashPattern[i];
- }
- aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(std::move(aPattern));
+ basegfx::B2DPolygon aCapPolygon(
+ { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
}
-
- if (!pen->GetColor().IsTransparent())
+ else if (aCap == LineCapTypeRoundAnchor)
{
- mrTargetHolders.Current().append(
- new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
- polygon,
- lineAttribute,
- aStrokeAttribute));
+ const basegfx::B2DPolygon aCapPolygon
+ = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
}
- else
+ else if (aCap == LineCapTypeDiamondAnchor)
{
- const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
- new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
- polygon,
- lineAttribute,
- aStrokeAttribute));
-
- mrTargetHolders.Current().append(
- new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
- drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
- (255 - pen->GetColor().GetAlpha()) / 255.0));
+ basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
+ {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
+ {-1.0, 0.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
}
-
- if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) && (pen->customStartCap->polygon.begin()->count() > 1))
+ else if (aCap == LineCapTypeArrowAnchor)
{
- SAL_WARN("drawinglayer.emf", "EMF+\tCustom Start Line Cap");
- ::basegfx::B2DPolyPolygon startCapPolygon(pen->customStartCap->polygon);
-
- // get the gradient of the first line in the polypolygon
- double x1 = polygon.begin()->getB2DPoint(0).getX();
- double y1 = polygon.begin()->getB2DPoint(0).getY();
- double x2 = polygon.begin()->getB2DPoint(1).getX();
- double y2 = polygon.begin()->getB2DPoint(1).getY();
-
- if ((x2 - x1) != 0)
- {
- double gradient = (y2 - y1) / (x2 - x1);
-
- // now we get the angle that we need to rotate the arrow by
- double angle = (M_PI / 2) - atan(gradient);
+ basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
+ aCapPolygon.setClosed(true);
+ return drawinglayer::attribute::LineStartEndAttribute(
+ 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
+ }
+ return drawinglayer::attribute::LineStartEndAttribute();
+ }
- // rotate the arrow
- startCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle));
- }
+ void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
+ sal_uInt32 penIndex)
+ {
+ const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
+ SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
- startCapPolygon.transform(maMapTransform);
+ if (!(pen && polygon.count()))
+ return;
- basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(0).getX(),
- 0.0, pen->penWidth, polygon.begin()->getB2DPoint(0).getY());
- startCapPolygon.transform(tran);
+ const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
+ drawinglayer::attribute::LineAttribute lineAttribute(
+ pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
+ css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
+ pen->fMiterMinimumAngle);
- if (pen->customStartCap->mbIsFilled)
- {
- mrTargetHolders.Current().append(
- new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
- startCapPolygon,
- pen->GetColor().getBColor()));
- }
+ drawinglayer::attribute::LineStartEndAttribute aStart;
+ if (pen->penDataFlags & EmfPlusPenDataStartCap)
+ {
+ if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
+ && (pen->customStartCap->polygon.begin()->count() > 1))
+ aStart = drawinglayer::attribute::LineStartEndAttribute(
+ pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
+ * pen->customStartCap->widthScale * pen->penWidth,
+ pen->customStartCap->polygon, false);
else
- {
- mrTargetHolders.Current().append(
- new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
- startCapPolygon,
- lineAttribute,
- aStrokeAttribute));
- }
+ aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
}
- if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) && (pen->customEndCap->polygon.begin()->count() > 1))
+ drawinglayer::attribute::LineStartEndAttribute aEnd;
+ if (pen->penDataFlags & EmfPlusPenDataEndCap)
{
- SAL_WARN("drawinglayer.emf", "EMF+\tCustom End Line Cap");
-
- ::basegfx::B2DPolyPolygon endCapPolygon(pen->customEndCap->polygon);
-
- // get the gradient of the first line in the polypolygon
- double x1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX();
- double y1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY();
- double x2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getX();
- double y2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getY();
+ if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
+ && (pen->customEndCap->polygon.begin()->count() > 1))
+ aEnd = drawinglayer::attribute::LineStartEndAttribute(
+ pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
+ * pen->customEndCap->widthScale * pen->penWidth,
+ pen->customEndCap->polygon, false);
+ else
+ aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
+ }
- if ((x2 - x1) != 0)
+ if (pen->GetColor().IsTransparent())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aContainer;
+ if (aStart.isDefault() && aEnd.isDefault())
+ aContainer.append(
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
+ else
{
- double gradient = (y2 - y1) / (x2 - x1);
-
- // now we get the angle that we need to rotate the arrow by
- double angle = (M_PI / 2) - atan(gradient);
-
- // rotate the arrow
- endCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle));
+ aContainer.resize(polygon.count());
+ for (sal_uInt32 i = 0; i < polygon.count(); i++)
+ aContainer[i] =
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ polygon.getB2DPolygon(i), lineAttribute,
+ pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd);
}
-
- endCapPolygon.transform(maMapTransform);
- basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(),
- 0.0, pen->penWidth, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY());
- endCapPolygon.transform(tran);
-
- if (pen->customEndCap->mbIsFilled)
- {
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
+ }
+ else
+ {
+ if (aStart.isDefault() && aEnd.isDefault())
mrTargetHolders.Current().append(
- new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
- endCapPolygon,
- pen->GetColor().getBColor()));
- }
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
else
- {
- mrTargetHolders.Current().append(
- new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
- endCapPolygon,
- lineAttribute,
- aStrokeAttribute));
- }
+ for (sal_uInt32 i = 0; i < polygon.count(); i++)
+ {
+ mrTargetHolders.Current().append(
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ polygon.getB2DPolygon(i), lineAttribute,
+ pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
+ }
}
-
mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
mrPropertyHolders.Current().setLineColorActive(true);
mrPropertyHolders.Current().setFillColorActive(false);
@@ -735,7 +715,7 @@ namespace emfplushelper
}
else // use Brush
{
- EMFPBrush* brush = static_cast<EMFPBrush*>( maEMFPObjects[brushIndexOrColor & 0xff].get() );
+ EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
// give up in case something wrong happened
@@ -826,23 +806,14 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
// store the blendpoints in the vector
- for (int i = 0; i < brush->blendPoints; i++)
+ for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
{
- double aBlendPoint;
+ const double aBlendPoint = brush->blendPositions[i];
basegfx::BColor aColor;
- if (brush->type == BrushTypeLinearGradient)
- {
- aBlendPoint = brush->blendPositions [i];
- }
- else
- {
- // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius
- aBlendPoint = 2. * ( 1. - brush->blendPositions [i] );
- }
- aColor.setGreen( aStartColor.getGreen() + brush->blendFactors[i] * ( aEndColor.getGreen() - aStartColor.getGreen() ) );
- aColor.setBlue ( aStartColor.getBlue() + brush->blendFactors[i] * ( aEndColor.getBlue() - aStartColor.getBlue() ) );
- aColor.setRed ( aStartColor.getRed() + brush->blendFactors[i] * ( aEndColor.getRed() - aStartColor.getRed() ) );
- const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * ( brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha() );
+ aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
+ aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
+ aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
+ const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
}
}
@@ -851,35 +822,17 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
// store the colorBlends in the vector
- for (int i = 0; i < brush->colorblendPoints; i++)
+ for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
{
- double aBlendPoint;
- basegfx::BColor aColor;
- if (brush->type == BrushTypeLinearGradient)
- {
- aBlendPoint = brush->colorblendPositions [i];
- }
- else
- {
- // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius
- aBlendPoint = 2. * ( 1. - brush->colorblendPositions [i] );
- }
- aColor = brush->colorblendColors[i].getBColor();
- aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0 );
+ const double aBlendPoint = brush->colorblendPositions[i];
+ const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
+ aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
}
}
else // ok, no extra points: just start and end
{
- if (brush->type == BrushTypeLinearGradient)
- {
- aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
- aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
- }
- else // again, here reverse
- {
- aVector.emplace_back(0.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
- aVector.emplace_back(1.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
- }
+ aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
+ aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
}
// get the polygon range to be able to map the start/end/center point correctly
@@ -930,7 +883,7 @@ namespace emfplushelper
aSpreadMethod));
}
else // BrushTypePathGradient
- {
+ { // TODO The PathGradient is not implemented, and Radial Gradient is used instead
basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
aCenterPoint = aPolygonTransformation * aCenterPoint;
@@ -941,9 +894,9 @@ namespace emfplushelper
polygon,
std::move(aVector),
aCenterPoint,
- 0.5, // relative radius
- true, // use UnitCoordinates to stretch the gradient
- drawinglayer::primitive2d::SpreadMethod::Repeat,
+ 0.7, // relative radius little bigger to cover all elements
+ true, // use UnitCoordinates to stretch the gradient
+ drawinglayer::primitive2d::SpreadMethod::Pad,
nullptr));
}
}
@@ -971,6 +924,8 @@ namespace emfplushelper
mnMmY(0),
mbMultipart(false),
mMFlags(0),
+ mdExtractedXScale(1.0),
+ mdExtractedYScale(1.0),
mrTargetHolders(rTargetHolders),
mrPropertyHolders(rPropertyHolders),
bIsGetDCProcessing(false)
@@ -1000,14 +955,8 @@ namespace emfplushelper
}
case EmfPlusCombineModeIntersect:
{
- if (leftPolygon.count())
- {
- aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
- leftPolygon,
- rightPolygon,
- true,
- false);
- }
+ aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ leftPolygon, rightPolygon, true, false);
break;
}
case EmfPlusCombineModeUnion:
@@ -1080,8 +1029,18 @@ namespace emfplushelper
if (bIsGetDCProcessing)
{
- SAL_INFO("drawinglayer.emf", "EMF+\t reset the current clipping region for the world space to infinity.");
- wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
+ if (aGetDCState.getClipPolyPolygonActive())
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved");
+ wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
+ else
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
+ wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
+ mrPropertyHolders);
+ }
bIsGetDCProcessing = false;
}
if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
@@ -1117,14 +1076,15 @@ namespace emfplushelper
{
case EmfPlusRecordTypeHeader:
{
- sal_uInt32 header, version;
+ sal_uInt32 version, emfPlusFlags;
+ SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
- rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
- SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << header);
- SAL_INFO("drawinglayer.emf", "EMF+\tVersion: " << std::dec << version);
+ rMS.ReadUInt32(version).ReadUInt32(emfPlusFlags).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
+ SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << std::hex << version);
+ SAL_INFO("drawinglayer.emf", "EMF+\tEmf+ Flags: 0x" << emfPlusFlags << std::dec);
+ SAL_INFO("drawinglayer.emf", "EMF+\tMetafile was recorded with a reference device context for " << ((emfPlusFlags & 1) ? "video display" : "printer"));
SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
- SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
break;
}
case EmfPlusRecordTypeEndOfFile:
@@ -1160,6 +1120,7 @@ namespace emfplushelper
case EmfPlusRecordTypeGetDC:
{
bIsGetDCProcessing = true;
+ aGetDCState = mrPropertyHolders.Current();
SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
break;
}
@@ -1257,7 +1218,11 @@ namespace emfplushelper
rMS.ReadUInt32(brushIndexOrColor);
SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
- EMFPPlusFillPolygon(static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get())->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
+ EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
+ if (region)
+ EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
+ else
+ SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
}
break;
case EmfPlusRecordTypeDrawEllipse:
@@ -1292,7 +1257,9 @@ namespace emfplushelper
{
// Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
sal_uInt32 brushIndexOrColor = 999;
- sal_Int32 rectangles;
+ ::basegfx::B2DPolyPolygon polyPolygon;
+ sal_uInt32 rectangles;
+ float x, y, width, height;
const bool isColor = (flags & 0x8000);
::basegfx::B2DPolygon polygon;
@@ -1307,11 +1274,9 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
}
- rMS.ReadInt32(rectangles);
-
- for (int i = 0; i < rectangles; i++)
+ rMS.ReadUInt32(rectangles);
+ for (sal_uInt32 i = 0; i < rectangles; i++)
{
- float x, y, width, height;
ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
polygon.clear();
polygon.append(Map(x, y));
@@ -1321,37 +1286,34 @@ namespace emfplushelper
polygon.setClosed(true);
SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
-
- ::basegfx::B2DPolyPolygon polyPolygon(polygon);
- if (type == EmfPlusRecordTypeFillRects)
- EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
- else
- EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
+ polyPolygon.append(polygon);
}
+ if (type == EmfPlusRecordTypeFillRects)
+ EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
break;
}
case EmfPlusRecordTypeFillPolygon:
{
- const sal_uInt8 index = flags & 0xff;
sal_uInt32 brushIndexOrColor, points;
rMS.ReadUInt32(brushIndexOrColor);
rMS.ReadUInt32(points);
- SAL_INFO("drawinglayer.emf", "EMF+\t FillPolygon in slot: " << index << " points: " << points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);
EMFPPath path(points, true);
path.Read(rMS, flags);
EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
-
break;
}
case EmfPlusRecordTypeDrawLines:
{
sal_uInt32 points;
rMS.ReadUInt32(points);
- SAL_INFO("drawinglayer.emf", "EMF+\t DrawLines in slot: " << (flags & 0xff) << " points: " << points);
+ SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
EMFPPath path(points, true);
path.Read(rMS, flags);
@@ -1379,46 +1341,98 @@ namespace emfplushelper
{
sal_uInt32 aCount;
float x1, y1, x2, y2, x3, y3, x4, y4;
- ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint;
::basegfx::B2DPolygon aPolygon;
rMS.ReadUInt32(aCount);
SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
- SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16...");
+ SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
+ "EMF+\t Bezier Draw not support number of points other than 4, 7, "
+ "10, 13, 16...");
if (aCount < 4)
{
- SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount);
+ SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
+ "than 4 points. Number of points: "
+ << aCount);
break;
}
ReadPoint(rMS, x1, y1, flags);
// We need to add first starting point
- aStartPoint = Map(x1, y1);
- aPolygon.append(aStartPoint);
-
+ aPolygon.append(Map(x1, y1));
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Bezier starting point: " << x1 << "," << y1);
for (sal_uInt32 i = 4; i <= aCount; i += 3)
{
ReadPoint(rMS, x2, y2, flags);
ReadPoint(rMS, x3, y3, flags);
ReadPoint(rMS, x4, y4, flags);
- SAL_INFO("drawinglayer.emf", "EMF+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4);
-
- aStartPoint = Map(x1, y1);
- aControlPointA = Map(x2, y2);
- aControlPointB = Map(x3, y3);
- aEndPoint = Map(x4, y4);
-
- ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint);
- cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
+ << y3 << " " << x4 << "," << y4);
+ aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4));
+ }
+ EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
+ break;
+ }
+ case EmfPlusRecordTypeDrawCurve:
+ {
+ sal_uInt32 aOffset, aNumSegments, points;
+ float aTension;
+ rMS.ReadFloat(aTension);
+ rMS.ReadUInt32(aOffset);
+ rMS.ReadUInt32(aNumSegments);
+ rMS.ReadUInt32(points);
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t Tension: " << aTension << " Offset: " << aOffset
+ << " NumSegments: " << aNumSegments
+ << " Points: " << points);
- EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
- // The ending coordinate of one Bezier curve is the starting coordinate of the next.
- x1 = x4;
- y1 = y4;
+ if (points >= 2)
+ EMFPPlusDrawPolygon(
+ path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments),
+ flags & 0xff);
+ else
+ SAL_WARN("drawinglayer.emf", "Not enough number of points");
+ break;
+ }
+ case EmfPlusRecordTypeDrawClosedCurve:
+ case EmfPlusRecordTypeFillClosedCurve:
+ {
+ // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
+ sal_uInt32 brushIndexOrColor = 999, points;
+ float aTension;
+ if (type == EmfPlusRecordTypeFillClosedCurve)
+ {
+ rMS.ReadUInt32(brushIndexOrColor);
+ SAL_INFO(
+ "drawinglayer.emf",
+ "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
}
+ rMS.ReadFloat(aTension);
+ rMS.ReadUInt32(points);
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t Tension: " << aTension << " Points: " << points);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
+ << std::hex << brushIndexOrColor << std::dec);
+ if (points < 3)
+ {
+ SAL_WARN("drawinglayer.emf", "Not enough number of points");
+ break;
+ }
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+ if (type == EmfPlusRecordTypeFillClosedCurve)
+ EMFPPlusFillPolygon(path.GetClosedCardinalSpline(*this, aTension),
+ flags & 0x8000, brushIndexOrColor);
+ else
+ EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension),
+ flags & 0xff);
break;
}
case EmfPlusRecordTypeDrawImage:
@@ -1435,16 +1449,16 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
// Source unit of measurement type must be 1 pixel
- if (sourceUnit == UnitTypePixel && maEMFPObjects[flags & 0xff])
+ if (EMFPImage* image = sourceUnit == UnitTypePixel ?
+ dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
+ nullptr)
{
- EMFPImage& image
- = *static_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get());
float sx, sy, sw, sh;
ReadRectangle(rMS, sx, sy, sw, sh);
::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
SAL_INFO("drawinglayer.emf",
- "EMF+\t "
+ "EMF+\t "
<< (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
: "DrawImagePoints")
<< " source rectangle: " << sx << "," << sy << " " << sw << "x"
@@ -1488,9 +1502,9 @@ namespace emfplushelper
SAL_INFO("drawinglayer.emf",
"EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
Size aSize;
- if (image.type == ImageDataTypeBitmap)
+ if (image->type == ImageDataTypeBitmap)
{
- aSize = image.graphic.GetBitmapEx().GetSizePixel();
+ aSize = image->graphic.GetBitmapEx().GetSizePixel();
SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
<< "x"
<< aSize.Height());
@@ -1522,36 +1536,36 @@ namespace emfplushelper
SAL_INFO(
"drawinglayer.emf",
"EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
- ::basegfx::B2DPoint aDstPoint(dx, dy);
- ::basegfx::B2DSize aDstSize(dw, dh);
+ const ::basegfx::B2DPoint aDstPoint(dx, dy);
+ const ::basegfx::B2DSize aDstSize(dw, dh);
const basegfx::B2DHomMatrix aTransformMatrix
= maMapTransform
* basegfx::B2DHomMatrix(
- /* Row 0, Column 0 */ aDstSize.getX(),
+ /* Row 0, Column 0 */ aDstSize.getWidth(),
/* Row 0, Column 1 */ fShearX,
/* Row 0, Column 2 */ aDstPoint.getX(),
/* Row 1, Column 0 */ fShearY,
- /* Row 1, Column 1 */ aDstSize.getY(),
+ /* Row 1, Column 1 */ aDstSize.getHeight(),
/* Row 1, Column 2 */ aDstPoint.getY());
- if (image.type == ImageDataTypeBitmap)
+ if (image->type == ImageDataTypeBitmap)
{
- BitmapEx aBmp(image.graphic.GetBitmapEx());
+ BitmapEx aBmp(image->graphic.GetBitmapEx());
aBmp.Crop(aSource);
aSize = aBmp.GetSizePixel();
if (aSize.Width() > 0 && aSize.Height() > 0)
{
mrTargetHolders.Current().append(
new drawinglayer::primitive2d::BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(aBmp), aTransformMatrix));
+ aBmp, aTransformMatrix));
}
else
SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
}
- else if (image.type == ImageDataTypeMetafile)
+ else if (image->type == ImageDataTypeMetafile)
{
- GDIMetaFile aGDI(image.graphic.GetGDIMetaFile());
+ GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
aGDI.Clip(aSource);
mrTargetHolders.Current().append(
new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix,
@@ -1586,7 +1600,7 @@ namespace emfplushelper
// get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
// get the font from the flags
- const EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() );
+ const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
if (!font)
{
break;
@@ -1648,7 +1662,7 @@ namespace emfplushelper
}
const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
- ::basegfx::B2DSize(font->emSize, font->emSize),
+ ::basegfx::B2DVector(font->emSize, font->emSize),
::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
ly + stringAlignmentVerticalOffset));
@@ -1690,6 +1704,7 @@ namespace emfplushelper
0, // text always starts at 0
stringLength,
std::move(emptyVector), // EMF-PLUS has no DX-array
+ {},
fontAttribute,
locale,
color.getBColor(), // Font Color
@@ -1709,6 +1724,7 @@ namespace emfplushelper
0, // text always starts at 0
stringLength,
std::move(emptyVector), // EMF-PLUS has no DX-array
+ {},
fontAttribute,
locale,
color.getBColor());
@@ -1739,8 +1755,8 @@ namespace emfplushelper
}
else
{
- mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
- mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
+ mnMmX = std::round(unitToPixel(static_cast<double>(mnMmX) * mfPageScale, flags, Direction::horizontal));
+ mnMmY = std::round(unitToPixel(static_cast<double>(mnMmY) * mfPageScale, flags, Direction::vertical));
mappingChanged();
}
break;
@@ -1815,7 +1831,7 @@ namespace emfplushelper
rMS.ReadUInt32(stackIndex);
SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
- GraphicStatePop(mGSStack, stackIndex, mrPropertyHolders.Current());
+ GraphicStatePop(mGSStack, stackIndex);
break;
}
case EmfPlusRecordTypeBeginContainer:
@@ -1837,12 +1853,12 @@ namespace emfplushelper
SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification.");
break;
}
- const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
- const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
GraphicStatePush(mGSContainerStack, stackIndex);
const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
- aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ),
- aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) );
+ unitToPixel(static_cast<double>(dw) / sw, flags, Direction::horizontal),
+ unitToPixel(static_cast<double>(dh) / sh, flags, Direction::vertical),
+ unitToPixel(static_cast<double>(dx) - sx, flags, Direction::horizontal),
+ unitToPixel(static_cast<double>(dy) - sy, flags, Direction::vertical));
maWorldTransform *= transform;
mappingChanged();
break;
@@ -1862,7 +1878,7 @@ namespace emfplushelper
rMS.ReadUInt32(stackIndex);
SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
- GraphicStatePop(mGSContainerStack, stackIndex, mrPropertyHolders.Current());
+ GraphicStatePop(mGSContainerStack, stackIndex);
break;
}
case EmfPlusRecordTypeSetWorldTransform:
@@ -1997,62 +2013,95 @@ namespace emfplushelper
break;
}
case EmfPlusRecordTypeSetClipRect:
- {
- int combineMode = (flags >> 8) & 0xf;
-
- SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect combine mode: " << combineMode);
-
- float dx, dy, dw, dh;
- ReadRectangle(rMS, dx, dy, dw, dh);
- SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
- ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
- ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
-
- ::basegfx::B2DPolyPolygon polyPolygon(
- ::basegfx::utils::createPolygonFromRect(
- ::basegfx::B2DRectangle(
- mappedPoint1.getX(),
- mappedPoint1.getY(),
- mappedPoint2.getX(),
- mappedPoint2.getY())));
-
- HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
- combineMode, polyPolygon), mrTargetHolders, mrPropertyHolders);
- break;
- }
case EmfPlusRecordTypeSetClipPath:
+ case EmfPlusRecordTypeSetClipRegion:
{
int combineMode = (flags >> 8) & 0xf;
- SAL_INFO("drawinglayer.emf", "EMF+\t SetClipPath combine mode: " << combineMode);
- SAL_INFO("drawinglayer.emf", "EMF+\t Path in slot: " << (flags & 0xff));
-
- EMFPPath *path = static_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
- if (!path)
+ ::basegfx::B2DPolyPolygon polyPolygon;
+ if (type == EmfPlusRecordTypeSetClipRect)
{
- SAL_WARN("drawinglayer.emf", "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
- break;
- }
+ SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect");
- ::basegfx::B2DPolyPolygon& clipPoly(path->GetPolygon(*this));
+ float dx, dy, dw, dh;
+ ReadRectangle(rMS, dx, dy, dw, dh);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
+ ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
+ ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
+
+ polyPolygon
+ = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
+ ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(),
+ mappedPoint2.getX(), mappedPoint2.getY())));
+ }
+ else if (type == EmfPlusRecordTypeSetClipPath)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff));
- HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
- combineMode, clipPoly), mrTargetHolders, mrPropertyHolders);
- break;
- }
- case EmfPlusRecordTypeSetClipRegion:
- {
- int combineMode = (flags >> 8) & 0xf;
- SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
+ EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
+ if (!path)
+ {
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
+ break;
+ }
+ polyPolygon = path->GetPolygon(*this);
+ }
+ else if (type == EmfPlusRecordTypeSetClipRegion)
+ {
+ SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
+ EMFPRegion* region
+ = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
+ if (!region)
+ {
+ SAL_WARN(
+ "drawinglayer.emf",
+ "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
+ break;
+ }
+ polyPolygon = region->regionPolyPolygon;
+ }
SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
- EMFPRegion *region = static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
- if (!region)
+ ::basegfx::B2DPolyPolygon aClippedPolyPolygon;
+ if (mrPropertyHolders.Current().getClipPolyPolygonActive())
{
- SAL_WARN("drawinglayer.emf", "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
- break;
+ aClippedPolyPolygon
+ = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
+ combineMode, polyPolygon);
}
-
- HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
- combineMode, region->regionPolyPolygon), mrTargetHolders, mrPropertyHolders);
+ else
+ {
+ //Combine with infinity
+ switch (combineMode)
+ {
+ case EmfPlusCombineModeReplace:
+ case EmfPlusCombineModeIntersect:
+ {
+ aClippedPolyPolygon = polyPolygon;
+ break;
+ }
+ case EmfPlusCombineModeUnion:
+ {
+ // Disable clipping as the clipping is infinity
+ aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ case EmfPlusCombineModeXOR:
+ case EmfPlusCombineModeComplement:
+ {
+ //TODO It is not correct and it should be fixed
+ aClippedPolyPolygon = polyPolygon;
+ break;
+ }
+ case EmfPlusCombineModeExclude:
+ {
+ //TODO It is not correct and it should be fixed
+ aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
+ break;
+ }
+ }
+ }
+ HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders);
break;
}
case EmfPlusRecordTypeOffsetClip:
@@ -2113,7 +2162,7 @@ namespace emfplushelper
}
// get the font from the flags
- EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() );
+ EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
if (!font)
{
break;
@@ -2154,7 +2203,7 @@ namespace emfplushelper
aDXArray.push_back(0);
basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
- ::basegfx::B2DSize(font->emSize, font->emSize),
+ ::basegfx::B2DVector(font->emSize, font->emSize),
::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
if (hasMatrix)
transformMatrix *= transform;
@@ -2169,6 +2218,7 @@ namespace emfplushelper
pos, // take character at current pos
aLength, // use determined length
std::move(aDXArray), // generated DXArray
+ {},
fontAttribute,
Application::GetSettings().GetLanguageTag().getLocale(),
color.getBColor(),
@@ -2188,6 +2238,7 @@ namespace emfplushelper
pos, // take character at current pos
aLength, // use determined length
std::move(aDXArray), // generated DXArray
+ {},
fontAttribute,
Application::GetSettings().GetLanguageTag().getLocale(),
color.getBColor());
diff --git a/drawinglayer/source/tools/emfphelperdata.hxx b/drawinglayer/source/tools/emfphelperdata.hxx
index cf3474b5b1a7..d2ed1008b67b 100644
--- a/drawinglayer/source/tools/emfphelperdata.hxx
+++ b/drawinglayer/source/tools/emfphelperdata.hxx
@@ -21,6 +21,7 @@
#include <wmfemfhelper.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/linestartendattribute.hxx>
#include <tools/stream.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <map>
@@ -53,9 +54,9 @@ namespace emfplushelper
#define EmfPlusRecordTypeFillRegion 0x4013
#define EmfPlusRecordTypeFillPath 0x4014
#define EmfPlusRecordTypeDrawPath 0x4015
- //TODO EmfPlusRecordTypeFillClosedCurve 0x4016
- //TODO EmfPlusRecordTypeDrawClosedCurve 0x4017
- //TODO EmfPlusRecordTypeDrawCurve 0x4018
+ #define EmfPlusRecordTypeFillClosedCurve 0x4016
+ #define EmfPlusRecordTypeDrawClosedCurve 0x4017
+ #define EmfPlusRecordTypeDrawCurve 0x4018
#define EmfPlusRecordTypeDrawBeziers 0x4019
#define EmfPlusRecordTypeDrawImage 0x401A
#define EmfPlusRecordTypeDrawImagePoints 0x401B
@@ -210,9 +211,15 @@ namespace emfplushelper
GraphicStateMap mGSStack;
GraphicStateMap mGSContainerStack;
+ /* Performance optimizators */
+ /* Extracted Scale values from Transformation Matrix */
+ double mdExtractedXScale;
+ double mdExtractedYScale;
+
/// data holders
wmfemfhelper::TargetHolders& mrTargetHolders;
wmfemfhelper::PropertyHolders& mrPropertyHolders;
+ wmfemfhelper::PropertyHolder aGetDCState;
bool bIsGetDCProcessing;
// readers
@@ -224,7 +231,10 @@ namespace emfplushelper
// stack actions
void GraphicStatePush(GraphicStateMap& map, sal_Int32 index);
- void GraphicStatePop (GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState);
+ void GraphicStatePop(GraphicStateMap& map, sal_Int32 index);
+
+ drawinglayer::attribute::LineStartEndAttribute CreateLineEnd(const sal_Int32 aCap,
+ const float aPenWidth) const;
// primitive creators
void EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex);
@@ -234,6 +244,14 @@ namespace emfplushelper
// helper functions
Color EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const;
+ enum class Direction
+ {
+ horizontal,
+ vertical
+ };
+ sal_uInt32 DPI(Direction d) { return d == Direction::horizontal ? mnHDPI : mnVDPI; }
+ double unitToPixel(double n, sal_uInt32 aUnitType, Direction d);
+
public:
EmfPlusHelperData(
SvMemoryStream& rMS,
@@ -252,8 +270,6 @@ namespace emfplushelper
static void ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed = false);
static bool readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget);
static ::basegfx::B2DPolyPolygon combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon);
-
- static float getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI);
};
}
diff --git a/drawinglayer/source/tools/emfpimage.cxx b/drawinglayer/source/tools/emfpimage.cxx
index a3300168e7f6..67a0cef99ed2 100644
--- a/drawinglayer/source/tools/emfpimage.cxx
+++ b/drawinglayer/source/tools/emfpimage.cxx
@@ -38,7 +38,7 @@ namespace emfplushelper
{
// non native formats
GraphicFilter filter;
- filter.ImportGraphic(graphic, OUString(), s);
+ filter.ImportGraphic(graphic, u"", s);
SAL_INFO("drawinglayer.emf", "EMF+\tbitmap width: " << graphic.GetSizePixel().Width() << " height: " << graphic.GetSizePixel().Height());
}
}
@@ -58,7 +58,7 @@ namespace emfplushelper
GraphicFilter filter;
// workaround buggy metafiles, which have wrong mfSize set (n#705956 for example)
SvMemoryStream mfStream(const_cast<char *>(static_cast<char const *>(s.GetData()) + s.Tell()), dataSize, StreamMode::READ);
- filter.ImportGraphic(graphic, OUString(), mfStream);
+ filter.ImportGraphic(graphic, u"", mfStream);
// debug code - write the stream to debug file /tmp/emf-stream.emf
#if OSL_DEBUG_LEVEL > 1
diff --git a/drawinglayer/source/tools/emfpimageattributes.cxx b/drawinglayer/source/tools/emfpimageattributes.cxx
index c13da361bf1a..11c1f47173e4 100644
--- a/drawinglayer/source/tools/emfpimageattributes.cxx
+++ b/drawinglayer/source/tools/emfpimageattributes.cxx
@@ -22,7 +22,6 @@
#include "emfpimageattributes.hxx"
using namespace ::com::sun::star;
-using namespace ::basegfx;
namespace emfplushelper
{
diff --git a/drawinglayer/source/tools/emfppath.cxx b/drawinglayer/source/tools/emfppath.cxx
index 4da379004fba..e7c4a5512c76 100644
--- a/drawinglayer/source/tools/emfppath.cxx
+++ b/drawinglayer/source/tools/emfppath.cxx
@@ -20,7 +20,6 @@
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
-#include <o3tl/safeint.hxx>
#include <sal/log.hxx>
#include "emfppath.hxx"
@@ -35,6 +34,13 @@ namespace
namespace emfplushelper
{
+ typedef double matrix [4][4];
+
+ constexpr sal_uInt32 nDetails = 8;
+ constexpr double alpha[nDetails]
+ = { 1. / nDetails, 2. / nDetails, 3. / nDetails, 4. / nDetails,
+ 5. / nDetails, 6. / nDetails, 7. / nDetails, 8. / nDetails };
+
// see 2.2.2.21 EmfPlusInteger7
// 2.2.2.22 EmfPlusInteger15
// and 2.2.2.37 EmfPlusPointR Object
@@ -66,7 +72,6 @@ namespace emfplushelper
}
nPoints = _nPoints;
- pPoints.reset( new float [nPoints*2] );
if (!bLines)
pPointTypes.reset( new sal_uInt8 [_nPoints] );
@@ -78,7 +83,8 @@ namespace emfplushelper
void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags)
{
- for (sal_uInt32 i = 0; i < nPoints; i ++)
+ float fx, fy;
+ for (sal_uInt32 i = 0; i < nPoints; i++)
{
if (pathFlags & 0x800)
{
@@ -87,8 +93,8 @@ namespace emfplushelper
// If 0x800 bit is set, the 0x4000 bit is undefined and must be ignored
sal_Int32 x = GetEmfPlusInteger(s);
sal_Int32 y = GetEmfPlusInteger(s);
- pPoints [i*2] = x;
- pPoints [i*2 + 1] = y;
+ xPoints.push_back(x);
+ yPoints.push_back(y);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPointR [x,y]: " << x << ", " << y);
}
else if (pathFlags & 0x4000)
@@ -96,16 +102,18 @@ namespace emfplushelper
// EMFPlusPoint: stored in signed short 16bit integer format
sal_Int16 x, y;
- s.ReadInt16( x ).ReadInt16( y );
- SAL_INFO ("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y);
- pPoints [i*2] = x;
- pPoints [i*2 + 1] = y;
+ s.ReadInt16(x).ReadInt16(y);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y);
+ xPoints.push_back(x);
+ yPoints.push_back(y);
}
else
{
// EMFPlusPointF: stored in Single (float) format
- s.ReadFloat( pPoints [i*2] ).ReadFloat( pPoints [i*2 + 1] );
- SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << pPoints [i * 2] << ", " << pPoints [i * 2 + 1]);
+ s.ReadFloat(fx).ReadFloat(fy);
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << fx << ", " << fy);
+ xPoints.push_back(fx);
+ yPoints.push_back(fy);
}
}
@@ -129,7 +137,7 @@ namespace emfplushelper
::basegfx::B2DPoint prev, mapped;
bool hasPrev = false;
- for (sal_uInt32 i = 0; i < nPoints; i ++)
+ for (sal_uInt32 i = 0; i < nPoints; i++)
{
if (p && pPointTypes && (pPointTypes [i] == 0))
{
@@ -140,9 +148,9 @@ namespace emfplushelper
}
if (bMapIt)
- mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]);
+ mapped = rR.Map(xPoints[i], yPoints [i]);
else
- mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]);
+ mapped = ::basegfx::B2DPoint(xPoints[i], yPoints[i]);
if (pPointTypes)
{
@@ -168,7 +176,7 @@ namespace emfplushelper
}
polygon.append (mapped);
- SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << xPoints[i] << "," << yPoints[i] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
if (hasPrev)
{
@@ -222,6 +230,91 @@ namespace emfplushelper
return aPolygon;
}
+
+ static void GetCardinalMatrix(float tension, matrix& m)
+ {
+ m[0][1] = 2. - tension;
+ m[0][2] = tension - 2.;
+ m[1][0] = 2. * tension;
+ m[1][1] = tension - 3.;
+ m[1][2] = 3. - 2. * tension;
+ m[3][1] = 1.;
+ m[0][3] = m[2][2] = tension;
+ m[0][0] = m[1][3] = m[2][0] = -tension;
+ m[2][1] = m[2][3] = m[3][0] = m[3][2] = m[3][3] = 0.;
+ }
+
+ static double calculateSplineCoefficients(float p0, float p1, float p2, float p3, sal_uInt32 step, matrix m)
+ {
+ double a = m[0][0] * p0 + m[0][1] * p1 + m[0][2] * p2 + m[0][3] * p3;
+ double b = m[1][0] * p0 + m[1][1] * p1 + m[1][2] * p2 + m[1][3] * p3;
+ double c = m[2][0] * p0 + m[2][2] * p2;
+ double d = p1;
+ return (d + alpha[step] * (c + alpha[step] * (b + alpha[step] * a)));
+ }
+
+ ::basegfx::B2DPolyPolygon& EMFPPath::GetCardinalSpline(EmfPlusHelperData const& rR, float fTension,
+ sal_uInt32 aOffset, sal_uInt32 aNumSegments)
+ {
+ ::basegfx::B2DPolygon polygon;
+ matrix mat;
+ double x, y;
+ if (aNumSegments >= nPoints)
+ aNumSegments = nPoints - 1;
+ GetCardinalMatrix(fTension, mat);
+ // duplicate first point
+ xPoints.push_front(xPoints.front());
+ yPoints.push_front(yPoints.front());
+ // duplicate last point
+ xPoints.push_back(xPoints.back());
+ yPoints.push_back(yPoints.back());
+
+ for (sal_uInt32 i = 3 + aOffset; i < aNumSegments + 3; i++)
+ {
+ for (sal_uInt32 s = 0; s < nDetails; s++)
+ {
+ x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1],
+ xPoints[i], s, mat);
+ y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1],
+ yPoints[i], s, mat);
+ polygon.append(rR.Map(x, y));
+ }
+ }
+ if (polygon.count())
+ aPolygon.append(polygon);
+ return aPolygon;
+ }
+
+ ::basegfx::B2DPolyPolygon& EMFPPath::GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension)
+ {
+ ::basegfx::B2DPolygon polygon;
+ matrix mat;
+ double x, y;
+ GetCardinalMatrix(fTension, mat);
+ // add three first points at the end
+ xPoints.push_back(xPoints[0]);
+ yPoints.push_back(yPoints[0]);
+ xPoints.push_back(xPoints[1]);
+ yPoints.push_back(yPoints[1]);
+ xPoints.push_back(xPoints[2]);
+ yPoints.push_back(yPoints[2]);
+
+ for (sal_uInt32 i = 3; i < nPoints + 3; i++)
+ {
+ for (sal_uInt32 s = 0; s < nDetails; s++)
+ {
+ x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1],
+ xPoints[i], s, mat);
+ y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1],
+ yPoints[i], s, mat);
+ polygon.append(rR.Map(x, y));
+ }
+ }
+ polygon.setClosed(true);
+ if (polygon.count())
+ aPolygon.append(polygon);
+ return aPolygon;
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppath.hxx b/drawinglayer/source/tools/emfppath.hxx
index 8db095a21a69..e6104fcb1fc7 100644
--- a/drawinglayer/source/tools/emfppath.hxx
+++ b/drawinglayer/source/tools/emfppath.hxx
@@ -23,13 +23,14 @@
namespace emfplushelper
{
- struct EMFPPath : public EMFPObject
+ class EMFPPath : public EMFPObject
{
::basegfx::B2DPolyPolygon aPolygon;
sal_uInt32 nPoints;
- std::unique_ptr<float[]> pPoints;
+ std::deque<float> xPoints, yPoints;
std::unique_ptr<sal_uInt8[]> pPointTypes;
+ public:
EMFPPath(sal_uInt32 _nPoints, bool bLines = false);
virtual ~EMFPPath() override;
@@ -37,6 +38,9 @@ namespace emfplushelper
void Read(SvStream& s, sal_uInt32 pathFlags);
::basegfx::B2DPolyPolygon& GetPolygon(EmfPlusHelperData const & rR, bool bMapIt = true, bool bAddLineToCloseShape = false);
+ ::basegfx::B2DPolyPolygon& GetCardinalSpline(EmfPlusHelperData const& rR, float fTension,
+ sal_uInt32 aOffset, sal_uInt32 aNumSegments);
+ ::basegfx::B2DPolyPolygon& GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension);
};
}
diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx
index d41dca676d4e..adfee3bd3706 100644
--- a/drawinglayer/source/tools/emfppen.cxx
+++ b/drawinglayer/source/tools/emfppen.cxx
@@ -18,7 +18,6 @@
*/
#include <com/sun/star/rendering/PathCapType.hpp>
-#include <com/sun/star/rendering/PathJoinType.hpp>
#include <o3tl/safeint.hxx>
#include <sal/log.hxx>
#include <rtl/ustrbuf.hxx>
@@ -31,26 +30,6 @@ using namespace ::basegfx;
namespace emfplushelper
{
- namespace {
-
- enum EmfPlusPenData
- {
- PenDataTransform = 0x00000001,
- PenDataStartCap = 0x00000002,
- PenDataEndCap = 0x00000004,
- PenDataJoin = 0x00000008,
- PenDataMiterLimit = 0x00000010,
- PenDataLineStyle = 0x00000020,
- PenDataDashedLineCap = 0x00000040,
- PenDataDashedLineOffset = 0x00000080,
- PenDataDashedLine = 0x00000100,
- PenDataAlignment = 0x00000200,
- PenDataCompoundLine = 0x00000400,
- PenDataCustomStartCap = 0x00000800,
- PenDataCustomEndCap = 0x00001000
- };
-
- }
EMFPPen::EMFPPen()
: penDataFlags(0)
@@ -58,8 +37,8 @@ namespace emfplushelper
, penWidth(0.0)
, startCap(0)
, endCap(0)
- , lineJoin(0)
- , miterLimit(0.0)
+ , maLineJoin(basegfx::B2DLineJoin::Miter)
+ , fMiterMinimumAngle(basegfx::deg2rad(5.0))
, dashStyle(0)
, dashCap(0)
, dashOffset(0.0)
@@ -138,18 +117,6 @@ namespace emfplushelper
return "";
}
- static OUString LineJoinTypeToString(sal_uInt32 jointype)
- {
- switch (jointype)
- {
- case LineJoinTypeMiter: return "LineJoinTypeMiter";
- case LineJoinTypeBevel: return "LineJoinTypeBevel";
- case LineJoinTypeRound: return "LineJoinTypeRound";
- case LineJoinTypeMiterClipped: return "LineJoinTypeMiterClipped";
- }
- return "";
- }
-
static OUString DashedLineCapTypeToString(sal_uInt32 dashedlinecaptype)
{
switch (dashedlinecaptype)
@@ -174,38 +141,49 @@ namespace emfplushelper
return "";
}
- /// Convert stroke caps between EMF+ and rendering API
- sal_Int8 EMFPPen::lcl_convertStrokeCap(sal_uInt32 nEmfStroke)
+ drawinglayer::attribute::StrokeAttribute
+ EMFPPen::GetStrokeAttribute(const double aTransformation) const
{
- switch (nEmfStroke)
+ if (penDataFlags & EmfPlusPenDataLineStyle // pen has a predefined line style
+ && dashStyle != EmfPlusLineStyleCustom)
{
- case EmfPlusLineCapTypeSquare: return rendering::PathCapType::SQUARE;
- case EmfPlusLineCapTypeRound: return rendering::PathCapType::ROUND;
+ const double pw = aTransformation * penWidth;
+ switch (dashStyle)
+ {
+ case EmfPlusLineStyleDash:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw });
+ case EmfPlusLineStyleDot:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ pw, pw });
+ case EmfPlusLineStyleDashDot:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw });
+ case EmfPlusLineStyleDashDotDot:
+ // [-loplugin:redundantfcast] false positive:
+ return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw, pw, pw });
+ }
}
-
- // we have no mapping for EmfPlusLineCapTypeTriangle = 0x00000003,
- // so return BUTT always
- return rendering::PathCapType::BUTT;
- }
-
- sal_Int8 EMFPPen::lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin)
- {
- switch (nEmfLineJoin)
+ else if (penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line
{
- case EmfPlusLineJoinTypeMiter: // fall-through
- case EmfPlusLineJoinTypeMiterClipped: return rendering::PathJoinType::MITER;
- case EmfPlusLineJoinTypeBevel: return rendering::PathJoinType::BEVEL;
- case EmfPlusLineJoinTypeRound: return rendering::PathJoinType::ROUND;
+ const double pw = aTransformation * penWidth;
+ // StrokeAttribute needs a double vector while the pen provides a float vector
+ std::vector<double> aPattern(dashPattern.size());
+ for (size_t i = 0; i < aPattern.size(); i++)
+ {
+ // convert from float to double and multiply with the adjusted pen width
+ aPattern[i] = pw * dashPattern[i];
+ }
+ return drawinglayer::attribute::StrokeAttribute(std::move(aPattern));
}
-
- assert(false); // Line Join type isn't in specification.
- return 0;
+ // EmfPlusLineStyleSolid: - do nothing special, use default stroke attribute
+ return drawinglayer::attribute::StrokeAttribute();
}
void EMFPPen::Read(SvStream& s, EmfPlusHelperData const & rR)
{
+ sal_Int32 lineJoin = EmfPlusLineJoinTypeMiter;
sal_uInt32 graphicsVersion, penType;
- int i;
s.ReadUInt32(graphicsVersion).ReadUInt32(penType).ReadUInt32(penDataFlags).ReadUInt32(penUnit).ReadFloat(penWidth);
SAL_INFO("drawinglayer.emf", "EMF+\t\tGraphics version: 0x" << std::hex << graphicsVersion);
SAL_INFO("drawinglayer.emf", "EMF+\t\tType: " << penType);
@@ -220,13 +198,13 @@ namespace emfplushelper
: 0.05f; // 0.05f is taken from old EMF+ implementation (case of Unit == Pixel etc.)
}
- if (penDataFlags & PenDataTransform)
+ if (penDataFlags & EmfPlusPenDataTransform)
{
EmfPlusHelperData::readXForm(s, pen_transformation);
SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataTransform: " << pen_transformation);
}
- if (penDataFlags & PenDataStartCap)
+ if (penDataFlags & EmfPlusPenDataStartCap)
{
s.ReadInt32(startCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tstartCap: " << LineCapTypeToString(startCap) << " (0x" << std::hex << startCap << ")");
@@ -236,7 +214,7 @@ namespace emfplushelper
startCap = 0;
}
- if (penDataFlags & PenDataEndCap)
+ if (penDataFlags & EmfPlusPenDataEndCap)
{
s.ReadInt32(endCap);
SAL_INFO("drawinglayer.emf", "EMF+\t\tendCap: " << LineCapTypeToString(endCap) << " (0x" << std::hex << startCap << ")");
@@ -246,27 +224,52 @@ namespace emfplushelper
endCap = 0;
}
- if (penDataFlags & PenDataJoin)
+ if (penDataFlags & EmfPlusPenDataJoin)
{
s.ReadInt32(lineJoin);
- SAL_WARN("drawinglayer.emf", "EMF+\t\tTODO PenDataJoin: " << LineJoinTypeToString(lineJoin) << " (0x" << std::hex << lineJoin << ")");
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t LineJoin: " << lineJoin);
+ switch (lineJoin)
+ {
+ case EmfPlusLineJoinTypeBevel:
+ maLineJoin = basegfx::B2DLineJoin::Bevel;
+ break;
+ case EmfPlusLineJoinTypeRound:
+ maLineJoin = basegfx::B2DLineJoin::Round;
+ break;
+ case EmfPlusLineJoinTypeMiter:
+ case EmfPlusLineJoinTypeMiterClipped:
+ default: // If nothing set, then apply Miter (based on MS Paint)
+ maLineJoin = basegfx::B2DLineJoin::Miter;
+ break;
+ }
}
else
- {
- lineJoin = 0;
- }
+ maLineJoin = basegfx::B2DLineJoin::Miter;
- if (penDataFlags & PenDataMiterLimit)
+ if (penDataFlags & EmfPlusPenDataMiterLimit)
{
+ float miterLimit;
s.ReadFloat(miterLimit);
- SAL_WARN("drawinglayer.emf", "EMF+\t\tTODO PenDataMiterLimit: " << std::dec << miterLimit);
+
+ // EMF+ JoinTypeMiterClipped is working as our B2DLineJoin::Miter
+ // For EMF+ LineJoinTypeMiter we are simulating it by changing angle
+ if (lineJoin == EmfPlusLineJoinTypeMiter)
+ miterLimit = 3.0 * miterLimit;
+ // asin angle must be in range [-1, 1]
+ if (abs(miterLimit) > 1.0)
+ fMiterMinimumAngle = 2.0 * asin(1.0 / miterLimit);
+ else
+ // enable miter limit for all angles
+ fMiterMinimumAngle = basegfx::deg2rad(180.0);
+ SAL_INFO("drawinglayer.emf",
+ "EMF+\t\t MiterLimit: " << std::dec << miterLimit
+ << ", Miter minimum angle (rad): " << fMiterMinimumAngle);
}
else
- {
- miterLimit = 0;
- }
+ fMiterMinimumAngle = basegfx::deg2rad(5.0);
+
- if (penDataFlags & PenDataLineStyle)
+ if (penDataFlags & EmfPlusPenDataLineStyle)
{
s.ReadInt32(dashStyle);
SAL_INFO("drawinglayer.emf", "EMF+\t\tdashStyle: " << DashedLineCapTypeToString(dashStyle) << " (0x" << std::hex << dashStyle << ")");
@@ -276,7 +279,7 @@ namespace emfplushelper
dashStyle = 0;
}
- if (penDataFlags & PenDataDashedLineCap)
+ if (penDataFlags & EmfPlusPenDataDashedLineCap)
{
s.ReadInt32(dashCap);
SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineCap: 0x" << std::hex << dashCap);
@@ -286,7 +289,7 @@ namespace emfplushelper
dashCap = 0;
}
- if (penDataFlags & PenDataDashedLineOffset)
+ if (penDataFlags & EmfPlusPenDataDashedLineOffset)
{
s.ReadFloat(dashOffset);
SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineOffset: 0x" << std::hex << dashOffset);
@@ -296,29 +299,24 @@ namespace emfplushelper
dashOffset = 0;
}
- if (penDataFlags & PenDataDashedLine)
+ if (penDataFlags & EmfPlusPenDataDashedLine)
{
dashStyle = EmfPlusLineStyleCustom;
- sal_Int32 dashPatternLen;
+ sal_uInt32 dashPatternLen;
- s.ReadInt32(dashPatternLen);
+ s.ReadUInt32(dashPatternLen);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\tdashPatternLen: " << dashPatternLen);
- if (dashPatternLen<0 || o3tl::make_unsigned(dashPatternLen)>SAL_MAX_INT32 / sizeof(float))
- {
- dashPatternLen = SAL_MAX_INT32 / sizeof(float);
- }
-
dashPattern.resize( dashPatternLen );
- for (i = 0; i < dashPatternLen; i++)
+ for (sal_uInt32 i = 0; i < dashPatternLen; i++)
{
s.ReadFloat(dashPattern[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tdashPattern[" << i << "]: " << dashPattern[i]);
}
}
- if (penDataFlags & PenDataAlignment)
+ if (penDataFlags & EmfPlusPenDataAlignment)
{
s.ReadInt32(alignment);
SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataAlignment: " << PenAlignmentToString(alignment) << " (0x" << std::hex << alignment << ")");
@@ -328,29 +326,24 @@ namespace emfplushelper
alignment = 0;
}
- if (penDataFlags & PenDataCompoundLine)
+ if (penDataFlags & EmfPlusPenDataCompoundLine)
{
SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataCompoundLine");
- sal_Int32 compoundArrayLen;
- s.ReadInt32(compoundArrayLen);
-
- if (compoundArrayLen<0 || o3tl::make_unsigned(compoundArrayLen)>SAL_MAX_INT32 / sizeof(float))
- {
- compoundArrayLen = SAL_MAX_INT32 / sizeof(float);
- }
+ sal_uInt32 compoundArrayLen;
+ s.ReadUInt32(compoundArrayLen);
compoundArray.resize(compoundArrayLen);
- for (i = 0; i < compoundArrayLen; i++)
+ for (sal_uInt32 i = 0; i < compoundArrayLen; i++)
{
s.ReadFloat(compoundArray[i]);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tcompoundArray[" << i << "]: " << compoundArray[i]);
}
}
- if (penDataFlags & PenDataCustomStartCap)
+ if (penDataFlags & EmfPlusPenDataCustomStartCap)
{
- s.ReadInt32(customStartCapLen);
+ s.ReadUInt32(customStartCapLen);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomStartCapLen: " << customStartCapLen);
sal_uInt64 const pos = s.Tell();
@@ -365,9 +358,9 @@ namespace emfplushelper
customStartCapLen = 0;
}
- if (penDataFlags & PenDataCustomEndCap)
+ if (penDataFlags & EmfPlusPenDataCustomEndCap)
{
- s.ReadInt32(customEndCapLen);
+ s.ReadUInt32(customEndCapLen);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomEndCapLen: " << customEndCapLen);
sal_uInt64 const pos = s.Tell();
diff --git a/drawinglayer/source/tools/emfppen.hxx b/drawinglayer/source/tools/emfppen.hxx
index 05b2fc376d7d..31812c8b0c0e 100644
--- a/drawinglayer/source/tools/emfppen.hxx
+++ b/drawinglayer/source/tools/emfppen.hxx
@@ -19,6 +19,7 @@
#pragma once
+#include <drawinglayer/attribute/strokeattribute.hxx>
#include "emfpbrush.hxx"
#include <vector>
@@ -26,6 +27,7 @@ namespace emfplushelper
{
const sal_uInt32 EmfPlusLineCapTypeSquare = 0x00000001;
const sal_uInt32 EmfPlusLineCapTypeRound = 0x00000002;
+ const sal_uInt32 EmfPlusLineCapTypeTriangle = 0x00000003;
const sal_uInt32 EmfPlusLineJoinTypeMiter = 0x00000000;
const sal_uInt32 EmfPlusLineJoinTypeBevel = 0x00000001;
@@ -102,17 +104,17 @@ namespace emfplushelper
float penWidth;
sal_Int32 startCap;
sal_Int32 endCap;
- sal_Int32 lineJoin;
- float miterLimit;
+ basegfx::B2DLineJoin maLineJoin;
+ double fMiterMinimumAngle;
sal_Int32 dashStyle;
sal_Int32 dashCap;
float dashOffset;
std::vector<float> dashPattern;
sal_Int32 alignment;
std::vector<float> compoundArray;
- sal_Int32 customStartCapLen;
+ sal_uInt32 customStartCapLen;
std::unique_ptr<EMFPCustomLineCap> customStartCap;
- sal_Int32 customEndCapLen;
+ sal_uInt32 customEndCapLen;
std::unique_ptr<EMFPCustomLineCap> customEndCap;
EMFPPen();
@@ -121,8 +123,7 @@ namespace emfplushelper
void Read(SvStream& s, EmfPlusHelperData const & rR);
- static sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke);
- static sal_Int8 lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin);
+ drawinglayer::attribute::StrokeAttribute GetStrokeAttribute(const double aTransformation) const;
};
}
diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx
index 2e2bb887a3d4..5658df3312d9 100644
--- a/drawinglayer/source/tools/primitive2dxmldump.cxx
+++ b/drawinglayer/source/tools/primitive2dxmldump.cxx
@@ -13,7 +13,9 @@
#include <tools/stream.hxx>
#include <tools/XmlWriter.hxx>
+#include <math.h>
#include <memory>
+#include <libxml/parser.h>
#include <sal/log.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
@@ -21,25 +23,32 @@
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/Tools.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
#include <primitive2d/textlineprimitive2d.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <drawinglayer/attribute/lineattribute.hxx>
#include <drawinglayer/attribute/fontattribute.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/utils/gradienttools.hxx>
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
#include <toolkit/helper/vclunohelper.hxx>
@@ -49,7 +58,6 @@
#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx>
#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
#include <drawinglayer/attribute/sdrfillattribute.hxx>
-#include <drawinglayer/attribute/fillgraphicattribute.hxx>
#include <drawinglayer/attribute/fillhatchattribute.hxx>
#include <drawinglayer/attribute/fillgradientattribute.hxx>
#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
@@ -77,9 +85,9 @@ void writeMatrix(::tools::XmlWriter& rWriter, const basegfx::B2DHomMatrix& rMatr
rWriter.attribute("xy21", rMatrix.get(1, 0));
rWriter.attribute("xy22", rMatrix.get(1, 1));
rWriter.attribute("xy23", rMatrix.get(1, 2));
- rWriter.attribute("xy31", rMatrix.get(2, 0));
- rWriter.attribute("xy32", rMatrix.get(2, 1));
- rWriter.attribute("xy33", rMatrix.get(2, 2));
+ rWriter.attribute("xy31", 0);
+ rWriter.attribute("xy32", 0);
+ rWriter.attribute("xy33", 1);
}
void writeMatrix3D(::tools::XmlWriter& rWriter, const basegfx::B3DHomMatrix& rMatrix)
@@ -132,6 +140,24 @@ void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon
rWriter.endElement();
}
+void writeStrokeAttribute(::tools::XmlWriter& rWriter,
+ const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute)
+{
+ if (!rStrokeAttribute.getDotDashArray().empty())
+ {
+ rWriter.startElement("stroke");
+
+ OUString sDotDash;
+ for (double fDotDash : rStrokeAttribute.getDotDashArray())
+ {
+ sDotDash += OUString::number(lround(fDotDash)) + " ";
+ }
+ rWriter.attribute("dotDashArray", sDotDash);
+ rWriter.attribute("fullDotDashLength", rStrokeAttribute.getFullDotDashLen());
+ rWriter.endElement();
+ }
+}
+
void writeLineAttribute(::tools::XmlWriter& rWriter,
const drawinglayer::attribute::LineAttribute& rLineAttribute)
{
@@ -141,36 +167,41 @@ void writeLineAttribute(::tools::XmlWriter& rWriter,
switch (rLineAttribute.getLineJoin())
{
case basegfx::B2DLineJoin::NONE:
- rWriter.attribute("linejoin", "NONE");
+ rWriter.attribute("linejoin", "NONE"_ostr);
break;
case basegfx::B2DLineJoin::Bevel:
- rWriter.attribute("linejoin", "Bevel");
+ rWriter.attribute("linejoin", "Bevel"_ostr);
break;
case basegfx::B2DLineJoin::Miter:
- rWriter.attribute("linejoin", "Miter");
+ {
+ rWriter.attribute("linejoin", "Miter"_ostr);
+ rWriter.attribute("miterangle",
+ basegfx::rad2deg(rLineAttribute.getMiterMinimumAngle()));
break;
+ }
case basegfx::B2DLineJoin::Round:
- rWriter.attribute("linejoin", "Round");
+ rWriter.attribute("linejoin", "Round"_ostr);
break;
default:
- rWriter.attribute("linejoin", "Unknown");
+ rWriter.attribute("linejoin", "Unknown"_ostr);
break;
}
switch (rLineAttribute.getLineCap())
{
case css::drawing::LineCap::LineCap_BUTT:
- rWriter.attribute("linecap", "BUTT");
+ rWriter.attribute("linecap", "BUTT"_ostr);
break;
case css::drawing::LineCap::LineCap_ROUND:
- rWriter.attribute("linecap", "ROUND");
+ rWriter.attribute("linecap", "ROUND"_ostr);
break;
case css::drawing::LineCap::LineCap_SQUARE:
- rWriter.attribute("linecap", "SQUARE");
+ rWriter.attribute("linecap", "SQUARE"_ostr);
break;
default:
- rWriter.attribute("linecap", "Unknown");
+ rWriter.attribute("linecap", "Unknown"_ostr);
break;
}
+
rWriter.endElement();
}
@@ -188,34 +219,34 @@ void writeSdrLineAttribute(::tools::XmlWriter& rWriter,
switch (rLineAttribute.getJoin())
{
case basegfx::B2DLineJoin::NONE:
- rWriter.attribute("linejoin", "NONE");
+ rWriter.attribute("linejoin", "NONE"_ostr);
break;
case basegfx::B2DLineJoin::Bevel:
- rWriter.attribute("linejoin", "Bevel");
+ rWriter.attribute("linejoin", "Bevel"_ostr);
break;
case basegfx::B2DLineJoin::Miter:
- rWriter.attribute("linejoin", "Miter");
+ rWriter.attribute("linejoin", "Miter"_ostr);
break;
case basegfx::B2DLineJoin::Round:
- rWriter.attribute("linejoin", "Round");
+ rWriter.attribute("linejoin", "Round"_ostr);
break;
default:
- rWriter.attribute("linejoin", "Unknown");
+ rWriter.attribute("linejoin", "Unknown"_ostr);
break;
}
switch (rLineAttribute.getCap())
{
case css::drawing::LineCap::LineCap_BUTT:
- rWriter.attribute("linecap", "BUTT");
+ rWriter.attribute("linecap", "BUTT"_ostr);
break;
case css::drawing::LineCap::LineCap_ROUND:
- rWriter.attribute("linecap", "ROUND");
+ rWriter.attribute("linecap", "ROUND"_ostr);
break;
case css::drawing::LineCap::LineCap_SQUARE:
- rWriter.attribute("linecap", "SQUARE");
+ rWriter.attribute("linecap", "SQUARE"_ostr);
break;
default:
- rWriter.attribute("linecap", "Unknown");
+ rWriter.attribute("linecap", "Unknown"_ostr);
break;
}
@@ -249,23 +280,24 @@ void writeSdrFillAttribute(::tools::XmlWriter& rWriter,
rWriter.startElement("gradient");
switch (rGradient.getStyle())
{
- case drawinglayer::attribute::GradientStyle::Linear:
- rWriter.attribute("style", "Linear");
+ default: // GradientStyle_MAKE_FIXED_SIZE
+ case css::awt::GradientStyle_LINEAR:
+ rWriter.attribute("style", "Linear"_ostr);
break;
- case drawinglayer::attribute::GradientStyle::Axial:
- rWriter.attribute("style", "Axial");
+ case css::awt::GradientStyle_AXIAL:
+ rWriter.attribute("style", "Axial"_ostr);
break;
- case drawinglayer::attribute::GradientStyle::Radial:
- rWriter.attribute("style", "Radial");
+ case css::awt::GradientStyle_RADIAL:
+ rWriter.attribute("style", "Radial"_ostr);
break;
- case drawinglayer::attribute::GradientStyle::Elliptical:
- rWriter.attribute("style", "Elliptical");
+ case css::awt::GradientStyle_ELLIPTICAL:
+ rWriter.attribute("style", "Elliptical"_ostr);
break;
- case drawinglayer::attribute::GradientStyle::Square:
- rWriter.attribute("style", "Square");
+ case css::awt::GradientStyle_SQUARE:
+ rWriter.attribute("style", "Square"_ostr);
break;
- case drawinglayer::attribute::GradientStyle::Rect:
- rWriter.attribute("style", "Rect");
+ case css::awt::GradientStyle_RECT:
+ rWriter.attribute("style", "Rect"_ostr);
break;
}
rWriter.attribute("border", rGradient.getBorder());
@@ -273,8 +305,23 @@ void writeSdrFillAttribute(::tools::XmlWriter& rWriter,
rWriter.attribute("offsetY", rGradient.getOffsetY());
rWriter.attribute("angle", rGradient.getAngle());
rWriter.attribute("steps", rGradient.getSteps());
- rWriter.attribute("startColor", convertColorToString(rGradient.getStartColor()));
- rWriter.attribute("endColor", convertColorToString(rGradient.getEndColor()));
+
+ auto const& rColorStops(rGradient.getColorStops());
+ for (size_t a(0); a < rColorStops.size(); a++)
+ {
+ if (0 == a)
+ rWriter.attribute("startColor",
+ convertColorToString(rColorStops[a].getStopColor()));
+ else if (rColorStops.size() == a + 1)
+ rWriter.attribute("endColor", convertColorToString(rColorStops[a].getStopColor()));
+ else
+ {
+ rWriter.startElement("colorStop");
+ rWriter.attribute("stopOffset", rColorStops[a].getStopOffset());
+ rWriter.attribute("stopColor", convertColorToString(rColorStops[a].getStopColor()));
+ rWriter.endElement();
+ }
+ }
rWriter.endElement();
}
@@ -285,13 +332,13 @@ void writeSdrFillAttribute(::tools::XmlWriter& rWriter,
switch (rHatch.getStyle())
{
case drawinglayer::attribute::HatchStyle::Single:
- rWriter.attribute("style", "Single");
+ rWriter.attribute("style", "Single"_ostr);
break;
case drawinglayer::attribute::HatchStyle::Double:
- rWriter.attribute("style", "Double");
+ rWriter.attribute("style", "Double"_ostr);
break;
case drawinglayer::attribute::HatchStyle::Triple:
- rWriter.attribute("style", "Triple");
+ rWriter.attribute("style", "Triple"_ostr);
break;
}
rWriter.attribute("distance", rHatch.getDistance());
@@ -318,19 +365,19 @@ void writeShadeMode(::tools::XmlWriter& rWriter, const css::drawing::ShadeMode&
switch (rMode)
{
case css::drawing::ShadeMode_FLAT:
- rWriter.attribute("shadeMode", "Flat");
+ rWriter.attribute("shadeMode", "Flat"_ostr);
break;
case css::drawing::ShadeMode_SMOOTH:
- rWriter.attribute("shadeMode", "Smooth");
+ rWriter.attribute("shadeMode", "Smooth"_ostr);
break;
case css::drawing::ShadeMode_PHONG:
- rWriter.attribute("shadeMode", "Phong");
+ rWriter.attribute("shadeMode", "Phong"_ostr);
break;
case css::drawing::ShadeMode_DRAFT:
- rWriter.attribute("shadeMode", "Draft");
+ rWriter.attribute("shadeMode", "Draft"_ostr);
break;
default:
- rWriter.attribute("shadeMode", "Undefined");
+ rWriter.attribute("shadeMode", "Undefined"_ostr);
break;
}
}
@@ -340,13 +387,13 @@ void writeProjectionMode(::tools::XmlWriter& rWriter, const css::drawing::Projec
switch (rMode)
{
case css::drawing::ProjectionMode_PARALLEL:
- rWriter.attribute("projectionMode", "Parallel");
+ rWriter.attribute("projectionMode", "Parallel"_ostr);
break;
case css::drawing::ProjectionMode_PERSPECTIVE:
- rWriter.attribute("projectionMode", "Perspective");
+ rWriter.attribute("projectionMode", "Perspective"_ostr);
break;
default:
- rWriter.attribute("projectionMode", "Undefined");
+ rWriter.attribute("projectionMode", "Undefined"_ostr);
break;
}
}
@@ -356,16 +403,16 @@ void writeNormalsKind(::tools::XmlWriter& rWriter, const css::drawing::NormalsKi
switch (rKind)
{
case css::drawing::NormalsKind_SPECIFIC:
- rWriter.attribute("normalsKind", "Specific");
+ rWriter.attribute("normalsKind", "Specific"_ostr);
break;
case css::drawing::NormalsKind_FLAT:
- rWriter.attribute("normalsKind", "Flat");
+ rWriter.attribute("normalsKind", "Flat"_ostr);
break;
case css::drawing::NormalsKind_SPHERE:
- rWriter.attribute("normalsKind", "Sphere");
+ rWriter.attribute("normalsKind", "Sphere"_ostr);
break;
default:
- rWriter.attribute("normalsKind", "Undefined");
+ rWriter.attribute("normalsKind", "Undefined"_ostr);
break;
}
}
@@ -376,16 +423,16 @@ void writeTextureProjectionMode(::tools::XmlWriter& rWriter, const char* pElemen
switch (rMode)
{
case css::drawing::TextureProjectionMode_OBJECTSPECIFIC:
- rWriter.attribute(pElement, "Specific");
+ rWriter.attribute(pElement, "Specific"_ostr);
break;
case css::drawing::TextureProjectionMode_PARALLEL:
- rWriter.attribute(pElement, "Parallel");
+ rWriter.attribute(pElement, "Parallel"_ostr);
break;
case css::drawing::TextureProjectionMode_SPHERE:
- rWriter.attribute(pElement, "Sphere");
+ rWriter.attribute(pElement, "Sphere"_ostr);
break;
default:
- rWriter.attribute(pElement, "Undefined");
+ rWriter.attribute(pElement, "Undefined"_ostr);
break;
}
}
@@ -395,16 +442,16 @@ void writeTextureKind(::tools::XmlWriter& rWriter, const css::drawing::TextureKi
switch (rKind)
{
case css::drawing::TextureKind2_LUMINANCE:
- rWriter.attribute("textureKind", "Luminance");
+ rWriter.attribute("textureKind", "Luminance"_ostr);
break;
case css::drawing::TextureKind2_INTENSITY:
- rWriter.attribute("textureKind", "Intensity");
+ rWriter.attribute("textureKind", "Intensity"_ostr);
break;
case css::drawing::TextureKind2_COLOR:
- rWriter.attribute("textureKind", "Color");
+ rWriter.attribute("textureKind", "Color"_ostr);
break;
default:
- rWriter.attribute("textureKind", "Undefined");
+ rWriter.attribute("textureKind", "Undefined"_ostr);
break;
}
}
@@ -414,16 +461,16 @@ void writeTextureMode(::tools::XmlWriter& rWriter, const css::drawing::TextureMo
switch (rMode)
{
case css::drawing::TextureMode_REPLACE:
- rWriter.attribute("textureMode", "Replace");
+ rWriter.attribute("textureMode", "Replace"_ostr);
break;
case css::drawing::TextureMode_MODULATE:
- rWriter.attribute("textureMode", "Modulate");
+ rWriter.attribute("textureMode", "Modulate"_ostr);
break;
case css::drawing::TextureMode_BLEND:
- rWriter.attribute("textureMode", "Blend");
+ rWriter.attribute("textureMode", "Blend"_ostr);
break;
default:
- rWriter.attribute("textureMode", "Undefined");
+ rWriter.attribute("textureMode", "Undefined"_ostr);
break;
}
}
@@ -439,6 +486,25 @@ void writeMaterialAttribute(::tools::XmlWriter& rWriter,
rWriter.endElement();
}
+void writeSpreadMethod(::tools::XmlWriter& rWriter,
+ const drawinglayer::primitive2d::SpreadMethod& rSpreadMethod)
+{
+ switch (rSpreadMethod)
+ {
+ case drawinglayer::primitive2d::SpreadMethod::Pad:
+ rWriter.attribute("spreadmethod", "pad"_ostr);
+ break;
+ case drawinglayer::primitive2d::SpreadMethod::Reflect:
+ rWriter.attribute("spreadmethod", "reflect"_ostr);
+ break;
+ case drawinglayer::primitive2d::SpreadMethod::Repeat:
+ rWriter.attribute("spreadmethod", "repeat"_ostr);
+ break;
+ default:
+ rWriter.attribute("spreadmethod", "unknown"_ostr);
+ }
+}
+
} // end anonymous namespace
Primitive2dXmlDump::Primitive2dXmlDump()
@@ -551,8 +617,7 @@ public:
default:
{
rWriter.startElement("unhandled");
- rWriter.attribute("id",
- OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
+ rWriter.attribute("id", sCurrentElementTag);
rWriter.attribute("idNumber", nId);
drawinglayer::geometry::ViewInformation3D aViewInformation3D;
@@ -598,20 +663,26 @@ xmlDocUniquePtr Primitive2dXmlDump::dumpAndParse(
return xmlDocUniquePtr(xmlParseDoc(reinterpret_cast<xmlChar*>(pBuffer.get())));
}
+OUString Primitive2dXmlDump::idToString(sal_uInt32 nId)
+{
+ return drawinglayer::primitive2d::idToString(nId);
+}
+
void Primitive2dXmlDump::decomposeAndWrite(
const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence,
::tools::XmlWriter& rWriter)
{
- for (size_t i = 0; i < rPrimitive2DSequence.size(); i++)
+ for (auto const& i : rPrimitive2DSequence)
{
- drawinglayer::primitive2d::Primitive2DReference xPrimitive2DReference
- = rPrimitive2DSequence[i];
- const BasePrimitive2D* pBasePrimitive
- = static_cast<const BasePrimitive2D*>(xPrimitive2DReference.get());
+ const BasePrimitive2D* pBasePrimitive = i.get();
sal_uInt32 nId = pBasePrimitive->getPrimitive2DID();
if (nId < maFilter.size() && maFilter[nId])
continue;
+ // handled by subclass
+ if (decomposeAndWrite(*pBasePrimitive, rWriter))
+ continue;
+
OUString sCurrentElementTag = drawinglayer::primitive2d::idToString(nId);
switch (nId)
@@ -623,7 +694,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.startElement("bitmap");
writeMatrix(rWriter, rBitmapPrimitive2D.getTransform());
- const BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapPrimitive2D.getXBitmap()));
+ const BitmapEx aBitmapEx(rBitmapPrimitive2D.getBitmap());
const Size& rSizePixel(aBitmapEx.GetSizePixel());
rWriter.attribute("height", rSizePixel.getHeight());
@@ -706,6 +777,49 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.endElement();
}
break;
+
+ case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D:
+ {
+ const PolygonStrokeArrowPrimitive2D& rPolygonStrokeArrowPrimitive2D
+ = dynamic_cast<const PolygonStrokeArrowPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("polygonstrokearrow");
+
+ rWriter.startElement("polygon");
+ rWriter.content(basegfx::utils::exportToSvgPoints(
+ rPolygonStrokeArrowPrimitive2D.getB2DPolygon()));
+ rWriter.endElement();
+
+ if (rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon().count())
+ {
+ rWriter.startElement("linestartattribute");
+ rWriter.attribute("width",
+ rPolygonStrokeArrowPrimitive2D.getStart().getWidth());
+ rWriter.attribute("centered",
+ static_cast<sal_Int32>(
+ rPolygonStrokeArrowPrimitive2D.getStart().isCentered()));
+ writePolyPolygon(rWriter,
+ rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon());
+ rWriter.endElement();
+ }
+
+ if (rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon().count())
+ {
+ rWriter.startElement("lineendattribute");
+ rWriter.attribute("width", rPolygonStrokeArrowPrimitive2D.getEnd().getWidth());
+ rWriter.attribute("centered",
+ static_cast<sal_Int32>(
+ rPolygonStrokeArrowPrimitive2D.getEnd().isCentered()));
+ writePolyPolygon(rWriter,
+ rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon());
+ rWriter.endElement();
+ }
+
+ writeLineAttribute(rWriter, rPolygonStrokeArrowPrimitive2D.getLineAttribute());
+ writeStrokeAttribute(rWriter, rPolygonStrokeArrowPrimitive2D.getStrokeAttribute());
+ rWriter.endElement();
+ }
+ break;
+
case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
{
const PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D
@@ -718,14 +832,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.endElement();
writeLineAttribute(rWriter, rPolygonStrokePrimitive2D.getLineAttribute());
-
- rWriter.startElement("stroke");
- const drawinglayer::attribute::StrokeAttribute& aStrokeAttribute
- = rPolygonStrokePrimitive2D.getStrokeAttribute();
- rWriter.attribute("fulldotdashlen", aStrokeAttribute.getFullDotDashLen());
- //rWriter.attribute("dotdasharray", aStrokeAttribute.getDotDashArray());
- rWriter.endElement();
-
+ writeStrokeAttribute(rWriter, rPolygonStrokePrimitive2D.getStrokeAttribute());
rWriter.endElement();
}
break;
@@ -736,9 +843,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.startElement("polypolygonstroke");
writeLineAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getLineAttribute());
-
- //getStrokeAttribute()
-
+ writeStrokeAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getStrokeAttribute());
writePolyPolygon(rWriter, rPolyPolygonStrokePrimitive2D.getB2DPolyPolygon());
rWriter.endElement();
@@ -823,6 +928,15 @@ void Primitive2dXmlDump::decomposeAndWrite(
const drawinglayer::attribute::FontAttribute& aFontAttribute
= rTextSimplePortionPrimitive2D.getFontAttribute();
rWriter.attribute("familyname", aFontAttribute.getFamilyName());
+ const std::vector<double> aDx = rTextSimplePortionPrimitive2D.getDXArray();
+ if (aDx.size())
+ {
+ for (size_t iDx = 0; iDx < aDx.size(); ++iDx)
+ {
+ OString sName = "dx" + OString::number(iDx);
+ rWriter.attribute(sName, OString::number(aDx[iDx]));
+ }
+ }
rWriter.endElement();
}
break;
@@ -872,18 +986,46 @@ void Primitive2dXmlDump::decomposeAndWrite(
}
break;
+ case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D:
+ {
+ const StructureTagPrimitive2D& rStructureTagPrimitive2D
+ = dynamic_cast<const StructureTagPrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("structuretag");
+ rWriter.attribute("structureelement",
+ rStructureTagPrimitive2D.getStructureElement());
+
+ decomposeAndWrite(rStructureTagPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ }
+ break;
+
case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D:
{
const SvgRadialGradientPrimitive2D& rSvgRadialGradientPrimitive2D
= dynamic_cast<const SvgRadialGradientPrimitive2D&>(*pBasePrimitive);
rWriter.startElement("svgradialgradient");
- basegfx::B2DPoint aFocusAttribute = rSvgRadialGradientPrimitive2D.getFocal();
+ if (rSvgRadialGradientPrimitive2D.isFocalSet())
+ {
+ basegfx::B2DPoint aFocalAttribute = rSvgRadialGradientPrimitive2D.getFocal();
+ rWriter.attribute("focalx", aFocalAttribute.getX());
+ rWriter.attribute("focaly", aFocalAttribute.getY());
+ }
+ basegfx::B2DPoint aStartPoint = rSvgRadialGradientPrimitive2D.getStart();
+ rWriter.attribute("startx", aStartPoint.getX());
+ rWriter.attribute("starty", aStartPoint.getY());
rWriter.attribute("radius",
OString::number(rSvgRadialGradientPrimitive2D.getRadius()));
- rWriter.attribute("focusx", aFocusAttribute.getX());
- rWriter.attribute("focusy", aFocusAttribute.getY());
+ writeSpreadMethod(rWriter, rSvgRadialGradientPrimitive2D.getSpreadMethod());
+ rWriter.attributeDouble(
+ "opacity",
+ rSvgRadialGradientPrimitive2D.getGradientEntries().front().getOpacity());
+
+ rWriter.startElement("transform");
+ writeMatrix(rWriter, rSvgRadialGradientPrimitive2D.getGradientTransform());
+ rWriter.endElement();
+ writePolyPolygon(rWriter, rSvgRadialGradientPrimitive2D.getPolyPolygon());
rWriter.endElement();
}
break;
@@ -900,7 +1042,7 @@ void Primitive2dXmlDump::decomposeAndWrite(
rWriter.attribute("starty", aStartAttribute.getY());
rWriter.attribute("endx", aEndAttribute.getX());
rWriter.attribute("endy", aEndAttribute.getY());
- //rWriter.attribute("spreadmethod", (int)rSvgLinearGradientPrimitive2D.getSpreadMethod());
+ writeSpreadMethod(rWriter, rSvgLinearGradientPrimitive2D.getSpreadMethod());
rWriter.attributeDouble(
"opacity",
rSvgLinearGradientPrimitive2D.getGradientEntries().front().getOpacity());
@@ -993,11 +1135,17 @@ void Primitive2dXmlDump::decomposeAndWrite(
case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
{
// ShadowPrimitive2D.
+ const ShadowPrimitive2D& rShadowPrimitive2D
+ = dynamic_cast<const ShadowPrimitive2D&>(*pBasePrimitive);
rWriter.startElement("shadow");
- drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
- pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
- drawinglayer::geometry::ViewInformation2D());
- decomposeAndWrite(aPrimitiveContainer, rWriter);
+ rWriter.attribute("color",
+ convertColorToString(rShadowPrimitive2D.getShadowColor()));
+ rWriter.attributeDouble("blur", rShadowPrimitive2D.getShadowBlur());
+
+ rWriter.startElement("transform");
+ writeMatrix(rWriter, rShadowPrimitive2D.getShadowTransform());
+ rWriter.endElement();
+
rWriter.endElement();
break;
}
@@ -1005,11 +1153,26 @@ void Primitive2dXmlDump::decomposeAndWrite(
case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
{
// ModifiedColorPrimitive2D.
+ const ModifiedColorPrimitive2D& rModifiedColorPrimitive2D
+ = dynamic_cast<const ModifiedColorPrimitive2D&>(*pBasePrimitive);
rWriter.startElement("modifiedColor");
- drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
- pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
- drawinglayer::geometry::ViewInformation2D());
- decomposeAndWrite(aPrimitiveContainer, rWriter);
+ const basegfx::BColorModifierSharedPtr& aColorModifier
+ = rModifiedColorPrimitive2D.getColorModifier();
+ rWriter.attribute("modifier", aColorModifier->getModifierName());
+
+ decomposeAndWrite(rModifiedColorPrimitive2D.getChildren(), rWriter);
+ rWriter.endElement();
+ break;
+ }
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ {
+ // SoftEdgePrimitive2D.
+ const SoftEdgePrimitive2D& rSoftEdgePrimitive2D
+ = dynamic_cast<const SoftEdgePrimitive2D&>(*pBasePrimitive);
+ rWriter.startElement("softedge");
+ rWriter.attribute("radius", OUString::number(rSoftEdgePrimitive2D.getRadius()));
+
+ decomposeAndWrite(rSoftEdgePrimitive2D.getChildren(), rWriter);
rWriter.endElement();
break;
}
@@ -1053,29 +1216,10 @@ void Primitive2dXmlDump::decomposeAndWrite(
default:
{
- OString aName("unhandled");
- switch (nId)
- {
- case PRIMITIVE2D_ID_RANGE_SVX | 14: // PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D
- {
- aName = "sdrCell";
- break;
- }
- }
- rWriter.startElement(aName);
- rWriter.attribute("id",
- OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8));
+ rWriter.startElement("unhandled");
+ rWriter.attribute("id", sCurrentElementTag);
rWriter.attribute("idNumber", nId);
- auto pBufferedDecomposition
- = dynamic_cast<const BufferedDecompositionPrimitive2D*>(pBasePrimitive);
- if (pBufferedDecomposition)
- {
- rWriter.attribute(
- "transparenceForShadow",
- OString::number(pBufferedDecomposition->getTransparenceForShadow()));
- }
-
drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer;
pBasePrimitive->get2DDecomposition(aPrimitiveContainer,
drawinglayer::geometry::ViewInformation2D());
diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx b/drawinglayer/source/tools/wmfemfhelper.cxx
index 22b1d4217413..13634c9ad1a5 100644
--- a/drawinglayer/source/tools/wmfemfhelper.cxx
+++ b/drawinglayer/source/tools/wmfemfhelper.cxx
@@ -21,10 +21,12 @@
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/metaact.hxx>
-#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/gradienttools.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
@@ -75,7 +77,7 @@ namespace drawinglayer::primitive2d
{
protected:
/// local decomposition.
- virtual void create2DDecomposition(Primitive2DContainer& rContainer,
+ virtual Primitive2DReference create2DDecomposition(
const geometry::ViewInformation2D& rViewInformation) const override;
public:
@@ -90,14 +92,14 @@ namespace drawinglayer::primitive2d
}
- void NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
- Primitive2DContainer& rContainer,
+ Primitive2DReference NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
const geometry::ViewInformation2D& /*rViewInformation*/) const
{
if (!getFillGradient().isDefault())
{
- createFill(rContainer, false);
+ return createFill(false);
}
+ return nullptr;
}
} // end of namespace
@@ -332,15 +334,7 @@ namespace wmfemfhelper
drawinglayer::primitive2d::Primitive2DContainer TargetHolder::getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
{
- const sal_uInt32 nCount(aTargets.size());
- drawinglayer::primitive2d::Primitive2DContainer xRetval(nCount);
-
- for (sal_uInt32 a(0); a < nCount; a++)
- {
- xRetval[a] = aTargets[a].get();
- }
- // Since we have released them from the list
- aTargets.clear();
+ drawinglayer::primitive2d::Primitive2DContainer xRetval = std::move(aTargets);
if (!xRetval.empty() && rPropertyHolder.getClipPolyPolygonActive())
{
@@ -348,12 +342,11 @@ namespace wmfemfhelper
if (rClipPolyPolygon.count())
{
- drawinglayer::primitive2d::Primitive2DReference xMask(
- new drawinglayer::primitive2d::MaskPrimitive2D(
- rClipPolyPolygon,
- std::move(xRetval)));
-
- xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask };
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer{
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ rClipPolyPolygon,
+ std::move(xRetval))
+ };
}
}
@@ -476,7 +469,7 @@ namespace wmfemfhelper
aLinePolygon.transform(rProperties.getTransformation());
rTarget.append(
new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
- aLinePolygon,
+ std::move(aLinePolygon),
rProperties.getLineColor()));
}
}
@@ -493,7 +486,7 @@ namespace wmfemfhelper
aFillPolyPolygon.transform(rProperties.getTransformation());
rTarget.append(
new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
- aFillPolyPolygon,
+ std::move(aFillPolyPolygon),
rProperties.getFillColor()));
}
}
@@ -515,7 +508,7 @@ namespace wmfemfhelper
{
basegfx::B2DPolygon aLinePolygon(rLinePolygon);
aLinePolygon.transform(rProperties.getTransformation());
- const drawinglayer::attribute::LineAttribute aLineAttribute(
+ drawinglayer::attribute::LineAttribute aLineAttribute(
rProperties.getLineColor(),
bWidthUsed ? rLineInfo.GetWidth() : 0.0,
rLineInfo.GetLineJoin(),
@@ -523,39 +516,23 @@ namespace wmfemfhelper
if(bDashDotUsed)
{
- std::vector< double > fDotDashArray;
- const double fDashLen(rLineInfo.GetDashLen());
- const double fDotLen(rLineInfo.GetDotLen());
- const double fDistance(rLineInfo.GetDistance());
-
- for(sal_uInt16 a(0); a < rLineInfo.GetDashCount(); a++)
- {
- fDotDashArray.push_back(fDashLen);
- fDotDashArray.push_back(fDistance);
- }
-
- for(sal_uInt16 b(0); b < rLineInfo.GetDotCount(); b++)
- {
- fDotDashArray.push_back(fDotLen);
- fDotDashArray.push_back(fDistance);
- }
-
+ std::vector< double > fDotDashArray = rLineInfo.GetDotDashArray();
const double fAccumulated(std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
- const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
+ drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
std::move(fDotDashArray),
fAccumulated);
rTarget.append(
new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
- aLinePolygon,
- aLineAttribute,
- aStrokeAttribute));
+ std::move(aLinePolygon),
+ std::move(aLineAttribute),
+ std::move(aStrokeAttribute)));
}
else
{
rTarget.append(
new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
- aLinePolygon,
+ std::move(aLinePolygon),
aLineAttribute));
}
}
@@ -648,7 +625,7 @@ namespace wmfemfhelper
rTarget.append(
new drawinglayer::primitive2d::BitmapPrimitive2D(
- VCLUnoHelper::CreateVCLXBitmap(rBitmapEx),
+ rBitmapEx,
aObjectTransform));
}
@@ -663,8 +640,8 @@ namespace wmfemfhelper
aWhite, rMaskColor
};
- Bitmap aMask(rBitmap.CreateMask(aWhite));
- Bitmap aSolid(rBitmap.GetSizePixel(), vcl::PixelFormat::N1_BPP, &aBiLevelPalette);
+ AlphaMask aMask(rBitmap.CreateAlphaMask(aWhite));
+ Bitmap aSolid(rBitmap.GetSizePixel(), vcl::PixelFormat::N8_BPP, &aBiLevelPalette);
aSolid.Erase(rMaskColor);
@@ -696,50 +673,13 @@ namespace wmfemfhelper
aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01);
}
- drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GradientStyle::Rect);
-
- switch(rGradient.GetStyle())
- {
- case GradientStyle::Linear :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Linear;
- break;
- }
- case GradientStyle::Axial :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Axial;
- break;
- }
- case GradientStyle::Radial :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Radial;
- break;
- }
- case GradientStyle::Elliptical :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Elliptical;
- break;
- }
- case GradientStyle::Square :
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Square;
- break;
- }
- default : // GradientStyle::Rect
- {
- aGradientStyle = drawinglayer::attribute::GradientStyle::Rect;
- break;
- }
- }
-
return drawinglayer::attribute::FillGradientAttribute(
- aGradientStyle,
+ rGradient.GetStyle(),
static_cast<double>(rGradient.GetBorder()) * 0.01,
static_cast<double>(rGradient.GetOfsX()) * 0.01,
static_cast<double>(rGradient.GetOfsY()) * 0.01,
toRadians(rGradient.GetAngle()),
- aStart,
- aEnd,
+ basegfx::BColorStops(aStart, aEnd),
rGradient.GetSteps());
}
@@ -939,12 +879,13 @@ namespace wmfemfhelper
const Gradient& rGradient,
PropertyHolder const & rPropertyHolder)
{
- const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ basegfx::BColor aSingleColor;
- if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{
// not really a gradient. Create filled rectangle
- return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder);
+ return CreateColorWallpaper(rRange, aSingleColor, rPropertyHolder);
}
else
{
@@ -952,7 +893,7 @@ namespace wmfemfhelper
rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pRetval(
new drawinglayer::primitive2d::FillGradientPrimitive2D(
rRange,
- aAttribute));
+ std::move(aAttribute)));
if(!rPropertyHolder.getTransformation().isIdentity())
{
@@ -1099,6 +1040,7 @@ namespace wmfemfhelper
sal_uInt16 nTextStart,
sal_uInt16 nTextLength,
std::vector< double >&& rDXArray,
+ std::vector< sal_Bool >&& rKashidaArray,
TargetHolder& rTarget,
PropertyHolder const & rProperty)
{
@@ -1124,7 +1066,7 @@ namespace wmfemfhelper
// prepare FontColor and Locale
const basegfx::BColor aFontColor(rProperty.getTextColor());
const Color aFillColor(rFont.GetFillColor());
- const css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
+ css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
const bool bWordLineMode(rFont.IsWordLineMode());
const bool bDecoratedIsNeeded(
@@ -1183,6 +1125,7 @@ namespace wmfemfhelper
nTextStart,
nTextLength,
std::move(rDXArray),
+ std::move(rKashidaArray),
aFontAttribute,
aLocale,
aFontColor,
@@ -1211,8 +1154,9 @@ namespace wmfemfhelper
nTextStart,
nTextLength,
std::vector(rDXArray),
- aFontAttribute,
- aLocale,
+ std::vector(rKashidaArray),
+ std::move(aFontAttribute),
+ std::move(aLocale),
aFontColor);
}
}
@@ -1256,7 +1200,7 @@ namespace wmfemfhelper
// prepare Primitive2DSequence, put text in foreground
drawinglayer::primitive2d::Primitive2DContainer aSequence(2);
- aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult);
+ aSequence[1] = pResult;
// prepare filled polygon
basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aTextRange));
@@ -1314,7 +1258,7 @@ namespace wmfemfhelper
if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
return;
- std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector;
+ drawinglayer::primitive2d::Primitive2DContainer aTargets;
basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
drawinglayer::attribute::FontAttribute aFontAttribute;
basegfx::B2DHomMatrix aTextTransform;
@@ -1336,7 +1280,7 @@ namespace wmfemfhelper
if(bOverlineUsed)
{
// create primitive geometry for overline
- aTargetVector.push_back(
+ aTargets.push_back(
new drawinglayer::primitive2d::TextLinePrimitive2D(
aTextTransform,
fLineWidth,
@@ -1349,7 +1293,7 @@ namespace wmfemfhelper
if(bUnderlineUsed)
{
// create primitive geometry for underline
- aTargetVector.push_back(
+ aTargets.push_back(
new drawinglayer::primitive2d::TextLinePrimitive2D(
aTextTransform,
fLineWidth,
@@ -1368,22 +1312,22 @@ namespace wmfemfhelper
// strikeout with character
const sal_Unicode aStrikeoutChar(
drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
- const css::lang::Locale aLocale(LanguageTag(
+ css::lang::Locale aLocale(LanguageTag(
rProperty.getLanguageType()).getLocale());
- aTargetVector.push_back(
+ aTargets.push_back(
new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
aTextTransform,
fLineWidth,
rProperty.getTextColor(),
aStrikeoutChar,
- aFontAttribute,
- aLocale));
+ std::move(aFontAttribute),
+ std::move(aLocale)));
}
else
{
// strikeout with geometry
- aTargetVector.push_back(
+ aTargets.push_back(
new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
aTextTransform,
fLineWidth,
@@ -1394,31 +1338,21 @@ namespace wmfemfhelper
}
}
- if(aTargetVector.empty())
+ if(aTargets.empty())
return;
// add created text primitive to target
if(rProperty.getTransformation().isIdentity())
{
- for(drawinglayer::primitive2d::BasePrimitive2D* a : aTargetVector)
- {
- rTarget.append(a);
- }
+ rTarget.append(std::move(aTargets));
}
else
{
// when a transformation is set, embed to it
- drawinglayer::primitive2d::Primitive2DContainer xTargets(aTargetVector.size());
-
- for(size_t a(0); a < aTargetVector.size(); a++)
- {
- xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]);
- }
-
rTarget.append(
new drawinglayer::primitive2d::TransformPrimitive2D(
rProperty.getTransformation(),
- std::move(xTargets)));
+ std::move(aTargets)));
}
}
@@ -1563,7 +1497,6 @@ namespace wmfemfhelper
}
else
{
- aLineInfo.SetLineJoin(basegfx::B2DLineJoin::NONE); // It were lines; force to NONE
createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
aLinePolygon.clear();
aLineInfo = pA->GetLineInfo();
@@ -1578,16 +1511,14 @@ namespace wmfemfhelper
aLinePolygon.append(aEnd);
}
- nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
+ nAction++;
+ if (nAction < nCount)
+ pAction = rMetaFile.GetAction(nAction);
}
nAction--;
-
- if(aLinePolygon.count())
- {
- aLineInfo.SetLineJoin(basegfx::B2DLineJoin::NONE); // It were lines; force to NONE
+ if (aLinePolygon.count())
createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
- }
}
break;
@@ -1805,6 +1736,7 @@ namespace wmfemfhelper
nTextIndex,
nTextLength,
std::move(aDXArray),
+ {},
rTargetHolders.Current(),
rPropertyHolders.Current());
}
@@ -1828,7 +1760,8 @@ namespace wmfemfhelper
{
// prepare DXArray (if used)
std::vector< double > aDXArray;
- const std::vector<sal_Int32> & rDXArray = pA->GetDXArray();
+ const KernArray& rDXArray = pA->GetDXArray();
+ std::vector< sal_Bool > aKashidaArray = pA->GetKashidaArray();
if(!rDXArray.empty())
{
@@ -1846,6 +1779,7 @@ namespace wmfemfhelper
nTextIndex,
nTextLength,
std::move(aDXArray),
+ std::move(aKashidaArray),
rTargetHolders.Current(),
rPropertyHolders.Current());
}
@@ -1909,6 +1843,7 @@ namespace wmfemfhelper
nTextIndex,
nTextLength,
std::move(aTextArray),
+ {},
rTargetHolders.Current(),
rPropertyHolders.Current());
}
@@ -2114,10 +2049,11 @@ namespace wmfemfhelper
if(!aRange.isEmpty())
{
const Gradient& rGradient = pA->GetGradient();
- const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange));
+ basegfx::BColor aSingleColor;
- if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{
// not really a gradient. Create filled rectangle
createFillPrimitive(
@@ -2146,7 +2082,7 @@ namespace wmfemfhelper
xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
new drawinglayer::primitive2d::FillGradientPrimitive2D(
aRange,
- aAttribute));
+ std::move(aAttribute)));
}
// #i112300# clip against polygon representing the rectangle from
@@ -2155,7 +2091,7 @@ namespace wmfemfhelper
aOutline.transform(rPropertyHolders.Current().getTransformation());
rTargetHolders.Current().append(
new drawinglayer::primitive2d::MaskPrimitive2D(
- aOutline,
+ std::move(aOutline),
std::move(xGradient)));
}
}
@@ -2172,7 +2108,7 @@ namespace wmfemfhelper
if(aOutline.count())
{
const Hatch& rHatch = pA->GetHatch();
- const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
+ drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
aOutline.transform(rPropertyHolders.Current().getTransformation());
@@ -2181,11 +2117,11 @@ namespace wmfemfhelper
new drawinglayer::primitive2d::FillHatchPrimitive2D(
aObjectRange,
basegfx::BColor(),
- aAttribute));
+ std::move(aAttribute)));
rTargetHolders.Current().append(
new drawinglayer::primitive2d::MaskPrimitive2D(
- aOutline,
+ std::move(aOutline),
drawinglayer::primitive2d::Primitive2DContainer { aFillHatch }));
}
@@ -2825,15 +2761,16 @@ namespace wmfemfhelper
// check if gradient is a real gradient
const Gradient& rGradient = pA->GetGradient();
- const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ basegfx::BColor aSingleColor;
- if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{
// not really a gradient; create UnifiedTransparencePrimitive2D
rTargetHolders.Current().append(
new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
std::move(xSubContent),
- aAttribute.getStartColor().luminance()));
+ aSingleColor.luminance()));
}
else
{
@@ -2845,7 +2782,7 @@ namespace wmfemfhelper
const drawinglayer::primitive2d::Primitive2DReference xTransparence(
new drawinglayer::primitive2d::FillGradientPrimitive2D(
aRange,
- aAttribute));
+ std::move(aAttribute)));
// create transparence primitive
rTargetHolders.Current().append(
@@ -2945,15 +2882,16 @@ namespace wmfemfhelper
// get and check if gradient is a real gradient
const Gradient& rGradient = pMetaGradientExAction->GetGradient();
- const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
+ basegfx::BColor aSingleColor;
- if(aAttribute.getStartColor() == aAttribute.getEndColor())
+ if (aAttribute.getColorStops().isSingleColor(aSingleColor))
{
// not really a gradient
rTargetHolders.Current().append(
new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
- aPolyPolygon,
- aAttribute.getStartColor()));
+ std::move(aPolyPolygon),
+ aSingleColor));
}
else
{
@@ -2961,7 +2899,7 @@ namespace wmfemfhelper
rTargetHolders.Current().append(
new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
aPolyPolygon,
- aAttribute));
+ std::move(aAttribute)));
}
}
}