summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source/primitive2d
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/primitive2d')
-rw-r--r--drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx46
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx65
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx88
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx130
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx82
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx91
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx88
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx125
-rw-r--r--drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx100
-rw-r--r--drawinglayer/source/primitive2d/Primitive2DContainer.cxx155
-rw-r--r--drawinglayer/source/primitive2d/Tools.cxx235
-rw-r--r--drawinglayer/source/primitive2d/animatedprimitive2d.cxx203
-rw-r--r--drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx94
-rw-r--r--drawinglayer/source/primitive2d/baseprimitive2d.cxx106
-rw-r--r--drawinglayer/source/primitive2d/bitmapprimitive2d.cxx77
-rw-r--r--drawinglayer/source/primitive2d/borderlineprimitive2d.cxx430
-rw-r--r--drawinglayer/source/primitive2d/controlprimitive2d.cxx348
-rw-r--r--drawinglayer/source/primitive2d/cropprimitive2d.cxx155
-rw-r--r--drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx102
-rw-r--r--drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx311
-rw-r--r--drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx146
-rw-r--r--drawinglayer/source/primitive2d/epsprimitive2d.cxx84
-rw-r--r--drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx334
-rw-r--r--drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx131
-rw-r--r--drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx198
-rw-r--r--drawinglayer/source/primitive2d/glowprimitive2d.cxx62
-rw-r--r--drawinglayer/source/primitive2d/graphicprimitive2d.cxx219
-rw-r--r--drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx697
-rw-r--r--drawinglayer/source/primitive2d/gridprimitive2d.cxx336
-rw-r--r--drawinglayer/source/primitive2d/groupprimitive2d.cxx75
-rw-r--r--drawinglayer/source/primitive2d/helplineprimitive2d.cxx186
-rw-r--r--drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx52
-rw-r--r--drawinglayer/source/primitive2d/invertprimitive2d.cxx43
-rw-r--r--drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx136
-rw-r--r--drawinglayer/source/primitive2d/maskprimitive2d.cxx62
-rw-r--r--drawinglayer/source/primitive2d/mediaprimitive2d.cxx148
-rw-r--r--drawinglayer/source/primitive2d/metafileprimitive2d.cxx135
-rw-r--r--drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx67
-rw-r--r--drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx60
-rw-r--r--drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx40
-rw-r--r--drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx150
-rw-r--r--drawinglayer/source/primitive2d/patternfillprimitive2d.cxx370
-rw-r--r--drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx77
-rw-r--r--drawinglayer/source/primitive2d/polygonprimitive2d.cxx611
-rw-r--r--drawinglayer/source/primitive2d/primitivetools2d.cxx127
-rw-r--r--drawinglayer/source/primitive2d/sceneprimitive2d.cxx689
-rw-r--r--drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx106
-rw-r--r--drawinglayer/source/primitive2d/shadowprimitive2d.cxx94
-rw-r--r--drawinglayer/source/primitive2d/softedgeprimitive2d.cxx73
-rw-r--r--drawinglayer/source/primitive2d/structuretagprimitive2d.cxx71
-rw-r--r--drawinglayer/source/primitive2d/svggradientprimitive2d.cxx1096
-rw-r--r--drawinglayer/source/primitive2d/textbreakuphelper.cxx276
-rw-r--r--drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx388
-rw-r--r--drawinglayer/source/primitive2d/texteffectprimitive2d.cxx245
-rw-r--r--drawinglayer/source/primitive2d/textenumsprimitive2d.cxx105
-rw-r--r--drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx152
-rw-r--r--drawinglayer/source/primitive2d/textlayoutdevice.cxx456
-rw-r--r--drawinglayer/source/primitive2d/textlineprimitive2d.cxx290
-rw-r--r--drawinglayer/source/primitive2d/textprimitive2d.cxx309
-rw-r--r--drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx259
-rw-r--r--drawinglayer/source/primitive2d/transformprimitive2d.cxx64
-rw-r--r--drawinglayer/source/primitive2d/transparenceprimitive2d.cxx57
-rw-r--r--drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx112
-rw-r--r--drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx257
-rw-r--r--drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx105
65 files changed, 12781 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
new file mode 100644
index 000000000..76fa1a976
--- /dev/null
+++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx
@@ -0,0 +1,46 @@
+/* -*- 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/BufferedDecompositionPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() {}
+
+void BufferedDecompositionPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getBuffered2DDecomposition().empty())
+ {
+ Primitive2DContainer aNewSequence;
+ create2DDecomposition(aNewSequence, rViewInformation);
+ const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition(
+ std::move(aNewSequence));
+ }
+
+ rVisitor.visit(getBuffered2DDecomposition());
+}
+
+} // 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
new file mode 100644
index 000000000..b3aed429b
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx
@@ -0,0 +1,65 @@
+/* -*- 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/PolyPolygonColorPrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+PolyPolygonColorPrimitive2D::PolyPolygonColorPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor)
+ : maPolyPolygon(rPolyPolygon)
+ , maBColor(rBColor)
+{
+}
+
+bool PolyPolygonColorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonColorPrimitive2D& rCompare
+ = static_cast<const PolyPolygonColorPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonColorPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonColorPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D;
+}
+
+} // 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
new file mode 100644
index 000000000..3e88b6c0d
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.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/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <rtl/ref.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonGradientPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getFillGradient().isDefault())
+ {
+ // create SubSequence with FillGradientPrimitive2D
+ const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange());
+ rtl::Reference<FillGradientPrimitive2D> pNewGradient = new FillGradientPrimitive2D(
+ aPolyPolygonRange, getDefinitionRange(), getFillGradient());
+ Primitive2DContainer aSubSequence{ pNewGradient };
+
+ // create mask primitive
+ rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)));
+ }
+}
+
+PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rPolyPolygon.getB2DRange())
+ , maFillGradient(rFillGradient)
+{
+}
+
+PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rDefinitionRange)
+ , maFillGradient(rFillGradient)
+{
+}
+
+bool PolyPolygonGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonGradientPrimitive2D& rCompare
+ = static_cast<const PolyPolygonGradientPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGradient() == rCompare.getFillGradient());
+ }
+
+ return false;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonGradientPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
new file mode 100644
index 000000000..a65d1d08a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx
@@ -0,0 +1,130 @@
+/* -*- 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/PolyPolygonGraphicPrimitive2D.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <vcl/graph.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonGraphicPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getFillGraphic().isDefault())
+ return;
+
+ 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;
+
+ const Size aPrefSize(rGraphic.GetPrefSize());
+
+ // does content have a size?
+ if (!(aPrefSize.Width() && aPrefSize.Height()))
+ return;
+
+ // create SubSequence with FillGraphicPrimitive2D based on polygon range
+ const basegfx::B2DRange aOutRange(getB2DPolyPolygon().getB2DRange());
+ const basegfx::B2DHomMatrix aNewObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(aOutRange.getRange(),
+ aOutRange.getMinimum()));
+ Primitive2DReference xSubRef;
+
+ if (aOutRange != getDefinitionRange())
+ {
+ // we want to paint (tiled) content which is defined relative to DefinitionRange
+ // with the same tiling and offset(s) in the target range of the geometry (the
+ // polygon). The range given in the local FillGraphicAttribute defines the position
+ // of the graphic in unit coordinates relative to the DefinitionRange. Transform
+ // this using DefinitionRange to get to the global definition and then with the
+ // inverse transformation from the target range to go to unit coordinates relative
+ // to that target coordinate system.
+ basegfx::B2DRange aAdaptedRange(getFillGraphic().getGraphicRange());
+
+ const basegfx::B2DHomMatrix aFromDefinitionRangeToGlobal(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(getDefinitionRange().getRange(),
+ getDefinitionRange().getMinimum()));
+
+ aAdaptedRange.transform(aFromDefinitionRangeToGlobal);
+
+ basegfx::B2DHomMatrix aFromGlobalToOutRange(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(aOutRange.getRange(),
+ aOutRange.getMinimum()));
+ aFromGlobalToOutRange.invert();
+
+ aAdaptedRange.transform(aFromGlobalToOutRange);
+
+ const drawinglayer::attribute::FillGraphicAttribute aAdaptedFillGraphicAttribute(
+ getFillGraphic().getGraphic(), aAdaptedRange, getFillGraphic().getTiling(),
+ getFillGraphic().getOffsetX(), getFillGraphic().getOffsetY());
+
+ xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, aAdaptedFillGraphicAttribute);
+ }
+ else
+ {
+ xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, getFillGraphic());
+ }
+
+ // embed to mask primitive
+ rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), Primitive2DContainer{ xSubRef }));
+}
+
+PolyPolygonGraphicPrimitive2D::PolyPolygonGraphicPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ const attribute::FillGraphicAttribute& rFillGraphic)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rDefinitionRange)
+ , maFillGraphic(rFillGraphic)
+{
+}
+
+bool PolyPolygonGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonGraphicPrimitive2D& rCompare
+ = static_cast<const PolyPolygonGraphicPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGraphic() == rCompare.getFillGraphic());
+ }
+
+ return false;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonGraphicPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
new file mode 100644
index 000000000..2bfed5fb1
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx
@@ -0,0 +1,82 @@
+/* -*- 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/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonHairlinePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if (nCount)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ rContainer.push_back(
+ new PolygonHairlinePrimitive2D(aPolyPolygon.getB2DPolygon(a), getBColor()));
+ }
+ }
+}
+
+PolyPolygonHairlinePrimitive2D::PolyPolygonHairlinePrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor)
+ : maPolyPolygon(rPolyPolygon)
+ , maBColor(rBColor)
+{
+}
+
+bool PolyPolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonHairlinePrimitive2D& rCompare
+ = static_cast<const PolyPolygonHairlinePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonHairlinePrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonHairlinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
new file mode 100644
index 000000000..7fc0502c8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx
@@ -0,0 +1,91 @@
+/* -*- 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/PolyPolygonHatchPrimitive2D.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
+#include <rtl/ref.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonHatchPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getFillHatch().isDefault())
+ {
+ // create SubSequence with FillHatchPrimitive2D
+ const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange());
+ rtl::Reference<FillHatchPrimitive2D> pNewHatch = new FillHatchPrimitive2D(
+ aPolyPolygonRange, getDefinitionRange(), getBackgroundColor(), getFillHatch());
+ Primitive2DContainer aSubSequence{ pNewHatch };
+
+ // create mask primitive
+ rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)));
+ }
+}
+
+PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBackgroundColor,
+ const attribute::FillHatchAttribute& rFillHatch)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rPolyPolygon.getB2DRange())
+ , maBackgroundColor(rBackgroundColor)
+ , maFillHatch(rFillHatch)
+{
+}
+
+PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rBackgroundColor, const attribute::FillHatchAttribute& rFillHatch)
+ : maPolyPolygon(rPolyPolygon)
+ , maDefinitionRange(rDefinitionRange)
+ , maBackgroundColor(rBackgroundColor)
+ , maFillHatch(rFillHatch)
+{
+}
+
+bool PolyPolygonHatchPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonHatchPrimitive2D& rCompare
+ = static_cast<const PolyPolygonHatchPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getBackgroundColor() == rCompare.getBackgroundColor()
+ && getFillHatch() == rCompare.getFillHatch());
+ }
+
+ return false;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonHatchPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx
new file mode 100644
index 000000000..4f9f83298
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.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/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonMarkerPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if (nCount)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aPolyPolygon.getB2DPolygon(a),
+ getRGBColorA(), getRGBColorB(),
+ getDiscreteDashLength()));
+ }
+ }
+}
+
+PolyPolygonMarkerPrimitive2D::PolyPolygonMarkerPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB, double fDiscreteDashLength)
+ : maPolyPolygon(rPolyPolygon)
+ , maRGBColorA(rRGBColorA)
+ , maRGBColorB(rRGBColorB)
+ , mfDiscreteDashLength(fDiscreteDashLength)
+{
+}
+
+bool PolyPolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonMarkerPrimitive2D& rCompare
+ = static_cast<const PolyPolygonMarkerPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonMarkerPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonMarkerPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONMARKERPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
new file mode 100644
index 000000000..d5ed17980
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx
@@ -0,0 +1,125 @@
+/* -*- 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/PolyPolygonSelectionPrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonSelectionPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (getTransparence() >= 1.0 || !getB2DPolyPolygon().count())
+ return;
+
+ Primitive2DContainer aRetval;
+
+ if (getFill() && getB2DPolyPolygon().isClosed())
+ {
+ // create fill primitive
+ const Primitive2DReference aFill(
+ new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), getColor()));
+
+ aRetval = Primitive2DContainer{ aFill };
+ }
+
+ if (getDiscreteGrow() > 0.0)
+ {
+ const attribute::LineAttribute aLineAttribute(getColor(),
+ getDiscreteGrow() * getDiscreteUnit() * 2.0);
+ const Primitive2DReference aFatLine(
+ new PolyPolygonStrokePrimitive2D(getB2DPolyPolygon(), aLineAttribute));
+
+ aRetval.push_back(aFatLine);
+ }
+
+ // embed filled to transparency (if used)
+ if (!aRetval.empty() && getTransparence() > 0.0)
+ {
+ const Primitive2DReference aTrans(
+ new UnifiedTransparencePrimitive2D(std::move(aRetval), getTransparence()));
+
+ aRetval = Primitive2DContainer{ aTrans };
+ }
+
+ rContainer.append(std::move(aRetval));
+}
+
+PolyPolygonSelectionPrimitive2D::PolyPolygonSelectionPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rColor,
+ double fTransparence, double fDiscreteGrow, bool bFill)
+ : maPolyPolygon(rPolyPolygon)
+ , maColor(rColor)
+ , mfTransparence(fTransparence)
+ , mfDiscreteGrow(fabs(fDiscreteGrow))
+ , mbFill(bFill)
+{
+}
+
+bool PolyPolygonSelectionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonSelectionPrimitive2D& rCompare
+ = static_cast<const PolyPolygonSelectionPrimitive2D&>(rPrimitive);
+
+ return (
+ getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() && getColor() == rCompare.getColor()
+ && getTransparence() == rCompare.getTransparence()
+ && getDiscreteGrow() == rCompare.getDiscreteGrow() && getFill() == rCompare.getFill());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonSelectionPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(basegfx::utils::getRange(getB2DPolyPolygon()));
+
+ if (getDiscreteGrow() > 0.0)
+ {
+ // get the current DiscreteUnit (not sure if getDiscreteUnit() is updated here, better go safe way)
+ const double fDiscreteUnit(
+ (rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0))
+ .getLength());
+
+ aRetval.grow(fDiscreteUnit * getDiscreteGrow());
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonSelectionPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONSELECTIONPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
new file mode 100644
index 000000000..7191f3085
--- /dev/null
+++ b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx
@@ -0,0 +1,100 @@
+/* -*- 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/PolyPolygonStrokePrimitive2D.hxx>
+
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+void PolyPolygonStrokePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon());
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if (nCount)
+ {
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ rContainer.push_back(new PolygonStrokePrimitive2D(
+ aPolyPolygon.getB2DPolygon(a), getLineAttribute(), getStrokeAttribute()));
+ }
+ }
+}
+
+PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ : maPolyPolygon(rPolyPolygon)
+ , maLineAttribute(rLineAttribute)
+ , maStrokeAttribute(rStrokeAttribute)
+{
+}
+
+PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute)
+ : maPolyPolygon(rPolyPolygon)
+ , maLineAttribute(rLineAttribute)
+{
+}
+
+bool PolyPolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolyPolygonStrokePrimitive2D& rCompare
+ = static_cast<const PolyPolygonStrokePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolyPolygonStrokePrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // get range of it (subdivided)
+ basegfx::B2DRange aRetval(basegfx::utils::getRange(getB2DPolyPolygon()));
+
+ // if width, grow by line width
+ if (getLineAttribute().getWidth())
+ {
+ aRetval.grow(getLineAttribute().getWidth() / 2.0);
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolyPolygonStrokePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D;
+}
+
+} // end drawinglayer::primitive2d namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/Primitive2DContainer.cxx b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
new file mode 100644
index 000000000..48b0c625e
--- /dev/null
+++ b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx
@@ -0,0 +1,155 @@
+/* -*- 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/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(
+ const css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource)
+{
+ 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());
+}
+
+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)
+ {
+ *p = new UnoPrimitive2D(rPrimitive);
+ ++p;
+ }
+ return aVal;
+}
+
+Primitive2DContainer Primitive2DContainer::maybeInvert(bool bInvert)
+{
+ if (bInvert)
+ std::reverse(begin(), end());
+ return std::move(*this);
+}
+
+// get B2DRange from a given Primitive2DSequence
+basegfx::B2DRange
+Primitive2DContainer::getB2DRange(const geometry::ViewInformation2D& aViewInformation) const
+{
+ basegfx::B2DRange aRetval;
+
+ if (!empty())
+ {
+ const sal_Int32 nCount(size());
+
+ for (sal_Int32 a(0); a < nCount; a++)
+ {
+ aRetval.expand(getB2DRangeFromPrimitive2DReference((*this)[a], aViewInformation));
+ }
+ }
+
+ return aRetval;
+}
+
+bool Primitive2DContainer::operator==(const Primitive2DContainer& rB) const
+{
+ const bool bAHasElements(!empty());
+
+ if (bAHasElements != !rB.empty())
+ {
+ return false;
+ }
+
+ if (!bAHasElements)
+ {
+ return true;
+ }
+
+ const size_t nCount(size());
+
+ if (nCount != rB.size())
+ {
+ return false;
+ }
+
+ for (size_t a(0); a < nCount; a++)
+ {
+ if (!arePrimitive2DReferencesEqual((*this)[a], rB[a]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Primitive2DContainer::~Primitive2DContainer() {}
+
+void Primitive2DContainer::append(const Primitive2DReference& rSource) { push_back(rSource); }
+
+void Primitive2DContainer::append(const Primitive2DContainer& rSource)
+{
+ insert(end(), rSource.begin(), rSource.end());
+}
+
+void Primitive2DContainer::append(Primitive2DContainer&& rSource)
+{
+ this->insert(this->end(), std::make_move_iterator(rSource.begin()),
+ std::make_move_iterator(rSource.end()));
+}
+
+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()
+{
+ std::unique_lock aGuard(m_aMutex);
+ return mxPrimitive->estimateUsage();
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx
new file mode 100644
index 000000000..6a3482319
--- /dev/null
+++ b/drawinglayer/source/primitive2d/Tools.cxx
@@ -0,0 +1,235 @@
+/* -*- 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/Tools.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive2d
+{
+// get B2DRange from a given Primitive2DReference
+basegfx::B2DRange
+getB2DRangeFromPrimitive2DReference(const Primitive2DReference& rCandidate,
+ const geometry::ViewInformation2D& aViewInformation)
+{
+ if (!rCandidate)
+ return basegfx::B2DRange();
+
+ return rCandidate->getB2DRange(aViewInformation);
+}
+
+bool arePrimitive2DReferencesEqual(const Primitive2DReference& rxA, const Primitive2DReference& rxB)
+{
+ const bool bAIs(rxA.is());
+
+ if (bAIs != rxB.is())
+ {
+ return false;
+ }
+
+ if (!bAIs)
+ {
+ return true;
+ }
+
+ 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->getBasePrimitive2D()) == (*pB->getBasePrimitive2D());
+}
+
+OUString idToString(sal_uInt32 nId)
+{
+ switch (nId)
+ {
+ case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+ return "TRANSPARENCE";
+ case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D:
+ return "ANIMATEDSWITCH";
+ case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D:
+ return "ANIMATEDBLINK";
+ case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D:
+ return "ANIMATEDINTERPOLATE";
+ case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
+ return "BACKGROUNDCOLOR";
+ case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+ return "BITMAP";
+ case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D:
+ return "CONTROL";
+ case PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D:
+ return "EMBEDDED3D";
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
+ return "FILLGRAPHIC";
+ case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
+ return "FILLGRADIENT";
+ case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D:
+ return "FILLHATCH";
+ case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D:
+ return "GRAPHIC";
+ case PRIMITIVE2D_ID_GRIDPRIMITIVE2D:
+ return "GRID";
+ case PRIMITIVE2D_ID_GROUPPRIMITIVE2D:
+ return "GROUP";
+ case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D:
+ return "HELPLINE";
+ case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+ return "MARKERARRAY";
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+ return "MASK";
+ case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D:
+ return "MEDIA";
+ case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D:
+ return "METAFILE";
+ case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+ return "MODIFIEDCOLOR";
+ case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+ return "POLYGONHAIRLINE";
+ case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D:
+ return "POLYGONMARKER";
+ case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+ return "POLYGONSTROKE";
+ case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D:
+ return "POLYGONSTROKEARROW";
+ case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D:
+ return "POLYPOLYGONSTROKE";
+ case PRIMITIVE2D_ID_POLYPOLYGONSTROKEARROWPRIMITIVE2D:
+ return "POLYPOLYGONSTROKEARROW";
+ case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+ return "POLYPOLYGONCOLOR";
+ case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D:
+ return "POLYPOLYGONGRADIENT";
+ case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D:
+ return "POLYPOLYGONHATCH";
+ case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
+ return "POLYPOLYGONGRAPHIC";
+ case PRIMITIVE2D_ID_SCENEPRIMITIVE2D:
+ return "SCENE";
+ case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D:
+ return "SHADOW";
+ case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D:
+ return "TEXTSIMPLEPORTION";
+ case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D:
+ return "TEXTDECORATEDPORTION";
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+ return "TRANSFORM";
+ case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+ return "UNIFIEDTRANSPARENCE";
+ case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+ return "POINTARRAY";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D:
+ return "TEXTHIERARCHYFIELD";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D:
+ return "TEXTHIERARCHYLINE";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D:
+ return "TEXTHIERARCHYPARAGRAPH";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D:
+ return "TEXTHIERARCHYBLOCK";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D:
+ return "TEXTHIERARCHYEDIT";
+ case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D:
+ return "POLYGONWAVE";
+ case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D:
+ return "WRONGSPELL";
+ case PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D:
+ return "TEXTEFFECT";
+ case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D:
+ return "TEXTHIERARCHYBULLET";
+ case PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D:
+ return "POLYPOLYGONHAIRLINE";
+ case PRIMITIVE2D_ID_EXECUTEPRIMITIVE2D:
+ return "EXECUTE";
+ case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D:
+ return "PAGEPREVIEW";
+ case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D:
+ return "STRUCTURETAG";
+ case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D:
+ return "BORDERLINE";
+ case PRIMITIVE2D_ID_POLYPOLYGONMARKERPRIMITIVE2D:
+ return "POLYPOLYGONMARKER";
+ case PRIMITIVE2D_ID_HITTESTPRIMITIVE2D:
+ return "HITTEST";
+ case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
+ return "INVERT";
+ case PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D:
+ return "DISCRETEBITMAP";
+ case PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D:
+ return "WALLPAPERBITMAP";
+ case PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D:
+ return "TEXTLINE";
+ case PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D:
+ return "TEXTCHARACTERSTRIKEOUT";
+ case PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D:
+ return "TEXTGEOMETRYSTRIKEOUT";
+ case PRIMITIVE2D_ID_EPSPRIMITIVE2D:
+ return "EPS";
+ case PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D:
+ return "DISCRETESHADOW";
+ case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D:
+ return "HIDDENGEOMETRY";
+ case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D:
+ return "SVGLINEARGRADIENT";
+ case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D:
+ return "SVGRADIALGRADIENT";
+ case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D:
+ return "SVGLINEARATOM";
+ case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D:
+ return "SVGRADIALATOM";
+ case PRIMITIVE2D_ID_CROPPRIMITIVE2D:
+ return "CROP";
+ case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D:
+ return "PATTERNFILL";
+ case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D:
+ return "OBJECTINFO";
+ case PRIMITIVE2D_ID_POLYPOLYGONSELECTIONPRIMITIVE2D:
+ return "POLYPOLYGONSELECTION";
+ case PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D:
+ return "PAGEHIERARCHY";
+ case PRIMITIVE2D_ID_GLOWPRIMITIVE2D:
+ return "GLOWPRIMITIVE";
+ case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D:
+ return "SOFTEDGEPRIMITIVE";
+ default:
+ return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
+ }
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/animatedprimitive2d.cxx b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
new file mode 100644
index 000000000..67349a834
--- /dev/null
+++ b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx
@@ -0,0 +1,203 @@
+/* -*- 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/animatedprimitive2d.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void AnimatedSwitchPrimitive2D::setAnimationEntry(const animation::AnimationEntry& rNew)
+ {
+ // clone given animation description
+ mpAnimationEntry = rNew.clone();
+ }
+
+ AnimatedSwitchPrimitive2D::AnimatedSwitchPrimitive2D(
+ const animation::AnimationEntry& rAnimationEntry,
+ Primitive2DContainer&& aChildren,
+ bool bIsTextAnimation)
+ : GroupPrimitive2D(std::move(aChildren)),
+ mbIsTextAnimation(bIsTextAnimation)
+ {
+ // clone given animation description
+ mpAnimationEntry = rAnimationEntry.clone();
+ }
+
+ AnimatedSwitchPrimitive2D::~AnimatedSwitchPrimitive2D()
+ {
+ }
+
+ bool AnimatedSwitchPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const AnimatedSwitchPrimitive2D& rCompare = static_cast< const AnimatedSwitchPrimitive2D& >(rPrimitive);
+
+ return (getAnimationEntry() == rCompare.getAnimationEntry());
+ }
+
+ return false;
+ }
+
+ void AnimatedSwitchPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(getChildren().empty())
+ return;
+
+ const double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+ const sal_uInt32 nLen(getChildren().size());
+ sal_uInt32 nIndex(basegfx::fround(fState * static_cast<double>(nLen)));
+
+ if(nIndex >= nLen)
+ {
+ nIndex = nLen - 1;
+ }
+
+ const Primitive2DReference xRef(getChildren()[nIndex], uno::UNO_SET_THROW);
+ rVisitor.visit(xRef);
+ }
+
+ // provide unique ID
+ sal_uInt32 AnimatedSwitchPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ AnimatedBlinkPrimitive2D::AnimatedBlinkPrimitive2D(
+ const animation::AnimationEntry& rAnimationEntry,
+ Primitive2DContainer&& aChildren)
+ : AnimatedSwitchPrimitive2D(rAnimationEntry, std::move(aChildren), true/*bIsTextAnimation*/)
+ {
+ }
+
+ void AnimatedBlinkPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getChildren().empty())
+ {
+ const double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+
+ if(fState < 0.5)
+ {
+ getChildren(rVisitor);
+ }
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 AnimatedBlinkPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ AnimatedInterpolatePrimitive2D::AnimatedInterpolatePrimitive2D(
+ const std::vector< basegfx::B2DHomMatrix >& rmMatrixStack,
+ const animation::AnimationEntry& rAnimationEntry,
+ Primitive2DContainer&& aChildren)
+ : AnimatedSwitchPrimitive2D(rAnimationEntry, std::move(aChildren), true/*bIsTextAnimation*/)
+ {
+ // copy matrices to locally pre-decomposed matrix stack
+ const sal_uInt32 nCount(rmMatrixStack.size());
+ maMatrixStack.reserve(nCount);
+
+ for(const auto& a : rmMatrixStack)
+ {
+ maMatrixStack.emplace_back(a);
+ }
+ }
+
+ void AnimatedInterpolatePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ const sal_uInt32 nSize(maMatrixStack.size());
+
+ if(nSize)
+ {
+ double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+
+ if(fState < 0.0)
+ {
+ fState = 0.0;
+ }
+ else if(fState > 1.0)
+ {
+ fState = 1.0;
+ }
+
+ const double fIndex(fState * static_cast<double>(nSize - 1));
+ const sal_uInt32 nIndA(sal_uInt32(floor(fIndex)));
+ const double fOffset(fIndex - static_cast<double>(nIndA));
+ basegfx::B2DHomMatrix aTargetTransform;
+ std::vector< basegfx::utils::B2DHomMatrixBufferedDecompose >::const_iterator aMatA(maMatrixStack.begin() + nIndA);
+
+ if(basegfx::fTools::equalZero(fOffset))
+ {
+ // use matrix from nIndA directly
+ aTargetTransform = aMatA->getB2DHomMatrix();
+ }
+ else
+ {
+ // interpolate. Get involved buffered decomposed matrices
+ const sal_uInt32 nIndB((nIndA + 1) % nSize);
+ std::vector< basegfx::utils::B2DHomMatrixBufferedDecompose >::const_iterator aMatB(maMatrixStack.begin() + nIndB);
+
+ // interpolate for fOffset [0.0 .. 1.0[
+ const basegfx::B2DVector aScale(basegfx::interpolate(aMatA->getScale(), aMatB->getScale(), fOffset));
+ const basegfx::B2DVector aTranslate(basegfx::interpolate(aMatA->getTranslate(), aMatB->getTranslate(), fOffset));
+ const double fRotate(((aMatB->getRotate() - aMatA->getRotate()) * fOffset) + aMatA->getRotate());
+ const double fShearX(((aMatB->getShearX() - aMatA->getShearX()) * fOffset) + aMatA->getShearX());
+
+ // build matrix for state
+ aTargetTransform = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate);
+ }
+
+ // create new transform primitive reference, return new sequence
+ Primitive2DReference xRef(new TransformPrimitive2D(aTargetTransform, Primitive2DContainer(getChildren())));
+ rVisitor.visit(xRef);
+ }
+ else
+ {
+ getChildren(rVisitor);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 AnimatedInterpolatePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D;
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
new file mode 100644
index 000000000..8f68d9e73
--- /dev/null
+++ b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx
@@ -0,0 +1,94 @@
+/* -*- 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/backgroundcolorprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void BackgroundColorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!rViewInformation.getViewport().isEmpty())
+ {
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rViewInformation.getViewport()));
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), getBColor()));
+ }
+ }
+
+ BackgroundColorPrimitive2D::BackgroundColorPrimitive2D(
+ const basegfx::BColor& rBColor,
+ double fTransparency)
+ : maBColor(rBColor),
+ mfTransparency(fTransparency)
+ {
+ }
+
+ bool BackgroundColorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const BackgroundColorPrimitive2D& rCompare = static_cast<const BackgroundColorPrimitive2D&>(rPrimitive);
+
+ return (getBColor() == rCompare.getBColor() && getTransparency() == rCompare.getTransparency());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange BackgroundColorPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // always as big as the view
+ return rViewInformation.getViewport();
+ }
+
+ void BackgroundColorPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getBuffered2DDecomposition().empty() && (maLastViewport != rViewInformation.getViewport()))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< BackgroundColorPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange
+ const_cast< BackgroundColorPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 BackgroundColorPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/baseprimitive2d.cxx b/drawinglayer/source/primitive2d/baseprimitive2d.cxx
new file mode 100644
index 000000000..a2e0eaf6b
--- /dev/null
+++ b/drawinglayer/source/primitive2d/baseprimitive2d.cxx
@@ -0,0 +1,106 @@
+/* -*- 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/Primitive2DContainer.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/utils/canvastools.hxx>
+
+using namespace css;
+
+namespace drawinglayer::primitive2d
+{
+BasePrimitive2D::BasePrimitive2D() {}
+
+BasePrimitive2D::~BasePrimitive2D() {}
+
+bool BasePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ return (getPrimitive2DID() == rPrimitive.getPrimitive2DID());
+}
+
+namespace
+{
+// Visitor class to get the B2D range from a tree of Primitive2DReference's
+//
+class B2DRangeVisitor : public Primitive2DDecompositionVisitor
+{
+public:
+ const geometry::ViewInformation2D& mrViewInformation;
+ basegfx::B2DRange maRetval;
+ B2DRangeVisitor(const geometry::ViewInformation2D& rViewInformation)
+ : mrViewInformation(rViewInformation)
+ {
+ }
+ virtual void visit(const Primitive2DReference& r) override
+ {
+ maRetval.expand(getB2DRangeFromPrimitive2DReference(r, mrViewInformation));
+ }
+ virtual void visit(const Primitive2DContainer& r) override
+ {
+ maRetval.expand(r.getB2DRange(mrViewInformation));
+ }
+ virtual void visit(Primitive2DContainer&& r) override
+ {
+ maRetval.expand(r.getB2DRange(mrViewInformation));
+ }
+};
+}
+
+basegfx::B2DRange
+BasePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ B2DRangeVisitor aVisitor(rViewInformation);
+ get2DDecomposition(aVisitor, rViewInformation);
+ return aVisitor.maRetval;
+}
+
+void BasePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& /*rVisitor*/,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+}
+
+Primitive2DContainer
+BasePrimitive2D::getDecomposition(const uno::Sequence<beans::PropertyValue>& rViewParameters)
+{
+ const auto aViewInformation = geometry::createViewInformation2D(rViewParameters);
+ Primitive2DContainer aContainer;
+ get2DDecomposition(aContainer, aViewInformation);
+ return aContainer;
+}
+
+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 BasePrimitive2D::estimateUsage()
+{
+ return 0; // for now ignore the objects themselves
+}
+
+} // end of namespace drawinglayer::primitive2d
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
new file mode 100644
index 000000000..6f256b6e1
--- /dev/null
+++ b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx
@@ -0,0 +1,77 @@
+/* -*- 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/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+
+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)
+{
+}
+
+bool BitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const BitmapPrimitive2D& rCompare = static_cast<const BitmapPrimitive2D&>(rPrimitive);
+
+ return (getXBitmap() == rCompare.getXBitmap() && getTransform() == rCompare.getTransform());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+BitmapPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(maTransform);
+ return aRetval;
+}
+
+sal_Int64 BitmapPrimitive2D::estimateUsage()
+{
+ if (!getXBitmap().is())
+ {
+ return 0;
+ }
+
+ uno::Reference<util::XAccounting> const xAcc(getXBitmap(), uno::UNO_QUERY);
+
+ if (!xAcc.is())
+ {
+ return 0;
+ }
+
+ return xAcc->estimateUsage();
+}
+
+// provide unique ID
+sal_uInt32 BitmapPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_BITMAPPRIMITIVE2D; }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
new file mode 100644
index 000000000..7ede1f2c3
--- /dev/null
+++ b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx
@@ -0,0 +1,430 @@
+/* -*- 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/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <rtl/math.hxx>
+
+#include <algorithm>
+
+
+namespace drawinglayer::primitive2d
+{
+ BorderLine::BorderLine(
+ const drawinglayer::attribute::LineAttribute& rLineAttribute,
+ double fStartLeft,
+ double fStartRight,
+ double fEndLeft,
+ double fEndRight)
+ : maLineAttribute(rLineAttribute),
+ mfStartLeft(fStartLeft),
+ mfStartRight(fStartRight),
+ mfEndLeft(fEndLeft),
+ mfEndRight(fEndRight),
+ mbIsGap(false)
+ {
+ }
+
+ BorderLine::BorderLine(
+ double fWidth)
+ : maLineAttribute(basegfx::BColor(), fWidth),
+ mfStartLeft(0.0),
+ mfStartRight(0.0),
+ mfEndLeft(0.0),
+ mfEndRight(0.0),
+ mbIsGap(true)
+ {
+ }
+
+ BorderLine::~BorderLine()
+ {
+ }
+
+ bool BorderLine::operator==(const BorderLine& rBorderLine) const
+ {
+ return getLineAttribute() == rBorderLine.getLineAttribute()
+ && getStartLeft() == rBorderLine.getStartLeft()
+ && getStartRight() == rBorderLine.getStartRight()
+ && getEndLeft() == rBorderLine.getEndLeft()
+ && getEndRight() == rBorderLine.getEndRight()
+ && isGap() == rBorderLine.isGap();
+ }
+
+ // helper to add a centered, maybe stroked line primitive to rContainer
+ static void addPolygonStrokePrimitive2D(
+ Primitive2DContainer& rContainer,
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ {
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(rStart);
+ aPolygon.append(rEnd);
+
+ if (rStrokeAttribute.isDefault())
+ {
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(
+ aPolygon,
+ rLineAttribute));
+ }
+ else
+ {
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(
+ aPolygon,
+ rLineAttribute,
+ rStrokeAttribute));
+ }
+ }
+
+ double BorderLinePrimitive2D::getFullWidth() const
+ {
+ double fRetval(0.0);
+
+ for(const auto& candidate : maBorderLines)
+ {
+ fRetval += candidate.getLineAttribute().getWidth();
+ }
+
+ return fRetval;
+ }
+
+ void BorderLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if (getStart().equal(getEnd()) || getBorderLines().empty())
+ return;
+
+ // get data and vectors
+ basegfx::B2DVector aVector(getEnd() - getStart());
+ aVector.normalize();
+ const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
+ const double fFullWidth(getFullWidth());
+ double fOffset(fFullWidth * -0.5);
+
+ for(const auto& candidate : maBorderLines)
+ {
+ const double fWidth(candidate.getLineAttribute().getWidth());
+
+ if(!candidate.isGap())
+ {
+ const basegfx::B2DVector aDeltaY(aPerpendicular * (fOffset + (fWidth * 0.5)));
+ const basegfx::B2DPoint aStart(getStart() + aDeltaY);
+ const basegfx::B2DPoint aEnd(getEnd() + aDeltaY);
+ const bool bStartPerpendicular(rtl::math::approxEqual(candidate.getStartLeft(), candidate.getStartRight()));
+ const bool bEndPerpendicular(rtl::math::approxEqual(candidate.getEndLeft(), candidate.getEndRight()));
+
+ if(bStartPerpendicular && bEndPerpendicular)
+ {
+ // start and end extends lead to an edge perpendicular to the line, so we can just use
+ // a PolygonStrokePrimitive2D for representation
+ addPolygonStrokePrimitive2D(
+ rContainer,
+ aStart - (aVector * candidate.getStartLeft()),
+ aEnd + (aVector * candidate.getEndLeft()),
+ candidate.getLineAttribute(),
+ getStrokeAttribute());
+ }
+ else
+ {
+ // start and/or end extensions lead to a lineStart/End that is *not*
+ // perpendicular to the line itself
+ if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ // without stroke, we can simply represent that using a filled polygon
+ const basegfx::B2DVector aHalfLineOffset(aPerpendicular * (candidate.getLineAttribute().getWidth() * 0.5));
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(aStart - aHalfLineOffset - (aVector * candidate.getStartLeft()));
+ aPolygon.append(aEnd - aHalfLineOffset + (aVector * candidate.getEndLeft()));
+ aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
+ aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
+
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ candidate.getLineAttribute().getColor()));
+ }
+ else
+ {
+ // with stroke, we have a problem - a filled polygon would lose the
+ // stroke. Let's represent the start and/or end as triangles, the main
+ // line still as PolygonStrokePrimitive2D.
+ // Fill default line Start/End for stroke, so we need no adaptations in else paths
+ basegfx::B2DPoint aStrokeStart(aStart - (aVector * candidate.getStartLeft()));
+ basegfx::B2DPoint aStrokeEnd(aEnd + (aVector * candidate.getEndLeft()));
+ const basegfx::B2DVector aHalfLineOffset(aPerpendicular * (candidate.getLineAttribute().getWidth() * 0.5));
+
+ if(!bStartPerpendicular)
+ {
+ const double fMin(std::min(candidate.getStartLeft(), candidate.getStartRight()));
+ const double fMax(std::max(candidate.getStartLeft(), candidate.getStartRight()));
+ basegfx::B2DPolygon aPolygon;
+
+ // create a triangle with min/max values for LineStart and add
+ if(rtl::math::approxEqual(candidate.getStartLeft(), fMax))
+ {
+ aPolygon.append(aStart - aHalfLineOffset - (aVector * candidate.getStartLeft()));
+ }
+
+ aPolygon.append(aStart - aHalfLineOffset - (aVector * fMin));
+ aPolygon.append(aStart + aHalfLineOffset - (aVector * fMin));
+
+ if(rtl::math::approxEqual(candidate.getStartRight(), fMax))
+ {
+ aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight()));
+ }
+
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ candidate.getLineAttribute().getColor()));
+
+ // Adapt StrokeStart accordingly
+ aStrokeStart = aStart - (aVector * fMin);
+ }
+
+ if(!bEndPerpendicular)
+ {
+ const double fMin(std::min(candidate.getEndLeft(), candidate.getEndRight()));
+ const double fMax(std::max(candidate.getEndLeft(), candidate.getEndRight()));
+ basegfx::B2DPolygon aPolygon;
+
+ // create a triangle with min/max values for LineEnd and add
+ if(rtl::math::approxEqual(candidate.getEndLeft(), fMax))
+ {
+ aPolygon.append(aEnd - aHalfLineOffset + (aVector * candidate.getEndLeft()));
+ }
+
+ if(rtl::math::approxEqual(candidate.getEndRight(), fMax))
+ {
+ aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight()));
+ }
+
+ aPolygon.append(aEnd + aHalfLineOffset + (aVector * fMin));
+ aPolygon.append(aEnd - aHalfLineOffset + (aVector * fMin));
+
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ candidate.getLineAttribute().getColor()));
+
+ // Adapt StrokeEnd accordingly
+ aStrokeEnd = aEnd + (aVector * fMin);
+ }
+
+ addPolygonStrokePrimitive2D(
+ rContainer,
+ aStrokeStart,
+ aStrokeEnd,
+ candidate.getLineAttribute(),
+ getStrokeAttribute());
+ }
+ }
+ }
+
+ fOffset += fWidth;
+ }
+ }
+
+ bool BorderLinePrimitive2D::isHorizontalOrVertical(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if (!getStart().equal(getEnd()))
+ {
+ const basegfx::B2DHomMatrix& rOTVT = rViewInformation.getObjectToViewTransformation();
+ const basegfx::B2DVector aVector(rOTVT * getEnd() - rOTVT * getStart());
+
+ return basegfx::fTools::equalZero(aVector.getX()) || basegfx::fTools::equalZero(aVector.getY());
+ }
+
+ return false;
+ }
+
+ BorderLinePrimitive2D::BorderLinePrimitive2D(
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ std::vector< BorderLine >&& rBorderLines,
+ const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute)
+ : maStart(rStart),
+ maEnd(rEnd),
+ maBorderLines(std::move(rBorderLines)),
+ maStrokeAttribute(rStrokeAttribute)
+ {
+ }
+
+ bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ return false;
+
+ const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive);
+
+ 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;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 BorderLinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D;
+ }
+
+ Primitive2DReference tryMergeBorderLinePrimitive2D(
+ const BorderLinePrimitive2D* pCandidateA,
+ const BorderLinePrimitive2D* pCandidateB)
+ {
+ assert(pCandidateA);
+ assert(pCandidateB);
+
+ // start of candidate has to match end of this
+ if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
+ {
+ return Primitive2DReference();
+ }
+
+ // candidate A needs a length
+ if(pCandidateA->getStart().equal(pCandidateA->getEnd()))
+ {
+ return Primitive2DReference();
+ }
+
+ // candidate B needs a length
+ if(pCandidateB->getStart().equal(pCandidateB->getEnd()))
+ {
+ return Primitive2DReference();
+ }
+
+ // StrokeAttribute has to be equal
+ if(!(pCandidateA->getStrokeAttribute() == pCandidateB->getStrokeAttribute()))
+ {
+ return Primitive2DReference();
+ }
+
+ // 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)))
+ {
+ return Primitive2DReference();
+ }
+
+ // number BorderLines has to be equal
+ const size_t count(pCandidateA->getBorderLines().size());
+ if(count != pCandidateB->getBorderLines().size())
+ {
+ return Primitive2DReference();
+ }
+
+ for(size_t a(0); a < count; a++)
+ {
+ const BorderLine& rBT(pCandidateA->getBorderLines()[a]);
+ const BorderLine& rBC(pCandidateB->getBorderLines()[a]);
+
+ // LineAttribute has to be the same
+ if(!(rBC.getLineAttribute() == rBT.getLineAttribute()))
+ {
+ return Primitive2DReference();
+ }
+
+ // isGap has to be the same
+ if(rBC.isGap() != rBT.isGap())
+ {
+ return Primitive2DReference();
+ }
+
+ if(rBT.isGap())
+ {
+ // when gap, width has to be equal
+ if(!rtl::math::approxEqual(rBT.getLineAttribute().getWidth(), rBC.getLineAttribute().getWidth()))
+ {
+ return Primitive2DReference();
+ }
+ }
+ else
+ {
+ // when not gap, the line extends have at least reach to the center ( > 0.0),
+ // else there is an extend usage. When > 0.0 they just overlap, no problem
+ if(rBT.getEndLeft() >= 0.0
+ && rBT.getEndRight() >= 0.0
+ && rBC.getStartLeft() >= 0.0
+ && rBC.getStartRight() >= 0.0)
+ {
+ // okay
+ }
+ else
+ {
+ return Primitive2DReference();
+ }
+ }
+ }
+
+ // all conditions met, create merged primitive
+ std::vector< BorderLine > aMergedBorderLines;
+
+ for(size_t a(0); a < count; a++)
+ {
+ const BorderLine& rBT(pCandidateA->getBorderLines()[a]);
+ const BorderLine& rBC(pCandidateB->getBorderLines()[a]);
+
+ if(rBT.isGap())
+ {
+ aMergedBorderLines.push_back(rBT);
+ }
+ else
+ {
+ aMergedBorderLines.push_back(
+ BorderLine(
+ rBT.getLineAttribute(),
+ rBT.getStartLeft(), rBT.getStartRight(),
+ rBC.getEndLeft(), rBC.getEndRight()));
+ }
+ }
+
+ return Primitive2DReference(
+ new BorderLinePrimitive2D(
+ pCandidateA->getStart(),
+ pCandidateB->getEnd(),
+ std::move(aMergedBorderLines),
+ pCandidateA->getStrokeAttribute()));
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/controlprimitive2d.cxx b/drawinglayer/source/primitive2d/controlprimitive2d.cxx
new file mode 100644
index 000000000..461400daf
--- /dev/null
+++ b/drawinglayer/source/primitive2d/controlprimitive2d.cxx
@@ -0,0 +1,348 @@
+/* -*- 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/controlprimitive2d.hxx>
+#include <com/sun/star/awt/XWindow.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 <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 <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.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>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ void ControlPrimitive2D::createXControl()
+ {
+ if(mxXControl.is() || !getControlModel().is())
+ return;
+
+ uno::Reference< beans::XPropertySet > xSet(getControlModel(), uno::UNO_QUERY);
+
+ if(!xSet.is())
+ return;
+
+ uno::Any aValue(xSet->getPropertyValue("DefaultControl"));
+ OUString aUnoControlTypeName;
+
+ if(!(aValue >>= aUnoControlTypeName))
+ return;
+
+ if(aUnoControlTypeName.isEmpty())
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< awt::XControl > xXControl(
+ xContext->getServiceManager()->createInstanceWithContext(aUnoControlTypeName, xContext), uno::UNO_QUERY);
+
+ if(xXControl.is())
+ {
+ xXControl->setModel(getControlModel());
+
+ // remember XControl
+ mxXControl = xXControl;
+ }
+ }
+
+ Primitive2DReference ControlPrimitive2D::createBitmapDecomposition(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DReference xRetval;
+ const uno::Reference< awt::XControl >& rXControl(getXControl());
+
+ if(rXControl.is())
+ {
+ uno::Reference< awt::XWindow > xControlWindow(rXControl, uno::UNO_QUERY);
+
+ if(xControlWindow.is())
+ {
+ // get decomposition to get size
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // get absolute discrete size (no mirror or rotate here)
+ aScale = basegfx::absolute(aScale);
+ basegfx::B2DVector aDiscreteSize(rViewInformation.getObjectToViewTransformation() * aScale);
+
+ // limit to a maximum square size, e.g. 300x150 pixels (45000)
+ const double fDiscreteMax(SvtOptionsDrawinglayer::GetQuadraticFormControlRenderLimit());
+ const double fDiscreteQuadratic(aDiscreteSize.getX() * aDiscreteSize.getY());
+ const bool bScaleUsed(fDiscreteQuadratic > fDiscreteMax);
+ double fFactor(1.0);
+
+ if(bScaleUsed)
+ {
+ // get factor and adapt to scaled size
+ fFactor = sqrt(fDiscreteMax / fDiscreteQuadratic);
+ aDiscreteSize *= fFactor;
+ }
+
+ // go to integer
+ const sal_Int32 nSizeX(basegfx::fround(aDiscreteSize.getX()));
+ const sal_Int32 nSizeY(basegfx::fround(aDiscreteSize.getY()));
+
+ if(nSizeX > 0 && nSizeY > 0)
+ {
+ // prepare VirtualDevice
+ ScopedVclPtrInstance< VirtualDevice > aVirtualDevice(*Application::GetDefaultDevice());
+ const Size aSizePixel(nSizeX, nSizeY);
+ aVirtualDevice->SetOutputSizePixel(aSizePixel);
+
+ // set size at control
+ xControlWindow->setPosSize(0, 0, nSizeX, nSizeY, awt::PosSize::POSSIZE);
+
+ // get graphics and view
+ uno::Reference< awt::XGraphics > xGraphics(aVirtualDevice->CreateUnoGraphics());
+ uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY);
+
+ if(xGraphics.is() && xControlView.is())
+ {
+ // link graphics and view
+ xControlView->setGraphics(xGraphics);
+
+ { // #i93162# For painting the control setting a Zoom (using setZoom() at the xControlView)
+ // is needed to define the font size. Normally this is done in
+ // ViewObjectContactOfUnoControl::createPrimitive2DSequence by using positionControlForPaint().
+ // For some reason the difference between MapUnit::MapTwipS and MapUnit::Map100thMM still plays
+ // a role there so that for Draw/Impress/Calc (the MapUnit::Map100thMM users) i need to set a zoom
+ // here, too. The factor includes the needed scale, but is calculated by pure comparisons. It
+ // is somehow related to the twips/100thmm relationship.
+ bool bUserIs100thmm(false);
+ const uno::Reference< awt::XControl > xControl(xControlView, uno::UNO_QUERY);
+
+ if(xControl.is())
+ {
+ uno::Reference<awt::XWindowPeer> xWindowPeer(xControl->getPeer());
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindowPeer);
+ if (pWindow)
+ {
+ pWindow = pWindow->GetParent();
+
+ if(pWindow && MapUnit::Map100thMM == pWindow->GetMapMode().GetMapUnit())
+ {
+ bUserIs100thmm = true;
+ }
+ }
+ }
+
+ if(bUserIs100thmm)
+ {
+ // calc screen zoom for text display. fFactor is already added indirectly in aDiscreteSize
+ basegfx::B2DVector aScreenZoom(
+ basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : aDiscreteSize.getX() / aScale.getX(),
+ basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : aDiscreteSize.getY() / aScale.getY());
+ static const double fZoomScale(28.0); // do not ask for this constant factor, but it gets the zoom right
+ aScreenZoom *= fZoomScale;
+
+ // set zoom at control view for text scaling
+ xControlView->setZoom(static_cast<float>(aScreenZoom.getX()), static_cast<float>(aScreenZoom.getY()));
+ }
+ }
+
+ try
+ {
+ // try to paint it to VirtualDevice
+ xControlView->draw(0, 0);
+
+ // get bitmap
+ const BitmapEx aContent(aVirtualDevice->GetBitmapEx(Point(), aSizePixel));
+
+ // to avoid scaling, use the Bitmap pixel size as primitive size
+ const Size aBitmapSize(aContent.GetSizePixel());
+ basegfx::B2DVector aBitmapSizeLogic(
+ rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1));
+
+ if(bScaleUsed)
+ {
+ // if scaled adapt to scaled size
+ aBitmapSizeLogic /= fFactor;
+ }
+
+ // short form for scale and translate transformation
+ const basegfx::B2DHomMatrix aBitmapTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aBitmapSizeLogic.getX(), aBitmapSizeLogic.getY(), aTranslate.getX(), aTranslate.getY()));
+
+ // create primitive
+ xRetval = new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aContent),
+ aBitmapTransform);
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("drawinglayer");
+ }
+ }
+ }
+ }
+ }
+
+ return xRetval;
+ }
+
+ Primitive2DReference ControlPrimitive2D::createPlaceholderDecomposition() const
+ {
+ // 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));
+ 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));
+
+ return xRetval;
+ }
+
+ void ControlPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // try to create a bitmap decomposition. If that fails for some reason,
+ // at least create a replacement decomposition.
+ Primitive2DReference xReference(createBitmapDecomposition(rViewInformation));
+
+ if(!xReference.is())
+ {
+ xReference = createPlaceholderDecomposition();
+ }
+
+ rContainer.push_back(xReference);
+ }
+
+ ControlPrimitive2D::ControlPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const uno::Reference< awt::XControlModel >& rxControlModel)
+ : maTransform(rTransform),
+ mxControlModel(rxControlModel)
+ {
+ }
+
+ ControlPrimitive2D::ControlPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const uno::Reference< awt::XControlModel >& rxControlModel,
+ const uno::Reference< awt::XControl >& rxXControl)
+ : maTransform(rTransform),
+ mxControlModel(rxControlModel),
+ mxXControl(rxXControl)
+ {
+ }
+
+ const uno::Reference< awt::XControl >& ControlPrimitive2D::getXControl() const
+ {
+ if(!mxXControl.is())
+ {
+ const_cast< ControlPrimitive2D* >(this)->createXControl();
+ }
+
+ return mxXControl;
+ }
+
+ bool ControlPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ // use base class compare operator
+ if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ return false;
+
+ const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive);
+
+ if(getTransform() != rCompare.getTransform())
+ return false;
+
+ // check if ControlModel references both are/are not
+ if (getControlModel().is() != rCompare.getControlModel().is())
+ return false;
+
+ if(getControlModel().is())
+ {
+ // both exist, check for equality
+ if (getControlModel() != rCompare.getControlModel())
+ return false;
+ }
+
+ // 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 true;
+ }
+
+ basegfx::B2DRange ControlPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // simply derivate from unit range
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+ return aRetval;
+ }
+
+ void ControlPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // 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
+ const basegfx::B2DVector aNewScaling(rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+
+ if(!getBuffered2DDecomposition().empty())
+ {
+ if(!maLastViewScaling.equal(aNewScaling))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewTransformation
+ const_cast< ControlPrimitive2D* >(this)->maLastViewScaling = aNewScaling;
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 ControlPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_CONTROLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/cropprimitive2d.cxx b/drawinglayer/source/primitive2d/cropprimitive2d.cxx
new file mode 100644
index 000000000..76393ad05
--- /dev/null
+++ b/drawinglayer/source/primitive2d/cropprimitive2d.cxx
@@ -0,0 +1,155 @@
+/* -*- 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 <primitive2d/cropprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ CropPrimitive2D::CropPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const basegfx::B2DHomMatrix& rTransformation,
+ double fCropLeft,
+ double fCropTop,
+ double fCropRight,
+ double fCropBottom)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maTransformation(rTransformation),
+ mfCropLeft(fCropLeft),
+ mfCropTop(fCropTop),
+ mfCropRight(fCropRight),
+ mfCropBottom(fCropBottom)
+ {
+ }
+
+ bool CropPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const CropPrimitive2D& rCompare = static_cast< const CropPrimitive2D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation()
+ && getCropLeft() == rCompare.getCropLeft()
+ && getCropTop() == rCompare.getCropTop()
+ && getCropRight() == rCompare.getCropRight()
+ && getCropBottom() == rCompare.getCropBottom());
+ }
+
+ return false;
+ }
+
+ void CropPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getChildren().empty())
+ return;
+
+ // get original object scale in unit coordinates (no mirroring)
+ const basegfx::B2DVector aObjectScale(basegfx::absolute(getTransformation() * basegfx::B2DVector(1.0, 1.0)));
+
+ // we handle cropping, so when no width or no height, content will be empty,
+ // so only do something when we have a width and a height
+ if(aObjectScale.equalZero())
+ return;
+
+ // calculate crop distances in unit coordinates. They are already combined with CropScaleFactor, thus
+ // are relative only to object scale
+ const double fBackScaleX(basegfx::fTools::equalZero(aObjectScale.getX()) ? 1.0 : 1.0 / fabs(aObjectScale.getX()));
+ const double fBackScaleY(basegfx::fTools::equalZero(aObjectScale.getY()) ? 1.0 : 1.0 / fabs(aObjectScale.getY()));
+ const double fLeft(getCropLeft() * fBackScaleX);
+ const double fTop(getCropTop() * fBackScaleY);
+ const double fRight(getCropRight() * fBackScaleX);
+ const double fBottom(getCropBottom() * fBackScaleY);
+
+ // calc new unit range for comparisons; the original range is the unit range
+ const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ const basegfx::B2DRange aNewRange(
+ -fLeft,
+ -fTop,
+ 1.0 + fRight,
+ 1.0 + fBottom);
+
+ // if we have no overlap the crop has removed everything, so we do only
+ // have to create content if this is not the case
+ if(!aNewRange.overlaps(aUnitRange))
+ return;
+
+ // create new transform; first take out old transform to get
+ // to unit coordinates by inverting. Inverting should be flawless
+ // since we already checked that object size is not zero in X or Y
+ basegfx::B2DHomMatrix aNewTransform(getTransformation());
+
+ aNewTransform.invert();
+
+ // apply crop enlargement in unit coordinates
+ aNewTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aNewRange.getRange(),
+ aNewRange.getMinimum()) * aNewTransform;
+
+ // apply original transformation. Since we have manipulated the crop
+ // in unit coordinates we do not need to care about mirroring or
+ // a corrected point for a possible shear or rotation, this all comes for
+ // free
+ aNewTransform = getTransformation() * aNewTransform;
+
+ // prepare TransformPrimitive2D with xPrimitive
+ const Primitive2DReference xTransformPrimitive(
+ new TransformPrimitive2D(
+ aNewTransform,
+ Primitive2DContainer(getChildren())));
+
+ if(aUnitRange.isInside(aNewRange))
+ {
+ // the new range is completely inside the old range (unit range),
+ // so no masking is needed
+ rVisitor.visit(xTransformPrimitive);
+ }
+ else
+ {
+ // mask with original object's bounds
+ basegfx::B2DPolyPolygon aMaskPolyPolygon(basegfx::utils::createUnitPolygon());
+ aMaskPolyPolygon.transform(getTransformation());
+
+ // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector
+ const Primitive2DReference xMask(
+ new MaskPrimitive2D(
+ aMaskPolyPolygon,
+ Primitive2DContainer { xTransformPrimitive }));
+
+ rVisitor.visit(xMask);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 CropPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_CROPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
new file mode 100644
index 000000000..8a86f8320
--- /dev/null
+++ b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx
@@ -0,0 +1,102 @@
+/* -*- 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/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void DiscreteBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use getViewTransformation() and getObjectTransformation() from
+ // ObjectAndViewTransformationDependentPrimitive2D to create a BitmapPrimitive2D
+ // with the correct mapping
+
+ if(getBitmapEx().IsEmpty())
+ return;
+
+ // get discrete size
+ const Size& rSizePixel = getBitmapEx().GetSizePixel();
+ const basegfx::B2DVector aDiscreteSize(rSizePixel.Width(), rSizePixel.Height());
+
+ // get inverse ViewTransformation
+ basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation());
+ aInverseViewTransformation.invert();
+
+ // get size and position in world coordinates
+ const basegfx::B2DVector aWorldSize(aInverseViewTransformation * aDiscreteSize);
+ const basegfx::B2DPoint aWorldTopLeft(getObjectTransformation() * getTopLeft());
+
+ // build object matrix in world coordinates so that the top-left
+ // position remains, but possible transformations (e.g. rotations)
+ // in the ObjectToView stack remain and get correctly applied
+ basegfx::B2DHomMatrix aObjectTransform;
+
+ aObjectTransform.set(0, 0, aWorldSize.getX());
+ aObjectTransform.set(1, 1, aWorldSize.getY());
+ aObjectTransform.set(0, 2, aWorldTopLeft.getX());
+ aObjectTransform.set(1, 2, aWorldTopLeft.getY());
+
+ // get inverse ObjectTransformation
+ basegfx::B2DHomMatrix aInverseObjectTransformation(getObjectTransformation());
+ aInverseObjectTransformation.invert();
+
+ // transform to object coordinate system
+ aObjectTransform = aInverseObjectTransformation * aObjectTransform;
+
+ // create BitmapPrimitive2D with now object-local coordinate data
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
+ aObjectTransform));
+ }
+
+ DiscreteBitmapPrimitive2D::DiscreteBitmapPrimitive2D(
+ const BitmapEx& rBitmapEx,
+ const basegfx::B2DPoint& rTopLeft)
+ : maBitmapEx(rBitmapEx),
+ maTopLeft(rTopLeft)
+ {
+ }
+
+ bool DiscreteBitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(ObjectAndViewTransformationDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const DiscreteBitmapPrimitive2D& rCompare = static_cast<const DiscreteBitmapPrimitive2D&>(rPrimitive);
+
+ return (getBitmapEx() == rCompare.getBitmapEx()
+ && getTopLeft() == rCompare.getTopLeft());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 DiscreteBitmapPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
new file mode 100644
index 000000000..290d9d2d7
--- /dev/null
+++ b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx
@@ -0,0 +1,311 @@
+/* -*- 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/discreteshadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <osl/diagnose.h>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ DiscreteShadow::DiscreteShadow(const BitmapEx& rBitmapEx)
+ : maBitmapEx(rBitmapEx)
+ {
+ const Size& rBitmapSize = getBitmapEx().GetSizePixel();
+
+ if(rBitmapSize.Width() != rBitmapSize.Height() || rBitmapSize.Width() < 7)
+ {
+ OSL_ENSURE(false, "DiscreteShadowPrimitive2D: wrong bitmap format (!)");
+ maBitmapEx = BitmapEx();
+ }
+ }
+
+ const BitmapEx& DiscreteShadow::getTopLeft() const
+ {
+ if(maTopLeft.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maTopLeft = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maTopLeft.Crop(
+ ::tools::Rectangle(Point(0, 0), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maTopLeft;
+ }
+
+ const BitmapEx& DiscreteShadow::getTop() const
+ {
+ if(maTop.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maTop = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maTop.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 1, 0), Size(1, nQuarter)));
+ }
+
+ return maTop;
+ }
+
+ const BitmapEx& DiscreteShadow::getTopRight() const
+ {
+ if(maTopRight.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maTopRight = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maTopRight.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 2, 0), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maTopRight;
+ }
+
+ const BitmapEx& DiscreteShadow::getRight() const
+ {
+ if(maRight.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maRight = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maRight.Crop(
+ ::tools::Rectangle(Point((nQuarter * 3) + 3, (nQuarter * 2) + 1), Size(nQuarter, 1)));
+ }
+
+ return maRight;
+ }
+
+ const BitmapEx& DiscreteShadow::getBottomRight() const
+ {
+ if(maBottomRight.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maBottomRight = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maBottomRight.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 2, (nQuarter * 2) + 2), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maBottomRight;
+ }
+
+ const BitmapEx& DiscreteShadow::getBottom() const
+ {
+ if(maBottom.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maBottom = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maBottom.Crop(
+ ::tools::Rectangle(Point((nQuarter * 2) + 1, (nQuarter * 3) + 3), Size(1, nQuarter)));
+ }
+
+ return maBottom;
+ }
+
+ const BitmapEx& DiscreteShadow::getBottomLeft() const
+ {
+ if(maBottomLeft.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maBottomLeft = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maBottomLeft.Crop(
+ ::tools::Rectangle(Point(0, (nQuarter * 2) + 2), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1)));
+ }
+
+ return maBottomLeft;
+ }
+
+ const BitmapEx& DiscreteShadow::getLeft() const
+ {
+ if(maLeft.IsEmpty())
+ {
+ const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const_cast< DiscreteShadow* >(this)->maLeft = getBitmapEx();
+ const_cast< DiscreteShadow* >(this)->maLeft.Crop(
+ ::tools::Rectangle(Point(0, (nQuarter * 2) + 1), Size(nQuarter, 1)));
+ }
+
+ return maLeft;
+ }
+
+} // end of namespace
+
+
+namespace drawinglayer::primitive2d
+{
+ void DiscreteShadowPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ Primitive2DContainer xRetval;
+
+ if(getDiscreteShadow().getBitmapEx().IsEmpty())
+ return;
+
+ const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const basegfx::B2DVector aScale(getTransform() * basegfx::B2DVector(1.0, 1.0));
+ const double fSingleX(getDiscreteUnit() / aScale.getX());
+ const double fSingleY(getDiscreteUnit() / aScale.getY());
+ const double fBorderX(fSingleX * nQuarter);
+ const double fBorderY(fSingleY * nQuarter);
+ const double fBigLenX((fBorderX * 2.0) + fSingleX);
+ const double fBigLenY((fBorderY * 2.0) + fSingleY);
+
+ xRetval.resize(8);
+
+ // TopLeft
+ xRetval[0] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopLeft()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ -fBorderX,
+ -fBorderY)));
+
+ // Top
+ xRetval[1] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTop()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ 1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX,
+ fBorderY,
+ fBorderX + fSingleX,
+ -fBorderY)));
+
+ // TopRight
+ xRetval[2] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopRight()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ 1.0 - fBorderX,
+ -fBorderY)));
+
+ // Right
+ xRetval[3] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getRight()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBorderX,
+ 1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY,
+ 1.0 + fSingleX,
+ fBorderY + fSingleY)));
+
+ // BottomRight
+ xRetval[4] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomRight()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ 1.0 - (fBorderX + fSingleX) + fSingleX,
+ 1.0 - (fBorderY + fSingleY) + fSingleY)));
+
+ // Bottom
+ xRetval[5] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottom()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ 1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX,
+ fBorderY,
+ fBorderX + fSingleX,
+ 1.0 + fSingleY)));
+
+ // BottomLeft
+ xRetval[6] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomLeft()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBigLenX,
+ fBigLenY,
+ -fBorderX,
+ 1.0 - fBorderY)));
+
+ // Left
+ xRetval[7] = Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getLeft()),
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fBorderX,
+ 1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY,
+ -fBorderX,
+ fBorderY + fSingleY)));
+
+ // put all in object transformation to get to target positions
+ rContainer.push_back(
+ new TransformPrimitive2D(
+ getTransform(),
+ std::move(xRetval)));
+ }
+
+ DiscreteShadowPrimitive2D::DiscreteShadowPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const DiscreteShadow& rDiscreteShadow)
+ : maTransform(rTransform),
+ maDiscreteShadow(rDiscreteShadow)
+ {
+ }
+
+ bool DiscreteShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const DiscreteShadowPrimitive2D& rCompare = static_cast<const DiscreteShadowPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getDiscreteShadow() == rCompare.getDiscreteShadow());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange DiscreteShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(getDiscreteShadow().getBitmapEx().IsEmpty())
+ {
+ // no graphics without valid bitmap definition
+ return basegfx::B2DRange();
+ }
+ else
+ {
+ // prepare normal objectrange
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ // extract discrete shadow size and grow
+ const basegfx::B2DVector aScale(rViewInformation.getViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+ const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2);
+ const double fGrowX((1.0 / aScale.getX()) * nQuarter);
+ const double fGrowY((1.0 / aScale.getY()) * nQuarter);
+ aRetval.grow(std::max(fGrowX, fGrowY));
+
+ return aRetval;
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 DiscreteShadowPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
new file mode 100644
index 000000000..f072df0c3
--- /dev/null
+++ b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx
@@ -0,0 +1,146 @@
+/* -*- 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/embedded3dprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/color/bcolor.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>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ bool Embedded3DPrimitive2D::impGetShadow3D() const
+ {
+ // create on demand
+ if(!mbShadow3DChecked && !getChildren3D().empty())
+ {
+ // create shadow extraction processor
+ processor3d::Shadow3DExtractingProcessor aShadowProcessor(
+ getViewInformation3D(),
+ getObjectTransformation(),
+ getLightNormal(),
+ getShadowSlant(),
+ getScene3DRange());
+
+ // process local primitives
+ aShadowProcessor.process(getChildren3D());
+
+ // fetch result and set checked flag
+ const_cast< Embedded3DPrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
+ const_cast< Embedded3DPrimitive2D* >(this)->mbShadow3DChecked = true;
+ }
+
+ // return if there are shadow primitives
+ return !maShadowPrimitives.empty();
+ }
+
+ void Embedded3DPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, 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));
+ const basegfx::BColor aYellow(1.0, 1.0, 0.0);
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, aYellow));
+ }
+
+ Embedded3DPrimitive2D::Embedded3DPrimitive2D(
+ const primitive3d::Primitive3DContainer& rxChildren3D,
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ const geometry::ViewInformation3D& rViewInformation3D,
+ const basegfx::B3DVector& rLightNormal,
+ double fShadowSlant,
+ const basegfx::B3DRange& rScene3DRange)
+ : mxChildren3D(rxChildren3D),
+ maObjectTransformation(rObjectTransformation),
+ maViewInformation3D(rViewInformation3D),
+ maLightNormal(rLightNormal),
+ mfShadowSlant(fShadowSlant),
+ maScene3DRange(rScene3DRange),
+ mbShadow3DChecked(false)
+ {
+ maLightNormal.normalize();
+ }
+
+ bool Embedded3DPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const Embedded3DPrimitive2D& rCompare = static_cast< const Embedded3DPrimitive2D& >(rPrimitive);
+
+ return (getChildren3D() == rCompare.getChildren3D()
+ && getObjectTransformation() == rCompare.getObjectTransformation()
+ && getViewInformation3D() == rCompare.getViewInformation3D()
+ && getLightNormal() == rCompare.getLightNormal()
+ && getShadowSlant() == rCompare.getShadowSlant()
+ && getScene3DRange() == rCompare.getScene3DRange());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange Embedded3DPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(maB2DRange.isEmpty())
+ {
+ // use the 3d transformation stack to create a projection of the 3D range
+ basegfx::B3DRange a3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
+ a3DRange.transform(getViewInformation3D().getObjectToView());
+
+ // create 2d range from projected 3d and transform with scene's object transformation
+ basegfx::B2DRange aNewRange;
+ aNewRange.expand(basegfx::B2DPoint(a3DRange.getMinX(), a3DRange.getMinY()));
+ aNewRange.expand(basegfx::B2DPoint(a3DRange.getMaxX(), a3DRange.getMaxY()));
+ aNewRange.transform(getObjectTransformation());
+
+ // check for 3D shadows and their 2D projections. If those exist, they need to be
+ // taken into account
+ if(impGetShadow3D())
+ {
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+
+ if(!aShadow2DRange.isEmpty())
+ {
+ aNewRange.expand(aShadow2DRange);
+ }
+ }
+
+ // assign to buffered value
+ const_cast< Embedded3DPrimitive2D* >(this)->maB2DRange = aNewRange;
+ }
+
+ return maB2DRange;
+ }
+
+ // provide unique ID
+ sal_uInt32 Embedded3DPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/epsprimitive2d.cxx b/drawinglayer/source/primitive2d/epsprimitive2d.cxx
new file mode 100644
index 000000000..9e876dad5
--- /dev/null
+++ b/drawinglayer/source/primitive2d/epsprimitive2d.cxx
@@ -0,0 +1,84 @@
+/* -*- 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/epsprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+ void EpsPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const GDIMetaFile& rSubstituteContent = getMetaFile();
+
+ if( rSubstituteContent.GetActionSize() )
+ {
+ // the default decomposition will use the Metafile replacement visualisation.
+ // To really use the Eps data, a renderer has to know and interpret this primitive
+ // directly.
+
+ rContainer.push_back(
+ new MetafilePrimitive2D(
+ getEpsTransform(),
+ rSubstituteContent));
+ }
+ }
+
+ EpsPrimitive2D::EpsPrimitive2D(
+ const basegfx::B2DHomMatrix& rEpsTransform,
+ const GfxLink& rGfxLink,
+ const GDIMetaFile& rMetaFile)
+ : maEpsTransform(rEpsTransform),
+ maGfxLink(rGfxLink),
+ maMetaFile(rMetaFile)
+ {
+ }
+
+ bool EpsPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const EpsPrimitive2D& rCompare = static_cast<const EpsPrimitive2D&>(rPrimitive);
+
+ return (getEpsTransform() == rCompare.getEpsTransform()
+ && getGfxLink() == rCompare.getGfxLink()
+ && getMetaFile() == rCompare.getMetaFile());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange EpsPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use own implementation to quickly answer the getB2DRange question.
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getEpsTransform());
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 EpsPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_EPSPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
new file mode 100644
index 000000000..7baf93bf5
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx
@@ -0,0 +1,334 @@
+/* -*- 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/fillgradientprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void FillGradientPrimitive2D::generateMatricesAndColors(
+ std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
+ basegfx::BColor& rOuterColor) 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(nSteps == 0)
+ {
+ nSteps = nMaxSteps;
+ }
+
+ if(nSteps < 2)
+ {
+ nSteps = 2;
+ }
+
+ if(nSteps > nMaxSteps)
+ {
+ nSteps = nMaxSteps;
+ }
+
+ nSteps = std::max(sal_uInt32(1), nSteps);
+
+ switch(getFillGradient().getStyle())
+ {
+ case attribute::GradientStyle::Linear:
+ {
+ texture::GeoTexSvxGradientLinear aGradient(
+ getDefinitionRange(),
+ getOutputRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Axial:
+ {
+ texture::GeoTexSvxGradientAxial aGradient(
+ getDefinitionRange(),
+ getOutputRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Radial:
+ {
+ texture::GeoTexSvxGradientRadial aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Elliptical:
+ {
+ texture::GeoTexSvxGradientElliptical aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Square:
+ {
+ texture::GeoTexSvxGradientSquare aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ break;
+ }
+ case attribute::GradientStyle::Rect:
+ {
+ texture::GeoTexSvxGradientRect aGradient(
+ getDefinitionRange(),
+ aStart,
+ aEnd,
+ nSteps,
+ getFillGradient().getBorder(),
+ getFillGradient().getOffsetX(),
+ getFillGradient().getOffsetY(),
+ getFillGradient().getAngle());
+ aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
+ 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
+ {
+ // 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++)
+ {
+ // 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(
+ new PolyPolygonColorPrimitive2D(
+ aCombinedPolyPoly,
+ rEntries[a].maBColor));
+
+ // reuse inner polygon, it's the 2nd one
+ aCombinedPolyPoly.remove(0);
+ }
+
+ // 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())
+ {
+ case attribute::GradientStyle::Radial:
+ case attribute::GradientStyle::Elliptical:
+ {
+ aUnitPolygon = basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
+ break;
+ }
+ default: // GradientStyle::Linear, attribute::GradientStyle::Axial, attribute::GradientStyle::Square, attribute::GradientStyle::Rect
+ {
+ aUnitPolygon = basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
+ break;
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ void FillGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, 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
+ // PolyPolygons where each one describes a 'ring' for the gradient such
+ // that the rings will not overlap. This is useful for the old XOR-paint
+ // 'trick' of VCL which is recorded in Metafiles; so this version may be
+ // used from the MetafilePrimitive2D in its decomposition.
+
+ if(!getFillGradient().isDefault())
+ {
+ createFill(rContainer, /*bOverlapping*/true);
+ }
+ }
+
+ FillGradientPrimitive2D::FillGradientPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rOutputRange),
+ maFillGradient(rFillGradient)
+ {
+ }
+
+ FillGradientPrimitive2D::FillGradientPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::B2DRange& rDefinitionRange,
+ const attribute::FillGradientAttribute& rFillGradient)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rDefinitionRange),
+ maFillGradient(rFillGradient)
+ {
+ }
+
+ bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const FillGradientPrimitive2D& rCompare = static_cast<const FillGradientPrimitive2D&>(rPrimitive);
+
+ return (getOutputRange() == rCompare.getOutputRange()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillGradient() == rCompare.getFillGradient());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return the geometrically visible area
+ return getOutputRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 FillGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
new file mode 100644
index 000000000..0f386eea2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
@@ -0,0 +1,131 @@
+/* -*- 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/fillgraphicprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <texture/texture.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <vcl/graph.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void FillGraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const attribute::FillGraphicAttribute& rAttribute = getFillGraphic();
+
+ if(rAttribute.isDefault())
+ return;
+
+ const Graphic& rGraphic = rAttribute.getGraphic();
+
+ if(GraphicType::Bitmap != rGraphic.GetType() && GraphicType::GdiMetafile != rGraphic.GetType())
+ return;
+
+ const Size aSize(rGraphic.GetPrefSize());
+
+ if(!(aSize.Width() && aSize.Height()))
+ return;
+
+ // we have a graphic (bitmap or metafile) with some size
+ if(rAttribute.getTiling())
+ {
+ // get object range and create tiling matrices
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+ texture::GeoTexSvxTiled aTiling(
+ rAttribute.getGraphicRange(),
+ rAttribute.getOffsetX(),
+ rAttribute.getOffsetY());
+
+ // get matrices and realloc retval
+ aTiling.appendTransformations(aMatrices);
+
+ // prepare content primitive
+ Primitive2DContainer xSeq;
+ create2DDecompositionOfGraphic(xSeq,
+ rGraphic,
+ basegfx::B2DHomMatrix());
+
+ for(const auto &a : aMatrices)
+ {
+ rContainer.push_back(new TransformPrimitive2D(
+ getTransformation() * a,
+ Primitive2DContainer(xSeq)));
+ }
+ }
+ else
+ {
+ // add graphic without tiling
+ const basegfx::B2DHomMatrix aObjectTransform(
+ getTransformation() * basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rAttribute.getGraphicRange().getRange(),
+ rAttribute.getGraphicRange().getMinimum()));
+
+ create2DDecompositionOfGraphic(rContainer,
+ rGraphic,
+ aObjectTransform);
+ }
+ }
+
+ FillGraphicPrimitive2D::FillGraphicPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransformation,
+ const attribute::FillGraphicAttribute& rFillGraphic)
+ : maTransformation(rTransformation),
+ maFillGraphic(rFillGraphic)
+ {
+ }
+
+ bool FillGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const FillGraphicPrimitive2D& rCompare = static_cast< const FillGraphicPrimitive2D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation()
+ && getFillGraphic() == rCompare.getFillGraphic());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillGraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return range of it
+ basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon());
+ aPolygon.transform(getTransformation());
+
+ return basegfx::utils::getRange(aPolygon);
+ }
+
+ // provide unique ID
+ sal_uInt32 FillGraphicPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
new file mode 100644
index 000000000..09a82bdd8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx
@@ -0,0 +1,198 @@
+/* -*- 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/fillhatchprimitive2d.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void FillHatchPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getFillHatch().isDefault())
+ return;
+
+ // create hatch
+ const basegfx::BColor aHatchColor(getFillHatch().getColor());
+ const double fAngle(getFillHatch().getAngle());
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+ double fDistance(getFillHatch().getDistance());
+ const bool bAdaptDistance(0 != getFillHatch().getMinimalDiscreteDistance());
+
+ // #i120230# evtl. adapt distance
+ if(bAdaptDistance)
+ {
+ const double fDiscreteDistance(getFillHatch().getDistance() / getDiscreteUnit());
+
+ if(fDiscreteDistance < static_cast<double>(getFillHatch().getMinimalDiscreteDistance()))
+ {
+ fDistance = static_cast<double>(getFillHatch().getMinimalDiscreteDistance()) * getDiscreteUnit();
+ }
+ }
+
+ // get hatch transformations
+ switch(getFillHatch().getStyle())
+ {
+ case attribute::HatchStyle::Triple:
+ {
+ // rotated 45 degrees
+ texture::GeoTexSvxHatch aHatch(
+ getDefinitionRange(),
+ getOutputRange(),
+ fDistance,
+ fAngle - M_PI_4);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Double:
+ {
+ // rotated 90 degrees
+ texture::GeoTexSvxHatch aHatch(
+ getDefinitionRange(),
+ getOutputRange(),
+ fDistance,
+ fAngle - M_PI_2);
+
+ aHatch.appendTransformations(aMatrices);
+
+ [[fallthrough]];
+ }
+ case attribute::HatchStyle::Single:
+ {
+ // angle as given
+ texture::GeoTexSvxHatch aHatch(
+ getDefinitionRange(),
+ getOutputRange(),
+ fDistance,
+ fAngle);
+
+ aHatch.appendTransformations(aMatrices);
+ }
+ }
+
+ // prepare return value
+ const bool bFillBackground(getFillHatch().isFillBackground());
+
+ // evtl. create filled background
+ if(bFillBackground)
+ {
+ // create primitive for background
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(getOutputRange())), getBColor()));
+ }
+
+ // create primitives
+ const basegfx::B2DPoint aStart(0.0, 0.0);
+ const basegfx::B2DPoint aEnd(1.0, 0.0);
+
+ for (const auto &a : aMatrices)
+ {
+ const basegfx::B2DHomMatrix& rMatrix = a;
+ basegfx::B2DPolygon aNewLine;
+
+ aNewLine.append(rMatrix * aStart);
+ aNewLine.append(rMatrix * aEnd);
+
+ // create hairline
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aNewLine, aHatchColor));
+ }
+ }
+
+ FillHatchPrimitive2D::FillHatchPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::BColor& rBColor,
+ const attribute::FillHatchAttribute& rFillHatch)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rOutputRange),
+ maFillHatch(rFillHatch),
+ maBColor(rBColor)
+ {
+ }
+
+ FillHatchPrimitive2D::FillHatchPrimitive2D(
+ const basegfx::B2DRange& rOutputRange,
+ const basegfx::B2DRange& rDefinitionRange,
+ const basegfx::BColor& rBColor,
+ const attribute::FillHatchAttribute& rFillHatch)
+ : maOutputRange(rOutputRange),
+ maDefinitionRange(rDefinitionRange),
+ maFillHatch(rFillHatch),
+ maBColor(rBColor)
+ {
+ }
+
+ bool FillHatchPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const FillHatchPrimitive2D& rCompare = static_cast<const FillHatchPrimitive2D&>(rPrimitive);
+
+ return (getOutputRange() == rCompare.getOutputRange()
+ && getDefinitionRange() == rCompare.getDefinitionRange()
+ && getFillHatch() == rCompare.getFillHatch()
+ && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange FillHatchPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return the geometrically visible area
+ return getOutputRange();
+ }
+
+ void FillHatchPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ bool bAdaptDistance(0 != getFillHatch().getMinimalDiscreteDistance());
+
+ if(bAdaptDistance)
+ {
+ // behave view-dependent
+ DiscreteMetricDependentPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+ else
+ {
+ // behave view-independent
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 FillHatchPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
new file mode 100644
index 000000000..6fe14c222
--- /dev/null
+++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx
@@ -0,0 +1,62 @@
+/* -*- 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/glowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, double fRadius,
+ Primitive2DContainer&& rChildren)
+ : GroupPrimitive2D(std::move(rChildren))
+ , maGlowColor(rGlowColor)
+ , mfGlowRadius(fRadius)
+{
+}
+
+bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const GlowPrimitive2D& rCompare = static_cast<const GlowPrimitive2D&>(rPrimitive);
+
+ return (getGlowRadius() == rCompare.getGlowRadius()
+ && getGlowColor() == rCompare.getGlowColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+GlowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval(GroupPrimitive2D::getB2DRange(rViewInformation));
+ // We need additional space for the glow from all sides
+ aRetval.grow(getGlowRadius());
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 GlowPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_GLOWPRIMITIVE2D; }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
new file mode 100644
index 000000000..4cd7a4963
--- /dev/null
+++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx
@@ -0,0 +1,219 @@
+/* -*- 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 <algorithm>
+
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <primitive2d/cropprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+namespace drawinglayer::primitive2d
+{
+void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D&) const
+{
+ if (0 == getGraphicAttr().GetAlpha())
+ {
+ // content is invisible, done
+ return;
+ }
+
+ // do not apply mirroring from GraphicAttr to the Metafile by calling
+ // GetTransformedGraphic, this will try to mirror the Metafile using Scale()
+ // at the Metafile. This again calls Scale at the single MetaFile actions,
+ // but this implementation never worked. I reworked that implementations,
+ // but for security reasons i will try not to use it.
+ basegfx::B2DHomMatrix aTransform(getTransform());
+
+ if (getGraphicAttr().IsMirrored())
+ {
+ // content needs mirroring
+ const bool bHMirr(getGraphicAttr().GetMirrorFlags() & BmpMirrorFlags::Horizontal);
+ const bool bVMirr(getGraphicAttr().GetMirrorFlags() & BmpMirrorFlags::Vertical);
+
+ // mirror by applying negative scale to the unit primitive and
+ // applying the object transformation on it.
+ aTransform
+ = basegfx::utils::createScaleB2DHomMatrix(bHMirr ? -1.0 : 1.0, bVMirr ? -1.0 : 1.0);
+ aTransform.translate(bHMirr ? 1.0 : 0.0, bVMirr ? 1.0 : 0.0);
+ aTransform = getTransform() * aTransform;
+ }
+
+ // Get transformed graphic. Suppress rotation and cropping, only filtering is needed
+ // here (and may be replaced later on). Cropping is handled below as mask primitive (if set).
+ // Also need to suppress mirroring, it is part of the transformation now (see above).
+ // Also move transparency handling to embedding to a UnifiedTransparencePrimitive2D; do
+ // that by remembering original transparency and applying that later if needed
+ GraphicAttr aSuppressGraphicAttr(getGraphicAttr());
+
+ aSuppressGraphicAttr.SetCrop(0, 0, 0, 0);
+ aSuppressGraphicAttr.SetRotation(0_deg10);
+ aSuppressGraphicAttr.SetMirrorFlags(BmpMirrorFlags::NONE);
+ aSuppressGraphicAttr.SetAlpha(255);
+
+ const GraphicObject& rGraphicObject = getGraphicObject();
+ Graphic aTransformedGraphic(rGraphicObject.GetGraphic());
+ const bool isBitmap(GraphicType::Bitmap == aTransformedGraphic.GetType()
+ && !aTransformedGraphic.getVectorGraphicData());
+ const bool isAdjusted(getGraphicAttr().IsAdjusted());
+ const bool isDrawMode(GraphicDrawMode::Standard != getGraphicAttr().GetDrawMode());
+
+ if (isBitmap && (isAdjusted || isDrawMode))
+ {
+ // the pure primitive solution with the color modifiers works well, too, but when
+ // it is a bitmap graphic the old modification currently is faster; so use it here
+ // instead of creating all as in create2DColorModifierEmbeddingsAsNeeded (see below).
+ // Still, crop, rotation, mirroring and transparency is handled by primitives already
+ // (see above).
+ // This could even be done when vector graphic, but we explicitly want to have the
+ // pure primitive solution for this; this will allow vector graphics to stay vector
+ // graphics, independent from the color filtering stuff. This will enhance e.g.
+ // SVG and print quality while reducing data size at the same time.
+ // The other way around the old modifications when only used on already bitmap objects
+ // will not lose any quality.
+ aTransformedGraphic = rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr);
+
+ // reset GraphicAttr after use to not apply double
+ aSuppressGraphicAttr = GraphicAttr();
+ }
+
+ // create sub-content; helper takes care of correct handling of
+ // bitmap, svg or metafile content
+ Primitive2DContainer aRetval;
+ create2DDecompositionOfGraphic(aRetval, aTransformedGraphic, aTransform);
+
+ if (aRetval.empty())
+ {
+ // content is invisible, done
+ return;
+ }
+
+ if (isAdjusted || isDrawMode)
+ {
+ // embed to needed ModifiedColorPrimitive2D's if necessary. Do this for
+ // adjustments and draw mode specials
+ aRetval = create2DColorModifierEmbeddingsAsNeeded(
+ std::move(aRetval), aSuppressGraphicAttr.GetDrawMode(),
+ std::clamp(aSuppressGraphicAttr.GetLuminance() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetContrast() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetChannelR() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetChannelG() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetChannelB() * 0.01, -1.0, 1.0),
+ std::clamp(aSuppressGraphicAttr.GetGamma(), 0.0, 10.0),
+ aSuppressGraphicAttr.IsInvert());
+
+ if (aRetval.empty())
+ {
+ // content is invisible, done
+ return;
+ }
+ }
+
+ if (getGraphicAttr().IsTransparent())
+ {
+ // check for transparency
+ const double fTransparency(
+ std::clamp((255 - getGraphicAttr().GetAlpha()) * (1.0 / 255.0), 0.0, 1.0));
+
+ if (!basegfx::fTools::equalZero(fTransparency))
+ {
+ Primitive2DReference aUnifiedTransparence(
+ new UnifiedTransparencePrimitive2D(std::move(aRetval), fTransparency));
+
+ aRetval = Primitive2DContainer{ aUnifiedTransparence };
+ }
+ }
+
+ if (getGraphicAttr().IsCropped())
+ {
+ // check for cropping
+ // calculate scalings between real image size and logic object size. This
+ // is necessary since the crop values are relative to original bitmap size
+ const basegfx::B2DVector aObjectScale(aTransform * basegfx::B2DVector(1.0, 1.0));
+ const basegfx::B2DVector aCropScaleFactor(rGraphicObject.calculateCropScaling(
+ aObjectScale.getX(), aObjectScale.getY(), getGraphicAttr().GetLeftCrop(),
+ getGraphicAttr().GetTopCrop(), getGraphicAttr().GetRightCrop(),
+ 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 };
+ }
+
+ rContainer.append(std::move(aRetval));
+}
+
+GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform,
+ const GraphicObject& rGraphicObject,
+ const GraphicAttr& rGraphicAttr)
+ : maTransform(rTransform)
+ , maGraphicObject(rGraphicObject)
+ , maGraphicAttr(rGraphicAttr)
+{
+}
+
+GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform,
+ const GraphicObject& rGraphicObject)
+ : maTransform(rTransform)
+ , maGraphicObject(rGraphicObject)
+{
+}
+
+bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const GraphicPrimitive2D& rCompare = static_cast<const GraphicPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getGraphicObject() == rCompare.getGraphicObject()
+ && getGraphicAttr() == rCompare.getGraphicAttr());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 GraphicPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
new file mode 100644
index 000000000..c79af14dd
--- /dev/null
+++ b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx
@@ -0,0 +1,697 @@
+/* -*- 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 <algorithm>
+
+#include <primitive2d/graphicprimitivehelper2d.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+// helper class for animated graphics
+
+#include <vcl/animate/Animation.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/skia/SkiaHelper.hxx>
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class AnimatedGraphicPrimitive2D : public AnimatedSwitchPrimitive2D
+ {
+ private:
+ /// the geometric definition
+ basegfx::B2DHomMatrix maTransform;
+
+ /** the Graphic with all its content possibilities, here only
+ animated is allowed and gets checked by isValidData().
+ an instance of Graphic is used here since it's ref-counted
+ and thus a safe copy for now
+ */
+ const Graphic maGraphic;
+
+ /// local animation processing data, excerpt from maGraphic
+ ::Animation maAnimation;
+
+ /// the on-demand created VirtualDevices for frame creation
+ ScopedVclPtrInstance< VirtualDevice > maVirtualDevice;
+ ScopedVclPtrInstance< VirtualDevice > maVirtualDeviceMask;
+
+ // index of the next frame that would be regularly prepared
+ sal_uInt32 mnNextFrameToPrepare;
+
+ /// buffering of 1st frame (always active)
+ Primitive2DReference maBufferedFirstFrame;
+
+ /// buffering of all frames
+ std::vector<Primitive2DReference> maBufferedPrimitives;
+ bool mbBufferingAllowed;
+
+ /// set if the animation is huge so that just always the next frame
+ /// is used instead of using timing
+ bool mbHugeSize;
+
+ /// helper methods
+ bool isValidData() const
+ {
+ return (GraphicType::Bitmap == maGraphic.GetType()
+ && maGraphic.IsAnimated()
+ && maAnimation.Count());
+ }
+
+ void ensureVirtualDeviceSizeAndState()
+ {
+ if (!isValidData())
+ return;
+
+ const Size aCurrent(maVirtualDevice->GetOutputSizePixel());
+ const Size aTarget(maAnimation.GetDisplaySizePixel());
+
+ if (aCurrent != aTarget)
+ {
+ maVirtualDevice->EnableMapMode(false);
+ maVirtualDeviceMask->EnableMapMode(false);
+ maVirtualDevice->SetOutputSizePixel(aTarget);
+ maVirtualDeviceMask->SetOutputSizePixel(aTarget);
+ }
+
+ maVirtualDevice->Erase();
+ maVirtualDeviceMask->Erase();
+ const ::tools::Rectangle aRect(Point(0, 0), aTarget);
+ maVirtualDeviceMask->SetFillColor(COL_BLACK);
+ maVirtualDeviceMask->SetLineColor();
+ maVirtualDeviceMask->DrawRect(aRect);
+ }
+
+ sal_uInt32 generateStepTime(sal_uInt32 nIndex) const
+ {
+ const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(nIndex));
+ sal_uInt32 nWaitTime(rAnimationBitmap.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)
+ {
+ // ATM the huge value would block the timer, so
+ // use a long time to show first page (whole day)
+ nWaitTime = 100 * 60 * 60 * 24;
+ }
+
+ // Bad trap: There are animated gifs with no set WaitTime (!).
+ // In that case use a default value.
+ if (0 == nWaitTime)
+ {
+ nWaitTime = 100;
+ }
+
+ return nWaitTime;
+ }
+
+ void createAndSetAnimationTiming()
+ {
+ if (!isValidData())
+ return;
+
+ animation::AnimationEntryLoop aAnimationLoop(maAnimation.GetLoopCount() ? maAnimation.GetLoopCount() : 0xffff);
+ const sal_uInt32 nCount(maAnimation.Count());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ const sal_uInt32 aStepTime(generateStepTime(a));
+ const animation::AnimationEntryFixed aTime(static_cast<double>(aStepTime), static_cast<double>(a) / static_cast<double>(nCount));
+
+ aAnimationLoop.append(aTime);
+ }
+
+ animation::AnimationEntryList aAnimationEntryList;
+ aAnimationEntryList.append(aAnimationLoop);
+
+ setAnimationEntry(aAnimationEntryList);
+ }
+
+ Primitive2DReference createFromBuffer() const
+ {
+ // create BitmapEx by extracting from VirtualDevices
+ const Bitmap aMainBitmap(maVirtualDevice->GetBitmap(Point(), maVirtualDevice->GetOutputSizePixel()));
+ bool useAlphaMask = false;
+#if defined(MACOSX) || defined(IOS)
+ useAlphaMask = true;
+#else
+ // GetBitmap()-> AlphaMask is optimized with SkiaSalBitmap::InterpretAs8Bit(), 1bpp mask is not.
+ if( SkiaHelper::isVCLSkiaEnabled())
+ useAlphaMask = true;
+#endif
+ BitmapEx bitmap;
+ if( useAlphaMask )
+ {
+ const AlphaMask aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
+ bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
+ }
+ else
+ {
+ const Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel()));
+ bitmap = BitmapEx(aMainBitmap, aMaskBitmap);
+ }
+
+ return Primitive2DReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(bitmap),
+ getTransform()));
+ }
+
+ void checkSafeToBuffer(sal_uInt32 nIndex)
+ {
+ if (mbBufferingAllowed)
+ {
+ // all frames buffered
+ if (!maBufferedPrimitives.empty() && nIndex < maBufferedPrimitives.size())
+ {
+ if (!maBufferedPrimitives[nIndex].is())
+ {
+ maBufferedPrimitives[nIndex] = createFromBuffer();
+
+ // check if buffering is complete
+ bool bBufferingComplete(true);
+
+ for (auto const & a: maBufferedPrimitives)
+ {
+ if (!a.is())
+ {
+ bBufferingComplete = false;
+ break;
+ }
+ }
+
+ if (bBufferingComplete)
+ {
+ maVirtualDevice.disposeAndClear();
+ maVirtualDeviceMask.disposeAndClear();
+ }
+ }
+ }
+ }
+ else
+ {
+ // always buffer first frame
+ if (0 == nIndex && !maBufferedFirstFrame.is())
+ {
+ maBufferedFirstFrame = createFromBuffer();
+ }
+ }
+ }
+
+ void createFrame(sal_uInt32 nTarget)
+ {
+ // mnNextFrameToPrepare is the target frame to create next (which implies that
+ // mnNextFrameToPrepare-1 *is* currently in the VirtualDevice when
+ // 0 != mnNextFrameToPrepare. nTarget is the target frame.
+ if (!isValidData())
+ return;
+
+ if (mnNextFrameToPrepare > nTarget)
+ {
+ // we are ahead request, reset mechanism to start at frame zero
+ ensureVirtualDeviceSizeAndState();
+ mnNextFrameToPrepare = 0;
+ }
+
+ while (mnNextFrameToPrepare <= nTarget)
+ {
+ // prepare step
+ const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(mnNextFrameToPrepare));
+
+ switch (rAnimationBitmap.meDisposal)
+ {
+ case Disposal::Not:
+ {
+ maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx);
+ Bitmap aAlphaMask = rAnimationBitmap.maBitmapEx.GetAlpha();
+
+ if (aAlphaMask.IsEmpty())
+ {
+ const Point aEmpty;
+ const ::tools::Rectangle aRect(aEmpty, maVirtualDeviceMask->GetOutputSizePixel());
+ const Wallpaper aWallpaper(COL_BLACK);
+ maVirtualDeviceMask->DrawWallpaper(aRect, aWallpaper);
+ }
+ else
+ {
+ BitmapEx aExpandVisibilityMask(aAlphaMask, aAlphaMask);
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask);
+ }
+
+ break;
+ }
+ case Disposal::Back:
+ {
+ // #i70772# react on no mask, for primitives, too.
+ const Bitmap & rMask(rAnimationBitmap.maBitmapEx.GetAlpha());
+ const Bitmap & rContent(rAnimationBitmap.maBitmapEx.GetBitmap());
+
+ maVirtualDeviceMask->Erase();
+ maVirtualDevice->DrawBitmap(rAnimationBitmap.maPositionPixel, rContent);
+
+ if (rMask.IsEmpty())
+ {
+ const ::tools::Rectangle aRect(rAnimationBitmap.maPositionPixel, rContent.GetSizePixel());
+ maVirtualDeviceMask->SetFillColor(COL_BLACK);
+ maVirtualDeviceMask->SetLineColor();
+ maVirtualDeviceMask->DrawRect(aRect);
+ }
+ else
+ {
+ BitmapEx aExpandVisibilityMask(rMask, rMask);
+ maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.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);
+ break;
+ }
+ }
+
+ // to not waste created data, check adding to buffers
+ checkSafeToBuffer(mnNextFrameToPrepare);
+
+ mnNextFrameToPrepare++;
+ }
+ }
+
+ Primitive2DReference tryTogetFromBuffer(sal_uInt32 nIndex) const
+ {
+ if (mbBufferingAllowed)
+ {
+ // all frames buffered, check if available
+ if (!maBufferedPrimitives.empty() && nIndex < maBufferedPrimitives.size())
+ {
+ if (maBufferedPrimitives[nIndex].is())
+ {
+ return maBufferedPrimitives[nIndex];
+ }
+ }
+ }
+ else
+ {
+ // always buffer first frame, it's sometimes requested out-of-order
+ if (0 == nIndex && maBufferedFirstFrame.is())
+ {
+ return maBufferedFirstFrame;
+ }
+ }
+
+ return Primitive2DReference();
+ }
+
+ public:
+ /// constructor
+ AnimatedGraphicPrimitive2D(
+ const Graphic& rGraphic,
+ const basegfx::B2DHomMatrix& rTransform);
+
+ /// data read access
+ const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
+
+ /// compare operator
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ /// override to deliver the correct expected frame dependent of timing
+ virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override;
+ };
+
+ }
+
+ AnimatedGraphicPrimitive2D::AnimatedGraphicPrimitive2D(
+ const Graphic& rGraphic,
+ const basegfx::B2DHomMatrix& rTransform)
+ : AnimatedSwitchPrimitive2D(
+ animation::AnimationEntryList(),
+ Primitive2DContainer(),
+ false),
+ maTransform(rTransform),
+ maGraphic(rGraphic),
+ maAnimation(rGraphic.GetAnimation()),
+ maVirtualDevice(*Application::GetDefaultDevice()),
+ maVirtualDeviceMask(*Application::GetDefaultDevice()),
+ mnNextFrameToPrepare(SAL_MAX_UINT32),
+ mbBufferingAllowed(false),
+ mbHugeSize(false)
+ {
+ // initialize AnimationTiming, needed to detect which frame is requested
+ // in get2DDecomposition
+ createAndSetAnimationTiming();
+
+ // check if we allow buffering
+ if (isValidData())
+ {
+ // allow buffering up to a size of:
+ // - 64 frames
+ // - sizes of 256x256 pixels
+ // This may be offered in option values if needed
+ static const sal_uInt64 nAllowedSize(64 * 256 * 256);
+ static const sal_uInt64 nHugeSize(10000000);
+ const Size aTarget(maAnimation.GetDisplaySizePixel());
+ const sal_uInt64 nUsedSize(static_cast<sal_uInt64>(maAnimation.Count()) * aTarget.Width() * aTarget.Height());
+
+ if (nUsedSize < nAllowedSize)
+ {
+ mbBufferingAllowed = true;
+ }
+
+ if (nUsedSize > nHugeSize)
+ {
+ mbHugeSize = true;
+ }
+ }
+
+ // prepare buffer space
+ if (mbBufferingAllowed && isValidData())
+ {
+ maBufferedPrimitives.resize(maAnimation.Count());
+ }
+ }
+
+ bool AnimatedGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ // do not use 'GroupPrimitive2D::operator==' here, that would compare
+ // the children. Also do not use 'BasePrimitive2D::operator==', that would
+ // check the ID-Type. Since we are a simple derivation without own ID,
+ // use the dynamic_cast RTTI directly
+ const AnimatedGraphicPrimitive2D* pCompare = dynamic_cast<const AnimatedGraphicPrimitive2D*>(&rPrimitive);
+
+ // use operator== of Graphic - if that is equal, the basic definition is equal
+ return (nullptr != pCompare
+ && getTransform() == pCompare->getTransform()
+ && maGraphic == pCompare->maGraphic);
+ }
+
+ void AnimatedGraphicPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if (!isValidData())
+ return;
+
+ Primitive2DReference aRetval;
+ const double fState(getAnimationEntry().getStateAtTime(rViewInformation.getViewTime()));
+ const sal_uInt32 nLen(maAnimation.Count());
+ sal_uInt32 nIndex(basegfx::fround(fState * static_cast<double>(nLen)));
+
+ // nIndex is the requested frame - it is in range [0..nLen[
+ // create frame representation in VirtualDevices
+ if (nIndex >= nLen)
+ {
+ nIndex = nLen - 1;
+ }
+
+ // check buffering shortcuts, may already be created
+ aRetval = tryTogetFromBuffer(nIndex);
+
+ if (aRetval.is())
+ {
+ rVisitor.visit(aRetval);
+ return;
+ }
+
+ // if huge size (and not the buffered 1st frame) simply
+ // create next frame
+ if (mbHugeSize && 0 != nIndex && mnNextFrameToPrepare <= nIndex)
+ {
+ nIndex = mnNextFrameToPrepare % nLen;
+ }
+
+ // frame not (yet) buffered or no buffering allowed, create it
+ const_cast<AnimatedGraphicPrimitive2D*>(this)->createFrame(nIndex);
+
+ // try to get from buffer again, may have been added from createFrame
+ aRetval = tryTogetFromBuffer(nIndex);
+
+ if (aRetval.is())
+ {
+ rVisitor.visit(aRetval);
+ return;
+ }
+
+ // did not work (not buffered and not 1st frame), create from buffer
+ aRetval = createFromBuffer();
+
+ rVisitor.visit(aRetval);
+ }
+
+} // end of namespace
+
+namespace drawinglayer::primitive2d
+{
+ void create2DDecompositionOfGraphic(
+ Primitive2DContainer& rContainer,
+ const Graphic& rGraphic,
+ const basegfx::B2DHomMatrix& rTransform)
+ {
+ Primitive2DContainer aRetval;
+
+ switch(rGraphic.GetType())
+ {
+ case GraphicType::Bitmap :
+ {
+ if(rGraphic.IsAnimated())
+ {
+ // prepare specialized AnimatedGraphicPrimitive2D
+ aRetval.resize(1);
+ aRetval[0] = new AnimatedGraphicPrimitive2D(
+ rGraphic,
+ 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))
+ {
+ // translate back to origin, scale to unit coordinates
+ basegfx::B2DHomMatrix aEmbedVectorGraphic(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -rSvgRange.getMinX(),
+ -rSvgRange.getMinY()));
+
+ aEmbedVectorGraphic.scale(
+ 1.0 / rSvgRange.getWidth(),
+ 1.0 / rSvgRange.getHeight());
+
+ // apply created object transformation
+ aEmbedVectorGraphic = rTransform * aEmbedVectorGraphic;
+
+ // add Vector Graphic Data primitives embedded
+ aRetval.resize(1);
+ aRetval[0] = new TransformPrimitive2D(
+ aEmbedVectorGraphic,
+ Primitive2DContainer(rGraphic.getVectorGraphicData()->getPrimitive2DSequence()));
+ }
+ }
+ else
+ {
+ aRetval.resize(1);
+ aRetval[0] = new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(rGraphic.GetBitmapEx()),
+ rTransform);
+ }
+
+ break;
+ }
+
+ case GraphicType::GdiMetafile :
+ {
+ // create MetafilePrimitive2D
+ const GDIMetaFile& rMetafile = rGraphic.GetGDIMetaFile();
+
+ aRetval.resize(1);
+ aRetval[0] = new MetafilePrimitive2D(
+ rTransform,
+ 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
+ // at them. This is an error, but we need to work around this
+ const Size aMetaFilePrefSize(rMetafile.GetPrefSize());
+ const Size aMetaFileRealSize(
+ rMetafile.GetBoundRect(
+ *Application::GetDefaultDevice()).GetSize());
+
+ if(aMetaFileRealSize.getWidth() > aMetaFilePrefSize.getWidth()
+ || aMetaFileRealSize.getHeight() > aMetaFilePrefSize.getHeight())
+ {
+ // clipping needed. Embed to MaskPrimitive2D. Create children and mask polygon
+ basegfx::B2DPolygon aMaskPolygon(basegfx::utils::createUnitPolygon());
+ aMaskPolygon.transform(rTransform);
+
+ Primitive2DReference mask = new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(aMaskPolygon),
+ std::move(aRetval));
+ aRetval = Primitive2DContainer { mask };
+ }
+ break;
+ }
+
+ default:
+ {
+ // nothing to create
+ break;
+ }
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ Primitive2DContainer create2DColorModifierEmbeddingsAsNeeded(
+ Primitive2DContainer&& rChildren,
+ GraphicDrawMode aGraphicDrawMode,
+ double fLuminance,
+ double fContrast,
+ double fRed,
+ double fGreen,
+ double fBlue,
+ double fGamma,
+ bool bInvert)
+ {
+ Primitive2DContainer aRetval;
+
+ if(rChildren.empty())
+ {
+ // no child content, done
+ return aRetval;
+ }
+
+ // set child content as retval; that is what will be used as child content in all
+ // embeddings from here
+ aRetval = std::move(rChildren);
+
+ if(GraphicDrawMode::Watermark == aGraphicDrawMode)
+ {
+ // this is solved by applying fixed values additionally to luminance
+ // and contrast, do it here and reset DrawMode to GraphicDrawMode::Standard
+ // original in svtools uses:
+ // #define WATERMARK_LUM_OFFSET 50
+ // #define WATERMARK_CON_OFFSET -70
+ fLuminance = std::clamp(fLuminance + 0.5, -1.0, 1.0);
+ fContrast = std::clamp(fContrast - 0.7, -1.0, 1.0);
+ aGraphicDrawMode = GraphicDrawMode::Standard;
+ }
+
+ // DrawMode (GraphicDrawMode::Watermark already handled)
+ switch(aGraphicDrawMode)
+ {
+ case GraphicDrawMode::Greys:
+ {
+ // convert to grey
+ const Primitive2DReference aPrimitiveGrey(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_gray>()));
+
+ aRetval = Primitive2DContainer { aPrimitiveGrey };
+ break;
+ }
+ case GraphicDrawMode::Mono:
+ {
+ // convert to mono (black/white with threshold 0.5)
+ const Primitive2DReference aPrimitiveBlackAndWhite(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_black_and_white>(0.5)));
+
+ aRetval = Primitive2DContainer { aPrimitiveBlackAndWhite };
+ break;
+ }
+ default: // case GraphicDrawMode::Standard:
+ {
+ assert(
+ aGraphicDrawMode != GraphicDrawMode::Watermark
+ && "OOps, GraphicDrawMode::Watermark should already be handled (see above)");
+ // nothing to do
+ break;
+ }
+ }
+
+ // mnContPercent, mnLumPercent, mnRPercent, mnGPercent, mnBPercent
+ // handled in a single call
+ if(!basegfx::fTools::equalZero(fLuminance)
+ || !basegfx::fTools::equalZero(fContrast)
+ || !basegfx::fTools::equalZero(fRed)
+ || !basegfx::fTools::equalZero(fGreen)
+ || !basegfx::fTools::equalZero(fBlue))
+ {
+ const Primitive2DReference aPrimitiveRGBLuminannceContrast(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_RGBLuminanceContrast>(
+ fRed,
+ fGreen,
+ fBlue,
+ fLuminance,
+ fContrast)));
+
+ aRetval = Primitive2DContainer { aPrimitiveRGBLuminannceContrast };
+ }
+
+ // gamma (boolean)
+ if(!basegfx::fTools::equal(fGamma, 1.0))
+ {
+ const Primitive2DReference aPrimitiveGamma(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_gamma>(
+ fGamma)));
+
+ aRetval = Primitive2DContainer { aPrimitiveGamma };
+ }
+
+ // invert (boolean)
+ if(bInvert)
+ {
+ const Primitive2DReference aPrimitiveInvert(
+ new ModifiedColorPrimitive2D(
+ std::move(aRetval),
+ std::make_shared<basegfx::BColorModifier_invert>()));
+
+ aRetval = Primitive2DContainer { aPrimitiveInvert };
+ }
+
+ return aRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/gridprimitive2d.cxx b/drawinglayer/source/primitive2d/gridprimitive2d.cxx
new file mode 100644
index 000000000..ddb468e9a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/gridprimitive2d.cxx
@@ -0,0 +1,336 @@
+/* -*- 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/gridprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void GridPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0))
+ return;
+
+ // decompose grid matrix to get logic size
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // create grid matrix which transforms from scaled logic to view
+ basegfx::B2DHomMatrix aRST(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+ aRST *= rViewInformation.getObjectToViewTransformation();
+
+ // get step widths
+ double fStepX(getWidth());
+ double fStepY(getHeight());
+ const double fMinimalStep(10.0);
+
+ // guarantee a step width of 10.0
+ if(basegfx::fTools::less(fStepX, fMinimalStep))
+ {
+ fStepX = fMinimalStep;
+ }
+
+ if(basegfx::fTools::less(fStepY, fMinimalStep))
+ {
+ fStepY = fMinimalStep;
+ }
+
+ // get relative distances in view coordinates
+ double fViewStepX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fStepX, 0.0)).getLength());
+ double fViewStepY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fStepY)).getLength());
+ double fSmallStepX(1.0), fViewSmallStepX(1.0), fSmallStepY(1.0), fViewSmallStepY(1.0);
+ sal_uInt32 nSmallStepsX(0), nSmallStepsY(0);
+
+ // setup subdivisions
+ if(getSubdivisionsX())
+ {
+ fSmallStepX = fStepX / getSubdivisionsX();
+ fViewSmallStepX = fViewStepX / getSubdivisionsX();
+ }
+
+ if(getSubdivisionsY())
+ {
+ fSmallStepY = fStepY / getSubdivisionsY();
+ fViewSmallStepY = fViewStepY / getSubdivisionsY();
+ }
+
+ // correct step width
+ while(fViewStepX < getSmallestViewDistance())
+ {
+ fViewStepX *= 2.0;
+ fStepX *= 2.0;
+ }
+
+ while(fViewStepY < getSmallestViewDistance())
+ {
+ fViewStepY *= 2.0;
+ fStepY *= 2.0;
+ }
+
+ // correct small step width
+ if(getSubdivisionsX())
+ {
+ while(fViewSmallStepX < getSmallestSubdivisionViewDistance())
+ {
+ fViewSmallStepX *= 2.0;
+ fSmallStepX *= 2.0;
+ }
+
+ nSmallStepsX = static_cast<sal_uInt32>(fStepX / fSmallStepX);
+ }
+
+ if(getSubdivisionsY())
+ {
+ while(fViewSmallStepY < getSmallestSubdivisionViewDistance())
+ {
+ fViewSmallStepY *= 2.0;
+ fSmallStepY *= 2.0;
+ }
+
+ nSmallStepsY = static_cast<sal_uInt32>(fStepY / fSmallStepY);
+ }
+
+ // calculate extended viewport in which grid points may lie at all
+ basegfx::B2DRange aExtendedViewport;
+
+ if(rViewInformation.getDiscreteViewport().isEmpty())
+ {
+ // not set, use logic size to travel over all potential grid points
+ aExtendedViewport = basegfx::B2DRange(0.0, 0.0, aScale.getX(), aScale.getY());
+ }
+ else
+ {
+ // transform unit range to discrete view
+ aExtendedViewport = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
+ basegfx::B2DHomMatrix aTrans(rViewInformation.getObjectToViewTransformation() * getTransform());
+ aExtendedViewport.transform(aTrans);
+
+ // intersect with visible part
+ aExtendedViewport.intersect(rViewInformation.getDiscreteViewport());
+
+ if(!aExtendedViewport.isEmpty())
+ {
+ // convert back and apply scale
+ aTrans.invert();
+ aTrans.scale(aScale.getX(), aScale.getY());
+ aExtendedViewport.transform(aTrans);
+
+ // crop start/end in X/Y to multiples of logical step width
+ const double fHalfCrossSize((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(3.0, 0.0)).getLength());
+ const double fMinX(floor((aExtendedViewport.getMinX() - fHalfCrossSize) / fStepX) * fStepX);
+ const double fMaxX(ceil((aExtendedViewport.getMaxX() + fHalfCrossSize) / fStepX) * fStepX);
+ const double fMinY(floor((aExtendedViewport.getMinY() - fHalfCrossSize) / fStepY) * fStepY);
+ const double fMaxY(ceil((aExtendedViewport.getMaxY() + fHalfCrossSize) / fStepY) * fStepY);
+
+ // put to aExtendedViewport and crop on object logic size
+ aExtendedViewport = basegfx::B2DRange(
+ std::max(fMinX, 0.0),
+ std::max(fMinY, 0.0),
+ std::min(fMaxX, aScale.getX()),
+ std::min(fMaxY, aScale.getY()));
+ }
+ }
+
+ if(aExtendedViewport.isEmpty())
+ return;
+
+ // prepare point vectors for point and cross markers
+ std::vector< basegfx::B2DPoint > aPositionsPoint;
+ std::vector< basegfx::B2DPoint > aPositionsCross;
+
+ for(double fX(aExtendedViewport.getMinX()); fX < aExtendedViewport.getMaxX(); fX += fStepX)
+ {
+ const bool bXZero(basegfx::fTools::equalZero(fX));
+
+ for(double fY(aExtendedViewport.getMinY()); fY < aExtendedViewport.getMaxY(); fY += fStepY)
+ {
+ const bool bYZero(basegfx::fTools::equalZero(fY));
+
+ if(!bXZero && !bYZero)
+ {
+ // get discrete position and test against 3x3 area surrounding it
+ // since it's a cross
+ const double fHalfCrossSize(3.0 * 0.5);
+ const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fY));
+ const basegfx::B2DRange aDiscreteRangeCross(
+ aViewPos.getX() - fHalfCrossSize, aViewPos.getY() - fHalfCrossSize,
+ aViewPos.getX() + fHalfCrossSize, aViewPos.getY() + fHalfCrossSize);
+
+ if(rViewInformation.getDiscreteViewport().overlaps(aDiscreteRangeCross))
+ {
+ const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
+ aPositionsCross.push_back(aLogicPos);
+ }
+ }
+
+ if(getSubdivisionsX() && !bYZero)
+ {
+ double fF(fX + fSmallStepX);
+
+ for(sal_uInt32 a(1); a < nSmallStepsX && fF < aExtendedViewport.getMaxX(); a++, fF += fSmallStepX)
+ {
+ const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fF, fY));
+
+ if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
+ {
+ const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
+ aPositionsPoint.push_back(aLogicPos);
+ }
+ }
+ }
+
+ if(getSubdivisionsY() && !bXZero)
+ {
+ double fF(fY + fSmallStepY);
+
+ for(sal_uInt32 a(1); a < nSmallStepsY && fF < aExtendedViewport.getMaxY(); a++, fF += fSmallStepY)
+ {
+ const basegfx::B2DPoint aViewPos(aRST * basegfx::B2DPoint(fX, fF));
+
+ if(rViewInformation.getDiscreteViewport().isInside(aViewPos))
+ {
+ const basegfx::B2DPoint aLogicPos(rViewInformation.getInverseObjectToViewTransformation() * aViewPos);
+ aPositionsPoint.push_back(aLogicPos);
+ }
+ }
+ }
+ }
+ }
+
+ // prepare return value
+ const sal_uInt32 nCountPoint(aPositionsPoint.size());
+ const sal_uInt32 nCountCross(aPositionsCross.size());
+
+ // add PointArrayPrimitive2D if point markers were added
+ if(nCountPoint)
+ {
+ rContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsPoint), getBColor()));
+ }
+
+ // add MarkerArrayPrimitive2D if cross markers were added
+ if(!nCountCross)
+ return;
+
+ 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()));
+ }
+ else
+ {
+ rContainer.push_back(new MarkerArrayPrimitive2D(std::move(aPositionsCross), getCrossMarker()));
+ }
+ }
+
+ GridPrimitive2D::GridPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ double fWidth,
+ double fHeight,
+ double fSmallestViewDistance,
+ double fSmallestSubdivisionViewDistance,
+ sal_uInt32 nSubdivisionsX,
+ sal_uInt32 nSubdivisionsY,
+ const basegfx::BColor& rBColor,
+ const BitmapEx& rCrossMarker)
+ : maTransform(rTransform),
+ mfWidth(fWidth),
+ mfHeight(fHeight),
+ mfSmallestViewDistance(fSmallestViewDistance),
+ mfSmallestSubdivisionViewDistance(fSmallestSubdivisionViewDistance),
+ mnSubdivisionsX(nSubdivisionsX),
+ mnSubdivisionsY(nSubdivisionsY),
+ maBColor(rBColor),
+ maCrossMarker(rCrossMarker)
+ {
+ }
+
+ bool GridPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const GridPrimitive2D& rCompare = static_cast<const GridPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getWidth() == rCompare.getWidth()
+ && getHeight() == rCompare.getHeight()
+ && getSmallestViewDistance() == rCompare.getSmallestViewDistance()
+ && getSmallestSubdivisionViewDistance() == rCompare.getSmallestSubdivisionViewDistance()
+ && getSubdivisionsX() == rCompare.getSubdivisionsX()
+ && getSubdivisionsY() == rCompare.getSubdivisionsY()
+ && getBColor() == rCompare.getBColor()
+ && getCrossMarker() == rCompare.getCrossMarker());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange GridPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get object's range
+ basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ aUnitRange.transform(getTransform());
+
+ // intersect with visible part
+ aUnitRange.intersect(rViewInformation.getViewport());
+
+ return aUnitRange;
+ }
+
+ void GridPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getBuffered2DDecomposition().empty())
+ {
+ if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange and ViewTransformation
+ const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
+ const_cast< GridPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 GridPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_GRIDPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/groupprimitive2d.cxx b/drawinglayer/source/primitive2d/groupprimitive2d.cxx
new file mode 100644
index 000000000..7a39bde2c
--- /dev/null
+++ b/drawinglayer/source/primitive2d/groupprimitive2d.cxx
@@ -0,0 +1,75 @@
+/* -*- 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/groupprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ GroupPrimitive2D::GroupPrimitive2D( Primitive2DContainer&& aChildren )
+ : maChildren(std::move(aChildren))
+ {
+ }
+
+ /** The compare opertator uses the Sequence::==operator, so only checking if
+ the references are equal. All non-equal references are interpreted as
+ non-equal.
+ */
+ bool GroupPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const GroupPrimitive2D& rCompare = static_cast< const GroupPrimitive2D& >(rPrimitive);
+
+ return getChildren() == rCompare.getChildren();
+ }
+
+ return false;
+ }
+
+ /// default: just return children, so all renderers not supporting group will use its content
+ void GroupPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ getChildren(rVisitor);
+ }
+
+ sal_Int64 GroupPrimitive2D::estimateUsage()
+ {
+ size_t nRet(0);
+ for (auto& it : getChildren())
+ {
+ if (it)
+ nRet += it->estimateUsage();
+ }
+ return nRet;
+ }
+
+ // provide unique ID
+ sal_uInt32 GroupPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_GROUPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/helplineprimitive2d.cxx b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
new file mode 100644
index 000000000..07d6a7f22
--- /dev/null
+++ b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx
@@ -0,0 +1,186 @@
+/* -*- 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/helplineprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void HelplinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(rViewInformation.getViewport().isEmpty() || getDirection().equalZero())
+ return;
+
+ // position to view coordinates, DashLen and DashLen in logic
+ const basegfx::B2DPoint aViewPosition(rViewInformation.getObjectToViewTransformation() * getPosition());
+
+ switch(getStyle())
+ {
+ default : // HelplineStyle2D::Point
+ {
+ const double fViewFixValue(15.0);
+ basegfx::B2DVector aNormalizedDirection(getDirection());
+ aNormalizedDirection.normalize();
+ aNormalizedDirection *= fViewFixValue;
+ const basegfx::B2DPoint aStartA(aViewPosition - aNormalizedDirection);
+ const basegfx::B2DPoint aEndA(aViewPosition + aNormalizedDirection);
+ basegfx::B2DPolygon aLineA;
+ aLineA.append(aStartA);
+ aLineA.append(aEndA);
+ aLineA.transform(rViewInformation.getInverseObjectToViewTransformation());
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLineA, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+
+ const basegfx::B2DVector aPerpendicularNormalizedDirection(basegfx::getPerpendicular(aNormalizedDirection));
+ const basegfx::B2DPoint aStartB(aViewPosition - aPerpendicularNormalizedDirection);
+ const basegfx::B2DPoint aEndB(aViewPosition + aPerpendicularNormalizedDirection);
+ basegfx::B2DPolygon aLineB;
+ aLineB.append(aStartB);
+ aLineB.append(aEndB);
+ aLineB.transform(rViewInformation.getInverseObjectToViewTransformation());
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLineB, getRGBColA(), getRGBColB(), getDiscreteDashLength()));
+
+ break;
+ }
+ case HelplineStyle2D::Line :
+ {
+ basegfx::B2DPolygon aLine;
+
+ if(basegfx::areParallel(getDirection(), basegfx::B2DVector(1.0, 0.0)))
+ {
+ // parallel to X-Axis, get cuts with Y-Axes
+ const double fCutA((rViewInformation.getDiscreteViewport().getMinX() - aViewPosition.getX()) / getDirection().getX());
+ const double fCutB((rViewInformation.getDiscreteViewport().getMaxX() - aViewPosition.getX()) / getDirection().getX());
+ const basegfx::B2DPoint aPosA(aViewPosition + (fCutA * getDirection()));
+ const basegfx::B2DPoint aPosB(aViewPosition + (fCutB * getDirection()));
+ const bool bBothLeft(aPosA.getX() < rViewInformation.getDiscreteViewport().getMinX() && aPosB.getX() < rViewInformation.getDiscreteViewport().getMinX());
+ const bool bBothRight(aPosA.getX() > rViewInformation.getDiscreteViewport().getMaxX() && aPosB.getX() < rViewInformation.getDiscreteViewport().getMaxX());
+
+ if(!bBothLeft && !bBothRight)
+ {
+ aLine.append(aPosA);
+ aLine.append(aPosB);
+ }
+ }
+ else
+ {
+ // get cuts with X-Axes
+ const double fCutA((rViewInformation.getDiscreteViewport().getMinY() - aViewPosition.getY()) / getDirection().getY());
+ const double fCutB((rViewInformation.getDiscreteViewport().getMaxY() - aViewPosition.getY()) / getDirection().getY());
+ const basegfx::B2DPoint aPosA(aViewPosition + (fCutA * getDirection()));
+ const basegfx::B2DPoint aPosB(aViewPosition + (fCutB * getDirection()));
+ const bool bBothAbove(aPosA.getY() < rViewInformation.getDiscreteViewport().getMinY() && aPosB.getY() < rViewInformation.getDiscreteViewport().getMinY());
+ const bool bBothBelow(aPosA.getY() > rViewInformation.getDiscreteViewport().getMaxY() && aPosB.getY() < rViewInformation.getDiscreteViewport().getMaxY());
+
+ if(!bBothAbove && !bBothBelow)
+ {
+ aLine.append(aPosA);
+ aLine.append(aPosB);
+ }
+ }
+
+ if(aLine.count())
+ {
+ // clip against visible area
+ const basegfx::B2DPolyPolygon aResult(basegfx::utils::clipPolygonOnRange(aLine, rViewInformation.getDiscreteViewport(), true, true));
+
+ 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()));
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ HelplinePrimitive2D::HelplinePrimitive2D(
+ const basegfx::B2DPoint& rPosition,
+ const basegfx::B2DVector& rDirection,
+ HelplineStyle2D eStyle,
+ const basegfx::BColor& rRGBColA,
+ const basegfx::BColor& rRGBColB,
+ double fDiscreteDashLength)
+ : maPosition(rPosition),
+ maDirection(rDirection),
+ meStyle(eStyle),
+ maRGBColA(rRGBColA),
+ maRGBColB(rRGBColB),
+ mfDiscreteDashLength(fDiscreteDashLength)
+ {
+ }
+
+ bool HelplinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const HelplinePrimitive2D& rCompare = static_cast<const HelplinePrimitive2D&>(rPrimitive);
+
+ return (getPosition() == rCompare.getPosition()
+ && getDirection() == rCompare.getDirection()
+ && getStyle() == rCompare.getStyle()
+ && getRGBColA() == rCompare.getRGBColA()
+ && getRGBColB() == rCompare.getRGBColB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+ }
+
+ void HelplinePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(!getBuffered2DDecomposition().empty())
+ {
+ if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< HelplinePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange and ViewTransformation
+ const_cast< HelplinePrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation();
+ const_cast< HelplinePrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 HelplinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx b/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx
new file mode 100644
index 000000000..c1a5442b2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx
@@ -0,0 +1,52 @@
+/* -*- 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/hiddengeometryprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ HiddenGeometryPrimitive2D::HiddenGeometryPrimitive2D(
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ basegfx::B2DRange HiddenGeometryPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ return getChildren().getB2DRange(rViewInformation);
+ }
+
+ void HiddenGeometryPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& /*rVisitor*/, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 HiddenGeometryPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/invertprimitive2d.cxx b/drawinglayer/source/primitive2d/invertprimitive2d.cxx
new file mode 100644
index 000000000..e2d01381e
--- /dev/null
+++ b/drawinglayer/source/primitive2d/invertprimitive2d.cxx
@@ -0,0 +1,43 @@
+/* -*- 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/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ InvertPrimitive2D::InvertPrimitive2D(
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 InvertPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_INVERTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
new file mode 100644
index 000000000..cdaa15e53
--- /dev/null
+++ b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx
@@ -0,0 +1,136 @@
+/* -*- 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/markerarrayprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void MarkerArrayPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ const std::vector< basegfx::B2DPoint >& rPositions = getPositions();
+ const sal_uInt32 nMarkerCount(rPositions.size());
+
+ if(!nMarkerCount || getMarker().IsEmpty())
+ return;
+
+ // get pixel size
+ Size aBitmapSize(getMarker().GetSizePixel());
+
+ if(!(aBitmapSize.Width() && aBitmapSize.Height()))
+ return;
+
+ // get logic half pixel size
+ basegfx::B2DVector aLogicHalfSize(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(aBitmapSize.getWidth() - 1.0, aBitmapSize.getHeight() - 1.0));
+
+ // use half size for expand
+ aLogicHalfSize *= 0.5;
+
+ for(const auto& rPosition : rPositions)
+ {
+ const basegfx::B2DRange aRange(rPosition - aLogicHalfSize, rPosition + aLogicHalfSize);
+ basegfx::B2DHomMatrix aTransform;
+
+ aTransform.set(0, 0, aRange.getWidth());
+ aTransform.set(1, 1, aRange.getHeight());
+ aTransform.set(0, 2, aRange.getMinX());
+ aTransform.set(1, 2, aRange.getMinY());
+
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getMarker()),
+ aTransform));
+ }
+ }
+
+ MarkerArrayPrimitive2D::MarkerArrayPrimitive2D(
+ std::vector< basegfx::B2DPoint >&& rPositions,
+ const BitmapEx& rMarker)
+ : maPositions(std::move(rPositions)),
+ maMarker(rMarker)
+ {
+ }
+
+ bool MarkerArrayPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MarkerArrayPrimitive2D& rCompare = static_cast<const MarkerArrayPrimitive2D&>(rPrimitive);
+
+ return (getPositions() == rCompare.getPositions()
+ && getMarker() == rCompare.getMarker());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MarkerArrayPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval;
+
+ if(!getPositions().empty())
+ {
+ // get the basic range from the position vector
+ for (auto const& pos : getPositions())
+ {
+ aRetval.expand(pos);
+ }
+
+ if(!getMarker().IsEmpty())
+ {
+ // get pixel size
+ const Size aBitmapSize(getMarker().GetSizePixel());
+
+ if(aBitmapSize.Width() && aBitmapSize.Height())
+ {
+ // get logic half size
+ basegfx::B2DVector aLogicHalfSize(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(aBitmapSize.getWidth(), aBitmapSize.getHeight()));
+
+ // use half size for expand
+ aLogicHalfSize *= 0.5;
+
+ // apply aLogicHalfSize
+ aRetval.expand(aRetval.getMinimum() - aLogicHalfSize);
+ aRetval.expand(aRetval.getMaximum() + aLogicHalfSize);
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 MarkerArrayPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/maskprimitive2d.cxx b/drawinglayer/source/primitive2d/maskprimitive2d.cxx
new file mode 100644
index 000000000..842085168
--- /dev/null
+++ b/drawinglayer/source/primitive2d/maskprimitive2d.cxx
@@ -0,0 +1,62 @@
+/* -*- 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/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ MaskPrimitive2D::MaskPrimitive2D(
+ const basegfx::B2DPolyPolygon& rMask,
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maMask(rMask)
+ {
+ }
+
+ bool MaskPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const MaskPrimitive2D& rCompare = static_cast< const MaskPrimitive2D& >(rPrimitive);
+
+ return (getMask() == rCompare.getMask());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MaskPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ return getMask().getB2DRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 MaskPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MASKPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/mediaprimitive2d.cxx b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
new file mode 100644
index 000000000..e097d2d98
--- /dev/null
+++ b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx
@@ -0,0 +1,148 @@
+/* -*- 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/mediaprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void MediaPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer xRetval;
+ xRetval.resize(1);
+
+ // create background object
+ basegfx::B2DPolygon aBackgroundPolygon(basegfx::utils::createUnitPolygon());
+ aBackgroundPolygon.transform(getTransform());
+ const Primitive2DReference xRefBackground(
+ new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aBackgroundPolygon),
+ getBackgroundColor()));
+ xRetval[0] = xRefBackground;
+
+ if(GraphicType::Bitmap == maSnapshot.GetType() || GraphicType::GdiMetafile == maSnapshot.GetType())
+ {
+ const GraphicObject aGraphicObject(maSnapshot);
+ const GraphicAttr aGraphicAttr;
+ xRetval.resize(2);
+ xRetval[1] = new GraphicPrimitive2D(getTransform(), aGraphicObject, aGraphicAttr);
+ }
+
+ if(getDiscreteBorder())
+ {
+ const basegfx::B2DVector aDiscreteInLogic(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(static_cast<double>(getDiscreteBorder()), static_cast<double>(getDiscreteBorder())));
+ const double fDiscreteSize(aDiscreteInLogic.getX() + aDiscreteInLogic.getY());
+
+ basegfx::B2DRange aSourceRange(0.0, 0.0, 1.0, 1.0);
+ aSourceRange.transform(getTransform());
+
+ basegfx::B2DRange aDestRange(aSourceRange);
+ aDestRange.grow(-0.5 * fDiscreteSize);
+
+ if(basegfx::fTools::equalZero(aDestRange.getWidth()) || basegfx::fTools::equalZero(aDestRange.getHeight()))
+ {
+ // shrunk primitive has no content (zero size in X or Y), nothing to display. Still create
+ // invisible content for HitTest and BoundRect
+ const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(std::move(xRetval)));
+
+ xRetval = Primitive2DContainer { xHiddenLines, };
+ }
+ else
+ {
+ // create transformation matrix from original range to shrunk range
+ basegfx::B2DHomMatrix aTransform;
+ aTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
+ aTransform.scale(aDestRange.getWidth() / aSourceRange.getWidth(), aDestRange.getHeight() / aSourceRange.getHeight());
+ aTransform.translate(aDestRange.getMinX(), aDestRange.getMinY());
+
+ // add transform primitive
+ Primitive2DReference aScaled(new TransformPrimitive2D(aTransform, std::move(xRetval)));
+ xRetval = Primitive2DContainer { aScaled };
+ }
+ }
+
+ rContainer.append(std::move(xRetval));
+ }
+
+ MediaPrimitive2D::MediaPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const OUString& rURL,
+ const basegfx::BColor& rBackgroundColor,
+ sal_uInt32 nDiscreteBorder,
+ const Graphic &rSnapshot)
+ : maTransform(rTransform),
+ maURL(rURL),
+ maBackgroundColor(rBackgroundColor),
+ mnDiscreteBorder(nDiscreteBorder),
+ maSnapshot(rSnapshot)
+ {
+ }
+
+ bool MediaPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MediaPrimitive2D& rCompare = static_cast<const MediaPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && maURL == rCompare.maURL
+ && getBackgroundColor() == rCompare.getBackgroundColor()
+ && getDiscreteBorder() == rCompare.getDiscreteBorder()
+ && maSnapshot.IsNone() == rCompare.maSnapshot.IsNone());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MediaPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ if(getDiscreteBorder())
+ {
+ const basegfx::B2DVector aDiscreteInLogic(rViewInformation.getInverseObjectToViewTransformation() *
+ basegfx::B2DVector(static_cast<double>(getDiscreteBorder()), static_cast<double>(getDiscreteBorder())));
+ const double fDiscreteSize(aDiscreteInLogic.getX() + aDiscreteInLogic.getY());
+
+ aRetval.grow(-0.5 * fDiscreteSize);
+ }
+
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 MediaPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MEDIAPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
new file mode 100644
index 000000000..608be0bbc
--- /dev/null
+++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx
@@ -0,0 +1,135 @@
+/* -*- 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/metafileprimitive2d.hxx>
+#include <wmfemfhelper.hxx>
+
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <vcl/canvastools.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ void MetafilePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, 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())
+ {
+ // 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())
+ {
+ 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 };
+ }
+ }
+
+ // 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;
+
+ // embed to target transformation
+ const Primitive2DReference aEmbeddedTransform(
+ new TransformPrimitive2D(
+ aAdaptedTransform,
+ std::move(xRetval)));
+
+ xRetval = Primitive2DContainer { aEmbeddedTransform };
+ }
+
+ rContainer.append(std::move(xRetval));
+ }
+
+ MetafilePrimitive2D::MetafilePrimitive2D(
+ const basegfx::B2DHomMatrix& rMetaFileTransform,
+ const GDIMetaFile& rMetaFile)
+ : maMetaFileTransform(rMetaFileTransform),
+ maMetaFile(rMetaFile)
+ {
+ }
+
+ bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const MetafilePrimitive2D& rCompare = static_cast<const MetafilePrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getMetaFile() == rCompare.getMetaFile());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use own implementation to quickly answer the getB2DRange question. The
+ // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
+ // this is not the case (i have already seen some wrong Metafiles) it should
+ // be embedded to a MaskPrimitive2D
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+
+ return aRetval;
+ }
+
+ // from MetafileAccessor
+ void MetafilePrimitive2D::accessMetafile(GDIMetaFile& rTargetMetafile) const
+ {
+ rTargetMetafile = maMetaFile;
+ }
+
+ // provide unique ID
+ sal_uInt32 MetafilePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_METAFILEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
new file mode 100644
index 000000000..2d0f572da
--- /dev/null
+++ b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx
@@ -0,0 +1,67 @@
+/* -*- 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/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ ModifiedColorPrimitive2D::ModifiedColorPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const basegfx::BColorModifierSharedPtr& rColorModifier)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maColorModifier(rColorModifier)
+ {
+ }
+
+ bool ModifiedColorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const ModifiedColorPrimitive2D& rCompare = static_cast<const ModifiedColorPrimitive2D&>(rPrimitive);
+
+ if(getColorModifier().get() == rCompare.getColorModifier().get())
+ {
+ return true;
+ }
+
+ if(!getColorModifier() || !rCompare.getColorModifier())
+ {
+ return false;
+ }
+
+ return *getColorModifier()== *rCompare.getColorModifier();
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 ModifiedColorPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
new file mode 100644
index 000000000..577f3171a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx
@@ -0,0 +1,60 @@
+/* -*- 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/objectinfoprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ ObjectInfoPrimitive2D::ObjectInfoPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDesc)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maName(rName),
+ maTitle(rTitle),
+ maDesc(rDesc)
+ {
+ }
+
+ bool ObjectInfoPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const ObjectInfoPrimitive2D& rCompare = static_cast<const ObjectInfoPrimitive2D&>(rPrimitive);
+
+ return (getName() == rCompare.getName()
+ && getTitle() == rCompare.getTitle()
+ && getDesc() == rCompare.getDesc());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 ObjectInfoPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx
new file mode 100644
index 000000000..8ffd7735a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx
@@ -0,0 +1,40 @@
+/* -*- 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/pagehierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+ PageHierarchyPrimitive2D::PageHierarchyPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 PageHierarchyPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
new file mode 100644
index 000000000..7aa1a4405
--- /dev/null
+++ b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx
@@ -0,0 +1,150 @@
+/* -*- 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/pagepreviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void PagePreviewPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aContent(getPageContent());
+
+ if(!(!aContent.empty()
+ && basegfx::fTools::more(getContentWidth(), 0.0)
+ && basegfx::fTools::more(getContentHeight(), 0.0)))
+ return;
+
+ // 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;
+
+ // check if content overlaps with target size and needs to be embedded with a
+ // clipping primitive
+ const basegfx::B2DRange aRealContentRange(aContent.getB2DRange(rViewInformation));
+ const basegfx::B2DRange aAllowedContentRange(0.0, 0.0, getContentWidth(), getContentHeight());
+
+ if(!aAllowedContentRange.isInside(aRealContentRange))
+ {
+ const Primitive2DReference xReferenceA(
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(aAllowedContentRange)), std::move(aContent)));
+ aContent = Primitive2DContainer { xReferenceA };
+ }
+
+ // create a mapping from content to object.
+ basegfx::B2DHomMatrix aPageTrans;
+
+ // #i101075# when keeping the aspect ratio is wanted, it is necessary to calculate
+ // an equidistant scaling in X and Y and a corresponding translation to
+ // center the output. Calculate needed scale factors
+ const double fScaleX(aScale.getX() / getContentWidth());
+ const double fScaleY(aScale.getY() / getContentHeight());
+
+ // to keep the aspect, use the smaller scale and adapt missing size by translation
+ if(fScaleX < fScaleY)
+ {
+ // height needs to be adapted
+ const double fNeededHeight(aScale.getY() / fScaleX);
+ const double fSpaceToAdd(fNeededHeight - getContentHeight());
+
+ aPageTrans.translate(0.0, fSpaceToAdd * 0.5);
+ aPageTrans.scale(fScaleX, aScale.getY() / fNeededHeight);
+ }
+ else
+ {
+ // width needs to be adapted
+ const double fNeededWidth(aScale.getX() / fScaleY);
+ const double fSpaceToAdd(fNeededWidth - getContentWidth());
+
+ aPageTrans.translate(fSpaceToAdd * 0.5, 0.0);
+ aPageTrans.scale(aScale.getX() / fNeededWidth, fScaleY);
+ }
+
+ // add the missing object transformation aspects
+ const basegfx::B2DHomMatrix aCombined(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+ aPageTrans = aCombined * aPageTrans;
+
+ // embed in necessary transformation to map from SdrPage to SdrPageObject
+ rContainer.push_back(new TransformPrimitive2D(aPageTrans, std::move(aContent)));
+ }
+
+ PagePreviewPrimitive2D::PagePreviewPrimitive2D(
+ const css::uno::Reference< css::drawing::XDrawPage >& rxDrawPage,
+ const basegfx::B2DHomMatrix& rTransform,
+ double fContentWidth,
+ double fContentHeight,
+ Primitive2DContainer&& rPageContent)
+ : mxDrawPage(rxDrawPage),
+ maPageContent(std::move(rPageContent)),
+ maTransform(rTransform),
+ mfContentWidth(fContentWidth),
+ mfContentHeight(fContentHeight)
+ {
+ }
+
+ bool PagePreviewPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PagePreviewPrimitive2D& rCompare = static_cast< const PagePreviewPrimitive2D& >(rPrimitive);
+
+ return (getXDrawPage() == rCompare.getXDrawPage()
+ && getPageContent() == rCompare.getPageContent()
+ && getTransform() == rCompare.getTransform()
+ && getContentWidth() == rCompare.getContentWidth()
+ && getContentHeight() == rCompare.getContentHeight());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange PagePreviewPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation`*/) const
+ {
+ // nothing is allowed to stick out of a PagePreviewPrimitive, thus we
+ // can quickly deliver our range here
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(getTransform());
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 PagePreviewPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
new file mode 100644
index 000000000..b667545b5
--- /dev/null
+++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
@@ -0,0 +1,370 @@
+/* -*- 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/patternfillprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <texture/texture.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <drawinglayer/converters.hxx>
+
+using namespace com::sun::star;
+
+#define MAXIMUM_SQUARE_LENGTH (186.0)
+#define MINIMUM_SQUARE_LENGTH (16.0)
+#define MINIMUM_TILES_LENGTH (3)
+
+namespace drawinglayer::primitive2d
+{
+ void PatternFillPrimitive2D::calculateNeededDiscreteBufferSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // reset parameters
+ rWidth = rHeight = 0;
+
+ // check if resolution is in the range which may be buffered
+ const basegfx::B2DPolyPolygon& rMaskPolygon = getMask();
+ const basegfx::B2DRange aMaskRange(rMaskPolygon.getB2DRange());
+
+ // get discrete rounded up square size of a single tile
+ const basegfx::B2DHomMatrix aMaskRangeTransformation(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+ const basegfx::B2DHomMatrix aTransform(
+ rViewInformation.getObjectToViewTransformation() * aMaskRangeTransformation);
+ const basegfx::B2DPoint aTopLeft(aTransform * getReferenceRange().getMinimum());
+ const basegfx::B2DPoint aX(aTransform * basegfx::B2DPoint(getReferenceRange().getMaxX(), getReferenceRange().getMinY()));
+ const basegfx::B2DPoint aY(aTransform * basegfx::B2DPoint(getReferenceRange().getMinX(), getReferenceRange().getMaxY()));
+ const double fW(basegfx::B2DVector(aX - aTopLeft).getLength());
+ const double fH(basegfx::B2DVector(aY - aTopLeft).getLength());
+ const double fSquare(fW * fH);
+
+ if(fSquare <= 0.0)
+ return;
+
+ // check if less than a maximum square pixels is used
+ static const sal_uInt32 fMaximumSquare(MAXIMUM_SQUARE_LENGTH * MAXIMUM_SQUARE_LENGTH);
+
+ if(fSquare >= fMaximumSquare)
+ return;
+
+ // calculate needed number of tiles and check if used more than a minimum count
+ const texture::GeoTexSvxTiled aTiling(getReferenceRange());
+ const sal_uInt32 nTiles(aTiling.getNumberOfTiles());
+ static const sal_uInt32 nMinimumTiles(MINIMUM_TILES_LENGTH * MINIMUM_TILES_LENGTH);
+
+ if(nTiles < nMinimumTiles)
+ return;
+
+ rWidth = basegfx::fround(ceil(fW));
+ rHeight = basegfx::fround(ceil(fH));
+ static const sal_uInt32 fMinimumSquare(MINIMUM_SQUARE_LENGTH * MINIMUM_SQUARE_LENGTH);
+
+ if(fSquare < fMinimumSquare)
+ {
+ const double fRel(fW/fH);
+ rWidth = basegfx::fround(sqrt(fMinimumSquare * fRel));
+ rHeight = basegfx::fround(sqrt(fMinimumSquare / fRel));
+ }
+ }
+
+ void PatternFillPrimitive2D::getTileSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ const basegfx::B2DRange aMaskRange(getMask().getB2DRange());
+
+ // get discrete rounded up square size of a single tile
+ const basegfx::B2DHomMatrix aMaskRangeTransformation(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+ const basegfx::B2DHomMatrix aTransform(
+ rViewInformation.getObjectToViewTransformation() * aMaskRangeTransformation);
+ const basegfx::B2DPoint aTopLeft(aTransform * getReferenceRange().getMinimum());
+ const basegfx::B2DPoint aX(aTransform * basegfx::B2DPoint(getReferenceRange().getMaxX(), getReferenceRange().getMinY()));
+ const basegfx::B2DPoint aY(aTransform * basegfx::B2DPoint(getReferenceRange().getMinX(), getReferenceRange().getMaxY()));
+ const double fW(basegfx::B2DVector(aX - aTopLeft).getLength());
+ const double fH(basegfx::B2DVector(aY - aTopLeft).getLength());
+
+ rWidth = basegfx::fround(ceil(fW));
+ rHeight = basegfx::fround(ceil(fH));
+ }
+
+ Primitive2DContainer PatternFillPrimitive2D::createContent(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aContent;
+
+ // see if buffering is wanted. If so, create buffered content in given resolution
+ 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 };
+
+ const BitmapEx aBitmapEx(
+ convertToBitmapEx(
+ std::move(xEmbedSeq),
+ aViewInformation2D,
+ mnDiscreteWidth,
+ mnDiscreteHeight,
+ mnDiscreteWidth * mnDiscreteHeight));
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ const Size& rBmpPix = aBitmapEx.GetSizePixel();
+
+ if(rBmpPix.Width() > 0 && rBmpPix.Height() > 0)
+ {
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new primitive2d::BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aBitmapEx),
+ basegfx::B2DHomMatrix()));
+ aContent = primitive2d::Primitive2DContainer { xEmbedRefBitmap };
+ }
+ }
+ }
+
+ if(aContent.empty())
+ {
+ // buffering was not tried or did fail - reset remembered buffered size
+ // in any case
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = pThat->mnDiscreteHeight = 0;
+
+ // use children as default context
+ aContent = getChildren();
+
+ // check if content needs to be clipped
+ const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
+ const basegfx::B2DRange aContentRange(aContent.getB2DRange(rViewInformation));
+
+ if(!aUnitRange.isInside(aContentRange))
+ {
+ const Primitive2DReference xRef(
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aUnitRange)),
+ std::move(aContent)));
+
+ aContent = Primitive2DContainer { xRef };
+ }
+ }
+
+ return aContent;
+ }
+
+ // create buffered content in given resolution
+ BitmapEx PatternFillPrimitive2D::createTileImage(sal_uInt32 nWidth, sal_uInt32 nHeight) const
+ {
+ const geometry::ViewInformation2D aViewInformation2D;
+ Primitive2DContainer aContent(createContent(aViewInformation2D));
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ basegfx::utils::createScaleB2DHomMatrix(nWidth, nHeight),
+ std::move(aContent)));
+ primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ return convertToBitmapEx(
+ std::move(xEmbedSeq),
+ aViewInformation2D,
+ nWidth,
+ nHeight,
+ nWidth * nHeight);
+ }
+
+ void PatternFillPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+
+ if(getChildren().empty())
+ return;
+
+ if(!(!getReferenceRange().isEmpty() && getReferenceRange().getWidth() > 0.0 && getReferenceRange().getHeight() > 0.0))
+ return;
+
+ const basegfx::B2DRange aMaskRange(getMask().getB2DRange());
+
+ if(!(!aMaskRange.isEmpty() && aMaskRange.getWidth() > 0.0 && aMaskRange.getHeight() > 0.0))
+ return;
+
+ // create tiling matrices
+ std::vector< basegfx::B2DHomMatrix > aMatrices;
+ texture::GeoTexSvxTiled aTiling(getReferenceRange());
+
+ aTiling.appendTransformations(aMatrices);
+
+ // create content
+ Primitive2DContainer aContent(createContent(rViewInformation));
+
+ // resize result
+ aRetval.resize(aMatrices.size());
+
+ // create one primitive for each matrix
+ for(size_t a(0); a < aMatrices.size(); a++)
+ {
+ aRetval[a] = new TransformPrimitive2D(
+ aMatrices[a],
+ Primitive2DContainer(aContent));
+ }
+
+ // transform result which is in unit coordinates to mask's object coordinates
+ {
+ const basegfx::B2DHomMatrix aMaskTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aMaskRange.getRange(),
+ aMaskRange.getMinimum()));
+
+ Primitive2DReference xRef(
+ new TransformPrimitive2D(
+ aMaskTransform,
+ std::move(aRetval)));
+
+ aRetval = Primitive2DContainer { xRef };
+ }
+
+ // embed result in mask
+ {
+ rContainer.push_back(
+ new MaskPrimitive2D(
+ getMask(),
+ std::move(aRetval)));
+ }
+ }
+
+ PatternFillPrimitive2D::PatternFillPrimitive2D(
+ const basegfx::B2DPolyPolygon& rMask,
+ Primitive2DContainer&& rChildren,
+ const basegfx::B2DRange& rReferenceRange)
+ : maMask(rMask),
+ maChildren(std::move(rChildren)),
+ maReferenceRange(rReferenceRange),
+ mnDiscreteWidth(0),
+ mnDiscreteHeight(0)
+ {
+ }
+
+ bool PatternFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PatternFillPrimitive2D& rCompare = static_cast< const PatternFillPrimitive2D& >(rPrimitive);
+
+ return (getMask() == rCompare.getMask()
+ && getChildren() == rCompare.getChildren()
+ && getReferenceRange() == rCompare.getReferenceRange());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange PatternFillPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /* rViewInformation */ ) const
+ {
+ return getMask().getB2DRange();
+ }
+
+ void PatternFillPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // The existing buffered decomposition uses a buffer in the remembered
+ // size or none if sizes are zero. Get new needed sizes which depend on
+ // the given ViewInformation
+ bool bResetBuffering = false;
+ sal_uInt32 nW(0);
+ sal_uInt32 nH(0);
+ calculateNeededDiscreteBufferSize(nW, nH, rViewInformation);
+ const bool bBufferingCurrentlyUsed(0 != mnDiscreteWidth && 0 != mnDiscreteHeight);
+ const bool bBufferingNextUsed(0 != nW && 0 != nH);
+
+ if(bBufferingNextUsed)
+ {
+ // buffering is now possible
+ if(bBufferingCurrentlyUsed)
+ {
+ if(nW > mnDiscreteWidth || nH > mnDiscreteHeight)
+ {
+ // Higher resolution is needed than used in the existing buffered
+ // decomposition - create new one
+ bResetBuffering = true;
+ }
+ else if(double(nW * nH) / double(mnDiscreteWidth * mnDiscreteHeight) <= 0.5)
+ {
+ // Size has shrunk for 50% or more - it's worth to refresh the buffering
+ // to spare some resources
+ bResetBuffering = true;
+ }
+ }
+ else
+ {
+ // currently no buffering used - reset evtl. unbuffered
+ // decomposition to start buffering
+ bResetBuffering = true;
+ }
+ }
+ else
+ {
+ // buffering is no longer possible
+ if(bBufferingCurrentlyUsed)
+ {
+ // reset decomposition to allow creation of unbuffered one
+ bResetBuffering = true;
+ }
+ }
+
+ if(bResetBuffering)
+ {
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = nW;
+ pThat->mnDiscreteHeight = nH;
+ pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ // call parent
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ sal_Int64 PatternFillPrimitive2D::estimateUsage()
+ {
+ size_t nRet(0);
+ for (auto& it : getChildren())
+ if (it)
+ nRet += it->estimateUsage();
+ return nRet;
+ }
+
+ // provide unique ID
+ sal_uInt32 PatternFillPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx
new file mode 100644
index 000000000..6299e1850
--- /dev/null
+++ b/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx
@@ -0,0 +1,77 @@
+/* -*- 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/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ PointArrayPrimitive2D::PointArrayPrimitive2D(
+ std::vector< basegfx::B2DPoint >&& rPositions,
+ const basegfx::BColor& rRGBColor)
+ : maPositions(std::move(rPositions)),
+ maRGBColor(rRGBColor)
+ {
+ }
+
+ bool PointArrayPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PointArrayPrimitive2D& rCompare = static_cast<const PointArrayPrimitive2D&>(rPrimitive);
+
+ return (getPositions() == rCompare.getPositions()
+ && getRGBColor() == rCompare.getRGBColor());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange PointArrayPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(maB2DRange.isEmpty())
+ {
+ basegfx::B2DRange aNewRange;
+
+ // get the basic range from the position vector
+ for (auto const& pos : getPositions())
+ {
+ aNewRange.expand(pos);
+ }
+
+ // assign to buffered value
+ const_cast< PointArrayPrimitive2D* >(this)->maB2DRange = aNewRange;
+ }
+
+ return maB2DRange;
+ }
+
+ // provide unique ID
+ sal_uInt32 PointArrayPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
new file mode 100644
index 000000000..1cbde11ae
--- /dev/null
+++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx
@@ -0,0 +1,611 @@
+/* -*- 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 <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/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <com/sun/star/drawing/LineCap.hpp>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const basegfx::BColor& rBColor)
+ : maPolygon(rPolygon)
+ , maBColor(rBColor)
+{
+}
+
+bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonHairlinePrimitive2D& rCompare
+ = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolygon() == rCompare.getB2DPolygon() && getBColor() == rCompare.getBColor());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // 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);
+ }
+ }
+
+ // return range
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonHairlinePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D;
+}
+
+void PolygonMarkerPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+{
+ // calculate logic DashLength
+ const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(getDiscreteDashLength(), 0.0));
+ const double fLogicDashLength(aDashVector.getX());
+
+ if (fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB()))
+ {
+ // apply dashing; get line and gap snippets
+ std::vector<double> aDash;
+ basegfx::B2DPolyPolygon aDashedPolyPolyA;
+ basegfx::B2DPolyPolygon aDashedPolyPolyB;
+
+ aDash.push_back(fLogicDashLength);
+ aDash.push_back(fLogicDashLength);
+ basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA,
+ &aDashedPolyPolyB, 2.0 * fLogicDashLength);
+
+ rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA, getRGBColorA()));
+ rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB, getRGBColorB()));
+ }
+ else
+ {
+ rContainer.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()));
+ }
+}
+
+PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maPolygon(rPolygon)
+ , maRGBColorA(rRGBColorA)
+ , maRGBColorB(rRGBColorB)
+ , mfDiscreteDashLength(fDiscreteDashLength)
+{
+}
+
+bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonMarkerPrimitive2D& rCompare
+ = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive);
+
+ return (getB2DPolygon() == rCompare.getB2DPolygon()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // 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);
+ }
+ }
+
+ // return range
+ return aRetval;
+}
+
+void PolygonMarkerPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ bool bNeedNewDecomposition(false);
+
+ if (!getBuffered2DDecomposition().empty())
+ {
+ if (rViewInformation.getInverseObjectToViewTransformation()
+ != maLastInverseObjectToViewTransformation)
+ {
+ bNeedNewDecomposition = true;
+ }
+ }
+
+ if (bNeedNewDecomposition)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast<PolygonMarkerPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // remember last used InverseObjectToViewTransformation
+ PolygonMarkerPrimitive2D* pThat = const_cast<PolygonMarkerPrimitive2D*>(this);
+ pThat->maLastInverseObjectToViewTransformation
+ = rViewInformation.getInverseObjectToViewTransformation();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+// provide unique ID
+sal_uInt32 PolygonMarkerPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D;
+}
+
+} // end of namespace
+
+namespace drawinglayer::primitive2d
+{
+void PolygonStrokePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getB2DPolygon().count())
+ return;
+
+ // #i102241# try to simplify before usage
+ const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon()));
+ basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+
+ if (getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen())
+ {
+ // no line dashing, just copy
+ aHairLinePolyPolygon.append(aB2DPolygon);
+ }
+ else
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(aB2DPolygon, getStrokeAttribute().getDotDashArray(),
+ &aHairLinePolyPolygon, nullptr,
+ getStrokeAttribute().getFullDotDashLen());
+ }
+
+ const sal_uInt32 nCount(aHairLinePolyPolygon.count());
+
+ if (!getLineAttribute().isDefault() && getLineAttribute().getWidth())
+ {
+ // create fat line data
+ const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0);
+ const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin());
+ const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap());
+ basegfx::B2DPolyPolygon aAreaPolyPolygon;
+ const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ // New version of createAreaGeometry; now creates bezier polygons
+ aAreaPolyPolygon.append(basegfx::utils::createAreaGeometry(
+ aHairLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, aLineJoin, aLineCap,
+ basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/,
+ 0.4 /* default fMaxPartOfEdge*/, fMiterMinimumAngle));
+ }
+
+ // create primitive
+ 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));
+ const basegfx::BColor aColor(getLineAttribute().getColor());
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(aNewPolyPolygon, aColor));
+ }
+ }
+ else
+ {
+ rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aHairLinePolyPolygon,
+ getLineAttribute().getColor()));
+ }
+}
+
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute)
+ : maPolygon(rPolygon)
+ , maLineAttribute(rLineAttribute)
+ , maStrokeAttribute(rStrokeAttribute)
+{
+ // MM01: keep these - these are no curve-decompposers but just checks
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+}
+
+PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute)
+ : maPolygon(rPolygon)
+ , maLineAttribute(rLineAttribute)
+{
+ // MM01: keep these - these are no curve-decompposers but just checks
+ // simplify curve segments: moved here to not need to use it
+ // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect
+ maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon);
+}
+
+bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonStrokePrimitive2D& rCompare
+ = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive);
+
+ return (getB2DPolygon() == rCompare.getB2DPolygon()
+ && getLineAttribute() == rCompare.getLineAttribute()
+ && getStrokeAttribute() == rCompare.getStrokeAttribute());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DRange aRetval;
+
+ if (getLineAttribute().getWidth())
+ {
+ bool bUseDecomposition(false);
+
+ if (basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin())
+ {
+ // if line is mitered, use parent call since mitered line
+ // geometry may use more space than the geometry grown by half line width
+ bUseDecomposition = true;
+ }
+
+ if (!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap())
+ {
+ // when drawing::LineCap_SQUARE is used the below method to grow the polygon
+ // range by half line width will not work, so use decomposition. Interestingly,
+ // 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
+ bUseDecomposition = true;
+ }
+
+ if (bUseDecomposition)
+ {
+ // 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;
+ }
+ 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);
+ }
+ }
+ else
+ {
+ // this is a hairline, thus the line width is view-dependent. Get range of polygon
+ // as base size
+ 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);
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonStrokePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D;
+}
+
+void PolygonWavePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getB2DPolygon().count())
+ return;
+
+ const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth()));
+ const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight()));
+
+ if (bHasWidth && bHasHeight)
+ {
+ // create waveline curve
+ const basegfx::B2DPolygon aWaveline(
+ basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight()));
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(aWaveline, getLineAttribute(), getStrokeAttribute()));
+ }
+ else
+ {
+ // flat waveline, decompose to simple line primitive
+ rContainer.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(),
+ getStrokeAttribute()));
+ }
+}
+
+PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute,
+ double fWaveWidth, double fWaveHeight)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
+ , mfWaveWidth(fWaveWidth)
+ , mfWaveHeight(fWaveHeight)
+{
+ if (mfWaveWidth < 0.0)
+ {
+ mfWaveWidth = 0.0;
+ }
+
+ if (mfWaveHeight < 0.0)
+ {
+ mfWaveHeight = 0.0;
+ }
+}
+
+PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon,
+ const attribute::LineAttribute& rLineAttribute,
+ double fWaveWidth, double fWaveHeight)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
+ , mfWaveWidth(fWaveWidth)
+ , mfWaveHeight(fWaveHeight)
+{
+ if (mfWaveWidth < 0.0)
+ {
+ mfWaveWidth = 0.0;
+ }
+
+ if (mfWaveHeight < 0.0)
+ {
+ mfWaveHeight = 0.0;
+ }
+}
+
+bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (PolygonStrokePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonWavePrimitive2D& rCompare
+ = static_cast<const PolygonWavePrimitive2D&>(rPrimitive);
+
+ return (getWaveWidth() == rCompare.getWaveWidth()
+ && getWaveHeight() == rCompare.getWaveHeight());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // get range of parent
+ basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation));
+
+ // if WaveHeight, grow by it
+ if (basegfx::fTools::more(getWaveHeight(), 0.0))
+ {
+ aRetval.grow(getWaveHeight());
+ }
+
+ // if line width, grow by it
+ if (basegfx::fTools::more(getLineAttribute().getWidth(), 0.0))
+ {
+ aRetval.grow(getLineAttribute().getWidth() * 0.5);
+ }
+
+ return aRetval;
+}
+
+// provide unique ID
+sal_uInt32 PolygonWavePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D;
+}
+
+void PolygonStrokeArrowPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // copy local polygon, it may be changed
+ basegfx::B2DPolygon aLocalPolygon(getB2DPolygon());
+ aLocalPolygon.removeDoublePoints();
+ basegfx::B2DPolyPolygon aArrowA;
+ basegfx::B2DPolyPolygon aArrowB;
+
+ if (!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1)
+ {
+ // apply arrows
+ const double fPolyLength(basegfx::utils::getLength(aLocalPolygon));
+ double fStart(0.0);
+ double fEnd(0.0);
+ double fStartOverlap(0.0);
+ double fEndOverlap(0.0);
+
+ if (!getStart().isDefault() && getStart().isActive())
+ {
+ // create start arrow primitive and consume
+ aArrowA = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(),
+ fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart);
+
+ // create some overlapping, compromise between straight and peaked markers
+ // for marker width 0.3cm and marker line width 0.02cm
+ fStartOverlap = getStart().getWidth() / 15.0;
+ }
+
+ if (!getEnd().isDefault() && getEnd().isActive())
+ {
+ // create end arrow primitive and consume
+ aArrowB = basegfx::utils::createAreaGeometryForLineStartEnd(
+ aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(),
+ fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd);
+
+ // create some overlapping
+ fEndOverlap = getEnd().getWidth() / 15.0;
+ }
+
+ if (0.0 != fStart || 0.0 != fEnd)
+ {
+ // build new poly, consume something from old poly
+ aLocalPolygon
+ = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart - fStartOverlap,
+ fPolyLength - fEnd + fEndOverlap, fPolyLength);
+ }
+ }
+
+ // add shaft
+ rContainer.push_back(
+ new PolygonStrokePrimitive2D(aLocalPolygon, getLineAttribute(), getStrokeAttribute()));
+
+ if (aArrowA.count())
+ {
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(aArrowA, getLineAttribute().getColor()));
+ }
+
+ if (aArrowB.count())
+ {
+ rContainer.push_back(
+ new PolyPolygonColorPrimitive2D(aArrowB, getLineAttribute().getColor()));
+ }
+}
+
+PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::StrokeAttribute& rStrokeAttribute,
+ const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute)
+ , maStart(rStart)
+ , maEnd(rEnd)
+{
+}
+
+PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D(
+ const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute,
+ const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd)
+ : PolygonStrokePrimitive2D(rPolygon, rLineAttribute)
+ , maStart(rStart)
+ , maEnd(rEnd)
+{
+}
+
+bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (PolygonStrokePrimitive2D::operator==(rPrimitive))
+ {
+ const PolygonStrokeArrowPrimitive2D& rCompare
+ = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive);
+
+ return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getStart().isActive() || getEnd().isActive())
+ {
+ // use decomposition when line start/end is used
+ return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // get range from parent
+ return PolygonStrokePrimitive2D::getB2DRange(rViewInformation);
+ }
+}
+
+// provide unique ID
+sal_uInt32 PolygonStrokeArrowPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/primitivetools2d.cxx b/drawinglayer/source/primitive2d/primitivetools2d.cxx
new file mode 100644
index 000000000..7c6d426e9
--- /dev/null
+++ b/drawinglayer/source/primitive2d/primitivetools2d.cxx
@@ -0,0 +1,127 @@
+/* -*- 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/primitivetools2d.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void DiscreteMetricDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // 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()))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< DiscreteMetricDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid DiscreteUnit
+ const_cast< DiscreteMetricDependentPrimitive2D* >(this)->mfDiscreteUnit = fDiscreteUnit;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+
+
+
+ void ViewportDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current Viewport
+ const basegfx::B2DRange& rViewport = rViewInformation.getViewport();
+
+ if(!getBuffered2DDecomposition().empty() && !rViewport.equal(getViewport()))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ViewportDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid DiscreteUnit
+ const_cast< ViewportDependentPrimitive2D* >(this)->maViewport = rViewport;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ void ViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current ViewTransformation
+ const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation();
+
+ if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid ViewTransformation
+ const_cast< ViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ void ObjectAndViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // get the current ViewTransformation
+ const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation();
+
+ if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ // get the current ObjectTransformation
+ const basegfx::B2DHomMatrix& rObjectTransformation = rViewInformation.getObjectTransformation();
+
+ if(!getBuffered2DDecomposition().empty() && rObjectTransformation != getObjectTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember new valid ViewTransformation, and ObjectTransformation
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation;
+ const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->maObjectTransformation = rObjectTransformation;
+ }
+
+ // call base implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
new file mode 100644
index 000000000..0bec2258d
--- /dev/null
+++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx
@@ -0,0 +1,689 @@
+/* -*- 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/sceneprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.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 <basegfx/raster/bzpixelraster.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <comphelper/threadpool.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+ BitmapEx BPixelRasterToBitmapEx(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize)
+ {
+ BitmapEx aRetval;
+ const sal_uInt32 nWidth(mnAntiAlialize ? rRaster.getWidth()/mnAntiAlialize : rRaster.getWidth());
+ const sal_uInt32 nHeight(mnAntiAlialize ? rRaster.getHeight()/mnAntiAlialize : rRaster.getHeight());
+
+ if(nWidth && nHeight)
+ {
+ const Size aDestSize(nWidth, nHeight);
+ vcl::bitmap::RawBitmap aContent(aDestSize, 32);
+
+ if(mnAntiAlialize)
+ {
+ const sal_uInt16 nDivisor(mnAntiAlialize * mnAntiAlialize);
+
+ for(sal_uInt32 y(0); y < nHeight; y++)
+ {
+ for(sal_uInt32 x(0); x < nWidth; x++)
+ {
+ sal_uInt16 nRed(0);
+ sal_uInt16 nGreen(0);
+ sal_uInt16 nBlue(0);
+ sal_uInt16 nAlpha(0);
+ sal_uInt32 nIndex(rRaster.getIndexFromXY(x * mnAntiAlialize, y * mnAntiAlialize));
+
+ for(sal_uInt32 c(0); c < mnAntiAlialize; c++)
+ {
+ for(sal_uInt32 d(0); d < mnAntiAlialize; d++)
+ {
+ const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
+ nRed += rPixel.getRed();
+ nGreen += rPixel.getGreen();
+ nBlue += rPixel.getBlue();
+ nAlpha += rPixel.getAlpha();
+ }
+
+ nIndex += rRaster.getWidth() - mnAntiAlialize;
+ }
+
+ nAlpha /= nDivisor;
+
+ if(nAlpha)
+ {
+ aContent.SetPixel(y, x, Color(ColorAlpha,
+ static_cast<sal_uInt8>(nAlpha),
+ static_cast<sal_uInt8>(nRed / nDivisor),
+ static_cast<sal_uInt8>(nGreen / nDivisor),
+ static_cast<sal_uInt8>(nBlue / nDivisor) ));
+ }
+ else
+ aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
+ }
+ }
+ }
+ else
+ {
+ sal_uInt32 nIndex(0);
+
+ for(sal_uInt32 y(0); y < nHeight; y++)
+ {
+ for(sal_uInt32 x(0); x < nWidth; x++)
+ {
+ const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++));
+
+ if(rPixel.getAlpha())
+ {
+ aContent.SetPixel(y, x, Color(ColorAlpha, rPixel.getAlpha(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue()));
+ }
+ else
+ aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0));
+ }
+ }
+ }
+
+ aRetval = vcl::bitmap::CreateFromData(std::move(aContent));
+
+ // #i101811# set PrefMapMode and PrefSize at newly created Bitmap
+ aRetval.SetPrefMapMode(MapMode(MapUnit::MapPixel));
+ aRetval.SetPrefSize(Size(nWidth, nHeight));
+ }
+
+ return aRetval;
+ }
+} // end of anonymous namespace
+
+namespace drawinglayer::primitive2d
+{
+ bool ScenePrimitive2D::impGetShadow3D() const
+ {
+ // create on demand
+ if(!mbShadow3DChecked && !getChildren3D().empty())
+ {
+ basegfx::B3DVector aLightNormal;
+ const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
+ const basegfx::B3DRange aScene3DRange(getChildren3D().getB3DRange(getViewInformation3D()));
+
+ if(!maSdrLightingAttribute.getLightVector().empty())
+ {
+ // get light normal from first light and normalize
+ aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
+ aLightNormal.normalize();
+ }
+
+ // create shadow extraction processor
+ processor3d::Shadow3DExtractingProcessor aShadowProcessor(
+ getViewInformation3D(),
+ getObjectTransformation(),
+ aLightNormal,
+ fShadowSlant,
+ aScene3DRange);
+
+ // process local primitives
+ aShadowProcessor.process(getChildren3D());
+
+ // fetch result and set checked flag
+ const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
+ const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
+ }
+
+ // return if there are shadow primitives
+ return !maShadowPrimitives.empty();
+ }
+
+ void ScenePrimitive2D::calculateDiscreteSizes(
+ const geometry::ViewInformation2D& rViewInformation,
+ basegfx::B2DRange& rDiscreteRange,
+ basegfx::B2DRange& rVisibleDiscreteRange,
+ basegfx::B2DRange& rUnitVisibleRange) const
+ {
+ // use unit range and transform to discrete coordinates
+ rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
+ rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // clip it against discrete Viewport (if set)
+ rVisibleDiscreteRange = rDiscreteRange;
+
+ if(!rViewInformation.getViewport().isEmpty())
+ {
+ rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
+ }
+
+ if(rVisibleDiscreteRange.isEmpty())
+ {
+ rUnitVisibleRange = rVisibleDiscreteRange;
+ }
+ else
+ {
+ // create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
+ // the relative position of rVisibleDiscreteRange inside rDiscreteRange
+ const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
+ const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
+
+ const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
+ ? 0.0
+ : (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
+ const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
+ ? 0.0
+ : (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
+
+ const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
+ ? 1.0
+ : (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
+ const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
+ ? 1.0
+ : (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
+
+ rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
+ }
+ }
+
+ void ScenePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // 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())
+ {
+ // test visibility
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+ const basegfx::B2DRange aViewRange(
+ rViewInformation.getViewport());
+
+ if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
+ {
+ // add extracted 2d shadows (before 3d scene creations itself)
+ rContainer.append(maShadowPrimitives);
+ }
+ }
+
+ // get the involved ranges (see helper method calculateDiscreteSizes for details)
+ basegfx::B2DRange aDiscreteRange;
+ basegfx::B2DRange aVisibleDiscreteRange;
+ basegfx::B2DRange aUnitVisibleRange;
+
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+
+ if(aVisibleDiscreteRange.isEmpty())
+ return;
+
+ // 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());
+ double fReduceFactor(1.0);
+
+ if(fViewVisibleArea > fMaximumVisibleArea)
+ {
+ fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
+ fViewSizeX *= fReduceFactor;
+ fViewSizeY *= fReduceFactor;
+ }
+
+ if(rViewInformation.getReducedDisplayQuality())
+ {
+ // when reducing the visualisation is allowed (e.g. an OverlayObject
+ // only needed for dragging), reduce resolution extra
+ // to speed up dragging interactions
+ const double fArea(fViewSizeX * fViewSizeY);
+ if (fArea != 0.0)
+ {
+ double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
+
+ if(fReducedVisualisationFactor > 1.0)
+ {
+ fReducedVisualisationFactor = 1.0;
+ }
+ else if(fReducedVisualisationFactor < 0.20)
+ {
+ fReducedVisualisationFactor = 0.20;
+ }
+
+ if(fReducedVisualisationFactor != 1.0)
+ {
+ fReduceFactor *= fReducedVisualisationFactor;
+ }
+ }
+ }
+
+ // determine the oversample value
+ static const sal_uInt16 nDefaultOversampleValue(3);
+ const sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0);
+
+ geometry::ViewInformation3D aViewInformation3D(getViewInformation3D());
+ {
+ // calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
+ // Start with full transformation from object to discrete units
+ basegfx::B2DHomMatrix aObjToUnit(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // bring to unit coordinates by applying inverse DiscreteRange
+ aObjToUnit.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY());
+ if (aDiscreteRange.getWidth() != 0.0 && aDiscreteRange.getHeight() != 0.0)
+ {
+ aObjToUnit.scale(1.0 / aDiscreteRange.getWidth(), 1.0 / aDiscreteRange.getHeight());
+ }
+
+ // calculate transformed user coordinate system
+ const basegfx::B2DPoint aStandardNull(0.0, 0.0);
+ const basegfx::B2DPoint aUnitRangeTopLeft(aObjToUnit * aStandardNull);
+ const basegfx::B2DVector aStandardXAxis(1.0, 0.0);
+ const basegfx::B2DVector aUnitRangeXAxis(aObjToUnit * aStandardXAxis);
+ const basegfx::B2DVector aStandardYAxis(0.0, 1.0);
+ const basegfx::B2DVector aUnitRangeYAxis(aObjToUnit * aStandardYAxis);
+
+ if(!aUnitRangeTopLeft.equal(aStandardNull) || !aUnitRangeXAxis.equal(aStandardXAxis) || !aUnitRangeYAxis.equal(aStandardYAxis))
+ {
+ // build transformation from unit range to user coordinate system; the unit range
+ // X and Y axes are the column vectors, the null point is the offset
+ basegfx::B2DHomMatrix aUnitRangeToUser;
+
+ aUnitRangeToUser.set3x2(
+ aUnitRangeXAxis.getX(), aUnitRangeYAxis.getX(), aUnitRangeTopLeft.getX(),
+ aUnitRangeXAxis.getY(), aUnitRangeYAxis.getY(), aUnitRangeTopLeft.getY());
+
+ // decompose to allow to apply this to the 3D transformation
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aUnitRangeToUser.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
+ // and not yet flipped in Y
+ basegfx::B3DHomMatrix aExtendedProjection(aViewInformation3D.getProjection());
+
+ // bring to unit coordinates, flip Y, leave Z unchanged
+ aExtendedProjection.scale(0.5, -0.5, 1.0);
+ aExtendedProjection.translate(0.5, 0.5, 0.0);
+
+ // apply extra; Y is flipped now, go with positive shear and rotate values
+ aExtendedProjection.scale(aScale.getX(), aScale.getY(), 1.0);
+ aExtendedProjection.shearXZ(fShearX, 0.0);
+ aExtendedProjection.rotate(0.0, 0.0, fRotate);
+ aExtendedProjection.translate(aTranslate.getX(), aTranslate.getY(), 0.0);
+
+ // back to state after projection
+ aExtendedProjection.translate(-0.5, -0.5, 0.0);
+ aExtendedProjection.scale(2.0, -2.0, 1.0);
+
+ aViewInformation3D = geometry::ViewInformation3D(
+ aViewInformation3D.getObjectTransformation(),
+ aViewInformation3D.getOrientation(),
+ aExtendedProjection,
+ aViewInformation3D.getDeviceToView(),
+ aViewInformation3D.getViewTime(),
+ aViewInformation3D.getExtendedInformationSequence());
+ }
+ }
+
+ // calculate logic render size in world coordinates for usage in renderer
+ const basegfx::B2DHomMatrix& aInverseOToV(rViewInformation.getInverseObjectToViewTransformation());
+ const double fLogicX((aInverseOToV * basegfx::B2DVector(aDiscreteRange.getWidth() * fReduceFactor, 0.0)).getLength());
+ 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());
+
+ // 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);
+
+ if(!(nRasterWidth && nRasterHeight))
+ return;
+
+ // create view unit buffer
+ basegfx::BZPixelRaster aBZPixelRaster(
+ nOversampleValue ? nRasterWidth * nOversampleValue : nRasterWidth,
+ nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight);
+
+ // check for parallel execution possibilities
+ static bool bMultithreadAllowed = false; // loplugin:constvars:ignore
+ sal_Int32 nThreadCount(0);
+ comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
+
+ if(bMultithreadAllowed)
+ {
+ nThreadCount = rThreadPool.getWorkerCount();
+
+ if(nThreadCount > 1)
+ {
+ // at least use 10px per processor, so limit number of processors to
+ // target pixel size divided by 10 (which might be zero what is okay)
+ nThreadCount = std::min(nThreadCount, nRasterHeight / 10);
+ }
+ }
+
+ if(nThreadCount > 1)
+ {
+ class Executor : public comphelper::ThreadTask
+ {
+ private:
+ std::unique_ptr<processor3d::ZBufferProcessor3D> mpZBufferProcessor3D;
+ const primitive3d::Primitive3DContainer& mrChildren3D;
+
+ public:
+ explicit Executor(
+ std::shared_ptr<comphelper::ThreadTaskTag> const & rTag,
+ std::unique_ptr<processor3d::ZBufferProcessor3D> pZBufferProcessor3D,
+ const primitive3d::Primitive3DContainer& rChildren3D)
+ : comphelper::ThreadTask(rTag),
+ mpZBufferProcessor3D(std::move(pZBufferProcessor3D)),
+ mrChildren3D(rChildren3D)
+ {
+ }
+
+ virtual void doWork() override
+ {
+ mpZBufferProcessor3D->process(mrChildren3D);
+ mpZBufferProcessor3D->finish();
+ mpZBufferProcessor3D.reset();
+ }
+ };
+
+ const sal_uInt32 nLinesPerThread(aBZPixelRaster.getHeight() / nThreadCount);
+ std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ for(sal_Int32 a(0); a < nThreadCount; a++)
+ {
+ std::unique_ptr<processor3d::ZBufferProcessor3D> pNewZBufferProcessor3D(new processor3d::ZBufferProcessor3D(
+ aViewInformation3D,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ aUnitVisibleRange,
+ nOversampleValue,
+ fFullViewSizeX,
+ fFullViewSizeY,
+ aBZPixelRaster,
+ nLinesPerThread * a,
+ a + 1 == nThreadCount ? aBZPixelRaster.getHeight() : nLinesPerThread * (a + 1)));
+ std::unique_ptr<Executor> pExecutor(new Executor(aTag, std::move(pNewZBufferProcessor3D), getChildren3D()));
+ rThreadPool.pushTask(std::move(pExecutor));
+ }
+
+ rThreadPool.waitUntilDone(aTag);
+ }
+ else
+ {
+ // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
+ processor3d::ZBufferProcessor3D aZBufferProcessor3D(
+ aViewInformation3D,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ aUnitVisibleRange,
+ nOversampleValue,
+ fFullViewSizeX,
+ fFullViewSizeY,
+ aBZPixelRaster,
+ 0,
+ aBZPixelRaster.getHeight());
+
+ aZBufferProcessor3D.process(getChildren3D());
+ aZBufferProcessor3D.finish();
+ }
+
+ const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmapEx(aBZPixelRaster, nOversampleValue);
+ const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
+
+ if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight()))
+ return;
+
+ // create transform for the created bitmap in discrete coordinates first.
+ basegfx::B2DHomMatrix aNew2DTransform;
+
+ aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
+ aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
+ aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
+ aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
+
+ // transform back to world coordinates for usage in primitive creation
+ aNew2DTransform *= aInverseOToV;
+
+ // create bitmap primitive and add
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(maOldRenderedBitmap),
+ aNew2DTransform));
+
+ // test: Allow to add an outline in the debugger when tests are needed
+ static bool bAddOutlineToCreated3DSceneRepresentation(false); // loplugin:constvars:ignore
+
+ if(bAddOutlineToCreated3DSceneRepresentation)
+ {
+ basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
+ aOutline.transform(aNew2DTransform);
+ rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0)));
+ }
+ }
+
+ Primitive2DContainer ScenePrimitive2D::getGeometry2D() const
+ {
+ Primitive2DContainer aRetval;
+
+ // create 2D projected geometry from 3D geometry
+ if(!getChildren3D().empty())
+ {
+ // create 2D geometry extraction processor
+ processor3d::Geometry2DExtractingProcessor aGeometryProcessor(
+ getViewInformation3D(),
+ getObjectTransformation());
+
+ // process local primitives
+ aGeometryProcessor.process(getChildren3D());
+
+ // fetch result
+ aRetval = aGeometryProcessor.getPrimitive2DSequence();
+ }
+
+ return aRetval;
+ }
+
+ Primitive2DContainer ScenePrimitive2D::getShadow2D() const
+ {
+ Primitive2DContainer aRetval;
+
+ // create 2D shadows from contained 3D primitives
+ if(impGetShadow3D())
+ {
+ // add extracted 2d shadows (before 3d scene creations itself)
+ aRetval = maShadowPrimitives;
+ }
+
+ return aRetval;
+ }
+
+ bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
+ {
+ if(maOldRenderedBitmap.IsEmpty() || maOldUnitVisiblePart.isEmpty())
+ return false;
+
+ basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
+
+ if(!maOldUnitVisiblePart.isInside(aRelativePoint))
+ return false;
+
+ // calculate coordinates relative to visualized part
+ double fDivisorX(maOldUnitVisiblePart.getWidth());
+ double fDivisorY(maOldUnitVisiblePart.getHeight());
+
+ if(basegfx::fTools::equalZero(fDivisorX))
+ {
+ fDivisorX = 1.0;
+ }
+
+ if(basegfx::fTools::equalZero(fDivisorY))
+ {
+ fDivisorY = 1.0;
+ }
+
+ 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),
+ mbShadow3DChecked(false),
+ mfOldDiscreteSizeX(0.0),
+ mfOldDiscreteSizeY(0.0)
+ {
+ }
+
+ bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const ScenePrimitive2D& rCompare = static_cast<const ScenePrimitive2D&>(rPrimitive);
+
+ return (getChildren3D() == rCompare.getChildren3D()
+ && getSdrSceneAttribute() == rCompare.getSdrSceneAttribute()
+ && getSdrLightingAttribute() == rCompare.getSdrLightingAttribute()
+ && getObjectTransformation() == rCompare.getObjectTransformation()
+ && getViewInformation3D() == rCompare.getViewInformation3D());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // transform unit range to discrete coordinate range
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+ aRetval.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
+
+ // force to discrete expanded bounds (it grows, so expanding works perfectly well)
+ aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
+ aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
+
+ // transform back from discrete (view) to world coordinates
+ aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
+
+ // expand by evtl. existing shadow primitives
+ if(impGetShadow3D())
+ {
+ const basegfx::B2DRange aShadow2DRange(maShadowPrimitives.getB2DRange(rViewInformation));
+
+ if(!aShadow2DRange.isEmpty())
+ {
+ aRetval.expand(aShadow2DRange);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // 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())
+ {
+ basegfx::B2DRange aVisibleDiscreteRange;
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ bDiscreteSizesAreCalculated = true;
+
+ // needs to be painted when the new part is not part of the last
+ // decomposition
+ if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
+ {
+ bNeedNewDecomposition = true;
+ }
+
+ // display has changed and cannot be reused when resolution got bigger. It
+ // can be reused when resolution got smaller, though.
+ if(!bNeedNewDecomposition)
+ {
+ if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
+ basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
+ {
+ bNeedNewDecomposition = true;
+ }
+ }
+ }
+
+ if(bNeedNewDecomposition)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ if(!bDiscreteSizesAreCalculated)
+ {
+ basegfx::B2DRange aVisibleDiscreteRange;
+ calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
+ }
+
+ // remember last used NewDiscreteSize and NewUnitVisiblePart
+ ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
+ pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
+ pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
+ pThat->maOldUnitVisiblePart = aUnitVisibleRange;
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 ScenePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SCENEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
new file mode 100644
index 000000000..5080a92a8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx
@@ -0,0 +1,106 @@
+/* -*- 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/sdrdecompositiontools2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ const basegfx::B2DHomMatrix& rMatrix)
+ {
+ const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon());
+
+ return createHiddenGeometryPrimitives2D(
+ false/*bFilled*/,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ rMatrix);
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon)
+ {
+ return createHiddenGeometryPrimitives2D(
+ false/*bFilled*/,
+ rPolyPolygon,
+ basegfx::B2DHomMatrix());
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ bool bFilled,
+ const basegfx::B2DRange& rRange)
+ {
+ return createHiddenGeometryPrimitives2D(
+ bFilled,
+ rRange,
+ basegfx::B2DHomMatrix());
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ bool bFilled,
+ const basegfx::B2DRange& rRange,
+ const basegfx::B2DHomMatrix& rMatrix)
+ {
+ const basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(rRange));
+
+ return createHiddenGeometryPrimitives2D(
+ bFilled,
+ aOutline,
+ rMatrix);
+ }
+
+ Primitive2DReference createHiddenGeometryPrimitives2D(
+ bool bFilled,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const basegfx::B2DHomMatrix& rMatrix)
+ {
+ // create fill or line primitive
+ Primitive2DReference xReference;
+ basegfx::B2DPolyPolygon aScaledOutline(rPolyPolygon);
+ aScaledOutline.transform(rMatrix);
+
+ if(bFilled)
+ {
+ xReference = new PolyPolygonColorPrimitive2D(
+ aScaledOutline,
+ basegfx::BColor(0.0, 0.0, 0.0));
+ }
+ else
+ {
+ const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0);
+
+ xReference = new PolyPolygonHairlinePrimitive2D(
+ aScaledOutline,
+ aGrayTone);
+ }
+
+ // create HiddenGeometryPrimitive2D
+ return Primitive2DReference(
+ new HiddenGeometryPrimitive2D(Primitive2DContainer { xReference }));
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
new file mode 100644
index 000000000..758aca3d2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx
@@ -0,0 +1,94 @@
+/* -*- 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/shadowprimitive2d.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+#include <memory>
+
+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)
+ {
+ }
+
+ bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const ShadowPrimitive2D& rCompare = static_cast< const ShadowPrimitive2D& >(rPrimitive);
+
+ return (getShadowTransform() == rCompare.getShadowTransform()
+ && getShadowColor() == rCompare.getShadowColor()
+ && getShadowBlur() == rCompare.getShadowBlur());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+ aRetval.grow(getShadowBlur());
+ aRetval.transform(getShadowTransform());
+ return aRetval;
+ }
+
+ void ShadowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) 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
+ rVisitor.visit(new TransformPrimitive2D(getShadowTransform(), std::move(aSequenceB)));
+ }
+
+ // provide unique ID
+ sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SHADOWPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
new file mode 100644
index 000000000..59cf59cd7
--- /dev/null
+++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx
@@ -0,0 +1,73 @@
+/* -*- 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/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+
+namespace drawinglayer::primitive2d
+{
+SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ , mfRadius(fRadius)
+{
+}
+
+bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (GroupPrimitive2D::operator==(rPrimitive))
+ {
+ auto& rCompare = static_cast<const SoftEdgePrimitive2D&>(rPrimitive);
+ return getRadius() == rCompare.getRadius();
+ }
+
+ return false;
+}
+
+void SoftEdgePrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (getChildren().empty())
+ return;
+
+ if (!mbInMaskGeneration)
+ {
+ GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ 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());
+
+ const Primitive2DReference xRef(
+ new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), aBColorModifier));
+ rVisitor.visit(xRef);
+}
+
+sal_uInt32 SoftEdgePrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
new file mode 100644
index 000000000..7e4de87ed
--- /dev/null
+++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
@@ -0,0 +1,71 @@
+/* -*- 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/structuretagprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ StructureTagPrimitive2D::StructureTagPrimitive2D(
+ const vcl::PDFWriter::StructElement& rStructureElement,
+ bool bBackground,
+ bool bIsImage,
+ Primitive2DContainer&& aChildren,
+ sal_Int32 const nAnchorStructureElementId)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maStructureElement(rStructureElement),
+ mbBackground(bBackground),
+ mbIsImage(bIsImage)
+ , m_nAnchorStructureElementId(nAnchorStructureElementId)
+ {
+ }
+
+ bool StructureTagPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const StructureTagPrimitive2D& rCompare = static_cast<const StructureTagPrimitive2D&>(rPrimitive);
+
+ return (isBackground() == rCompare.isBackground() &&
+ isImage() == rCompare.isImage());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 StructureTagPrimitive2D::getPrimitive2DID() const
+ {
+ 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
new file mode 100644
index 000000000..6b49c4b49
--- /dev/null
+++ b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx
@@ -0,0 +1,1096 @@
+/* -*- 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/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#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 <vcl/skia/SkiaHelper.hxx>
+
+using namespace com::sun::star;
+
+
+namespace
+{
+ sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
+ {
+ // use color distance, assume to do every color step (full quality)
+ sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
+
+ if(nSteps)
+ {
+ // calc discrete length to change color all 1.5 discrete units (pixels)
+ const sal_uInt32 nDistSteps(basegfx::fround(fDelta / (fDiscreteUnit * 1.5)));
+
+ nSteps = std::min(nSteps, nDistSteps);
+ }
+
+ // roughly cut when too big or too small
+ nSteps = std::min(nSteps, sal_uInt32(255));
+ nSteps = std::max(nSteps, sal_uInt32(1));
+
+ return nSteps;
+ }
+} // end of anonymous namespace
+
+
+namespace drawinglayer::primitive2d
+{
+ void SvgGradientHelper::createSingleGradientEntryFill(Primitive2DContainer& rContainer) const
+ {
+ const SvgGradientEntryVector& rEntries = getGradientEntries();
+ const sal_uInt32 nCount(rEntries.size());
+
+ if(nCount)
+ {
+ const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1];
+ const double fOpacity(rSingleEntry.getOpacity());
+
+ if(fOpacity > 0.0)
+ {
+ Primitive2DReference xRef(
+ new PolyPolygonColorPrimitive2D(
+ getPolyPolygon(),
+ rSingleEntry.getColor()));
+
+ if(fOpacity < 1.0)
+ {
+ Primitive2DContainer aContent { xRef };
+
+ xRef = Primitive2DReference(
+ new UnifiedTransparencePrimitive2D(
+ std::move(aContent),
+ 1.0 - fOpacity));
+ }
+
+ rContainer.push_back(xRef);
+ }
+ }
+ else
+ {
+ OSL_ENSURE(false, "Single gradient entry construction without entry (!)");
+ }
+ }
+
+ void SvgGradientHelper::checkPreconditions()
+ {
+ mbPreconditionsChecked = true;
+ const SvgGradientEntryVector& rEntries = getGradientEntries();
+
+ if(rEntries.empty())
+ {
+ // no fill at all, done
+ return;
+ }
+
+ // sort maGradientEntries by offset, small to big
+ std::sort(maGradientEntries.begin(), maGradientEntries.end());
+
+ // gradient with at least two colors
+ bool bAllInvisible(true);
+ bool bInvalidEntries(false);
+
+ for(const SvgGradientEntry& rCandidate : rEntries)
+ {
+ if(basegfx::fTools::equalZero(rCandidate.getOpacity()))
+ {
+ // invisible
+ mbFullyOpaque = false;
+ }
+ else if(basegfx::fTools::equal(rCandidate.getOpacity(), 1.0))
+ {
+ // completely opaque
+ bAllInvisible = false;
+ }
+ else
+ {
+ // opacity
+ bAllInvisible = false;
+ mbFullyOpaque = false;
+ }
+
+ if(!basegfx::fTools::betweenOrEqualEither(rCandidate.getOffset(), 0.0, 1.0))
+ {
+ bInvalidEntries = true;
+ }
+ }
+
+ if(bAllInvisible)
+ {
+ // all invisible, nothing to do
+ return;
+ }
+
+ if(bInvalidEntries)
+ {
+ // invalid entries, do nothing
+ SAL_WARN("drawinglayer", "SvgGradientHelper got invalid SvgGradientEntries outside [0.0 .. 1.0]");
+ return;
+ }
+
+ const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+
+ if(aPolyRange.isEmpty())
+ {
+ // no range to fill, nothing to do
+ return;
+ }
+
+ const double fPolyWidth(aPolyRange.getWidth());
+ const double fPolyHeight(aPolyRange.getHeight());
+
+ if(basegfx::fTools::equalZero(fPolyWidth) || basegfx::fTools::equalZero(fPolyHeight))
+ {
+ // no width/height to fill, nothing to do
+ return;
+ }
+
+ mbCreatesContent = true;
+
+ if(1 == rEntries.size())
+ {
+ // fill with single existing color
+ setSingleEntry();
+ }
+ }
+
+ const SvgGradientEntry& SvgGradientHelper::FindEntryLessOrEqual(
+ sal_Int32& rInt,
+ const double fFrac) const
+ {
+ const bool bMirror(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent(bMirror ? getMirroredGradientEntries() : getGradientEntries());
+
+ for(SvgGradientEntryVector::const_reverse_iterator aIter(rCurrent.rbegin()); aIter != rCurrent.rend(); ++aIter)
+ {
+ if(basegfx::fTools::lessOrEqual(aIter->getOffset(), fFrac))
+ {
+ return *aIter;
+ }
+ }
+
+ // walk over gap to the left, be prepared for missing 0.0/1.0 entries
+ rInt--;
+ const bool bMirror2(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent2(bMirror2 ? getMirroredGradientEntries() : getGradientEntries());
+ return rCurrent2.back();
+ }
+
+ const SvgGradientEntry& SvgGradientHelper::FindEntryMore(
+ sal_Int32& rInt,
+ const double fFrac) const
+ {
+ const bool bMirror(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent(bMirror ? getMirroredGradientEntries() : getGradientEntries());
+
+ for(SvgGradientEntryVector::const_iterator aIter(rCurrent.begin()); aIter != rCurrent.end(); ++aIter)
+ {
+ if(basegfx::fTools::more(aIter->getOffset(), fFrac))
+ {
+ return *aIter;
+ }
+ }
+
+ // walk over gap to the right, be prepared for missing 0.0/1.0 entries
+ rInt++;
+ const bool bMirror2(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2);
+ const SvgGradientEntryVector& rCurrent2(bMirror2 ? getMirroredGradientEntries() : getGradientEntries());
+ return rCurrent2.front();
+ }
+
+ // tdf#124424 Adapted creation of color runs to do in a single effort. Previous
+ // version tried to do this from [0.0 .. 1.0] and to re-use transformed versions
+ // in the caller if SpreadMethod was on some repeat mode, but had problems when
+ // e.g. like in the bugdoc from the task a negative-only fStart/fEnd run was
+ // requested in which case it did nothing. Even when reusing the spread might
+ // not have been a full one from [0.0 .. 1.0].
+ // This gets complicated due to mirrored runs, but also for gradient definitions
+ // with missing entries for 0.0 and 1.0 in which case these have to be guessed
+ // to be there with same parametrisation as their nearest existing entries. These
+ // *could* have been added at checkPreconditions() but would then create unnecessary
+ // spreads on zone overlaps.
+ void SvgGradientHelper::createRun(
+ Primitive2DContainer& rTargetColor,
+ Primitive2DContainer& rTargetOpacity,
+ double fStart,
+ double fEnd) const
+ {
+ double fInt(0.0);
+ double fFrac(0.0);
+ double fEnd2(0.0);
+
+ if(SpreadMethod::Pad == getSpreadMethod())
+ {
+ if(fStart < 0.0)
+ {
+ fFrac = std::modf(fStart, &fInt);
+ const SvgGradientEntry& rFront(getGradientEntries().front());
+ const SvgGradientEntry aTemp(1.0 + fFrac, rFront.getColor(), rFront.getOpacity());
+ createAtom(rTargetColor, rTargetOpacity, aTemp, rFront, static_cast<sal_Int32>(fInt - 1), 0);
+ fStart = rFront.getOffset();
+ }
+
+ if(fEnd > 1.0)
+ {
+ // change fEnd early, but create geometry later (after range below)
+ fEnd2 = fEnd;
+ fEnd = getGradientEntries().back().getOffset();
+ }
+ }
+
+ while(fStart < fEnd)
+ {
+ fFrac = std::modf(fStart, &fInt);
+
+ if(fFrac < 0.0)
+ {
+ fInt -= 1;
+ fFrac = 1.0 + fFrac;
+ }
+
+ sal_Int32 nIntLeft(static_cast<sal_Int32>(fInt));
+ sal_Int32 nIntRight(nIntLeft);
+
+ const SvgGradientEntry& rLeft(FindEntryLessOrEqual(nIntLeft, fFrac));
+ const SvgGradientEntry& rRight(FindEntryMore(nIntRight, fFrac));
+ createAtom(rTargetColor, rTargetOpacity, rLeft, rRight, nIntLeft, nIntRight);
+
+ const double fNextfStart(static_cast<double>(nIntRight) + rRight.getOffset());
+
+ if(basegfx::fTools::more(fNextfStart, fStart))
+ {
+ fStart = fNextfStart;
+ }
+ else
+ {
+ SAL_WARN("drawinglayer", "SvgGradientHelper spread error");
+ fStart += 1.0;
+ }
+ }
+
+ if(fEnd2 > 1.0)
+ {
+ // create end run for SpreadMethod::Pad late to keep correct creation order
+ fFrac = std::modf(fEnd2, &fInt);
+ const SvgGradientEntry& rBack(getGradientEntries().back());
+ const SvgGradientEntry aTemp(fFrac, rBack.getColor(), rBack.getOpacity());
+ createAtom(rTargetColor, rTargetOpacity, rBack, aTemp, 0, static_cast<sal_Int32>(fInt));
+ }
+ }
+
+ void SvgGradientHelper::createResult(
+ Primitive2DContainer& rContainer,
+ Primitive2DContainer aTargetColor,
+ Primitive2DContainer aTargetOpacity,
+ const basegfx::B2DHomMatrix& rUnitGradientToObject,
+ bool bInvert) const
+ {
+ Primitive2DContainer aTargetColorEntries(aTargetColor.maybeInvert(bInvert));
+ Primitive2DContainer aTargetOpacityEntries(aTargetOpacity.maybeInvert(bInvert));
+
+ if(aTargetColorEntries.empty())
+ return;
+
+ Primitive2DReference xRefContent;
+
+ if(!aTargetOpacityEntries.empty())
+ {
+ const Primitive2DReference xRefOpacity = new TransparencePrimitive2D(
+ std::move(aTargetColorEntries),
+ std::move(aTargetOpacityEntries));
+
+ xRefContent = new TransformPrimitive2D(
+ rUnitGradientToObject,
+ Primitive2DContainer { xRefOpacity });
+ }
+ else
+ {
+ xRefContent = new TransformPrimitive2D(
+ rUnitGradientToObject,
+ std::move(aTargetColorEntries));
+ }
+
+ rContainer.push_back(new MaskPrimitive2D(
+ getPolyPolygon(),
+ Primitive2DContainer { xRefContent }));
+ }
+
+ SvgGradientHelper::SvgGradientHelper(
+ const basegfx::B2DHomMatrix& rGradientTransform,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ SvgGradientEntryVector&& rGradientEntries,
+ const basegfx::B2DPoint& rStart,
+ bool bUseUnitCoordinates,
+ SpreadMethod aSpreadMethod)
+ : maGradientTransform(rGradientTransform),
+ maPolyPolygon(rPolyPolygon),
+ maGradientEntries(std::move(rGradientEntries)),
+ maStart(rStart),
+ maSpreadMethod(aSpreadMethod),
+ mbPreconditionsChecked(false),
+ mbCreatesContent(false),
+ mbSingleEntry(false),
+ mbFullyOpaque(true),
+ mbUseUnitCoordinates(bUseUnitCoordinates)
+ {
+ }
+
+ SvgGradientHelper::~SvgGradientHelper()
+ {
+ }
+
+ const SvgGradientEntryVector& SvgGradientHelper::getMirroredGradientEntries() const
+ {
+ if(maMirroredGradientEntries.empty() && !getGradientEntries().empty())
+ {
+ const_cast< SvgGradientHelper* >(this)->createMirroredGradientEntries();
+ }
+
+ return maMirroredGradientEntries;
+ }
+
+ void SvgGradientHelper::createMirroredGradientEntries()
+ {
+ if(!maMirroredGradientEntries.empty() || getGradientEntries().empty())
+ return;
+
+ const sal_uInt32 nCount(getGradientEntries().size());
+ maMirroredGradientEntries.clear();
+ maMirroredGradientEntries.reserve(nCount);
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a];
+
+ maMirroredGradientEntries.emplace_back(
+ 1.0 - rCandidate.getOffset(),
+ rCandidate.getColor(),
+ rCandidate.getOpacity());
+ }
+ }
+
+ bool SvgGradientHelper::operator==(const SvgGradientHelper& rSvgGradientHelper) const
+ {
+ const SvgGradientHelper& rCompare = rSvgGradientHelper;
+
+ return (getGradientTransform() == rCompare.getGradientTransform()
+ && getPolyPolygon() == rCompare.getPolyPolygon()
+ && getGradientEntries() == rCompare.getGradientEntries()
+ && getStart() == rCompare.getStart()
+ && getUseUnitCoordinates() == rCompare.getUseUnitCoordinates()
+ && getSpreadMethod() == rCompare.getSpreadMethod());
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ void SvgLinearGradientPrimitive2D::checkPreconditions()
+ {
+ // call parent
+ SvgGradientHelper::checkPreconditions();
+
+ if(getCreatesContent())
+ {
+ // Check Vector
+ const basegfx::B2DVector aVector(getEnd() - getStart());
+
+ if(basegfx::fTools::equalZero(aVector.getX()) && basegfx::fTools::equalZero(aVector.getY()))
+ {
+ // fill with single color using last stop color
+ setSingleEntry();
+ }
+ }
+ }
+
+ void SvgLinearGradientPrimitive2D::createAtom(
+ Primitive2DContainer& rTargetColor,
+ Primitive2DContainer& rTargetOpacity,
+ const SvgGradientEntry& rFrom,
+ const SvgGradientEntry& rTo,
+ sal_Int32 nOffsetFrom,
+ sal_Int32 nOffsetTo) const
+ {
+ // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
+ if(rFrom.getOffset() == rTo.getOffset())
+ {
+ OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
+ }
+ else
+ {
+ rTargetColor.push_back(
+ new SvgLinearAtomPrimitive2D(
+ rFrom.getColor(), rFrom.getOffset() + nOffsetFrom,
+ rTo.getColor(), rTo.getOffset() + nOffsetTo));
+
+ if(!getFullyOpaque())
+ {
+ const double fTransFrom(1.0 - rFrom.getOpacity());
+ const double fTransTo(1.0 - rTo.getOpacity());
+ const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
+ const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
+
+ rTargetOpacity.push_back(
+ new SvgLinearAtomPrimitive2D(
+ aColorFrom, rFrom.getOffset() + nOffsetFrom,
+ aColorTo, rTo.getOffset() + nOffsetTo));
+ }
+ }
+ }
+
+ void SvgLinearGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(!getPreconditionsChecked())
+ {
+ const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions();
+ }
+
+ if(getSingleEntry())
+ {
+ // fill with last existing color
+ createSingleGradientEntryFill(rContainer);
+ }
+ else if(getCreatesContent())
+ {
+ // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
+ // invisible, width and height to fill are not empty
+ const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+ const double fPolyWidth(aPolyRange.getWidth());
+ const double fPolyHeight(aPolyRange.getHeight());
+
+ // create ObjectTransform based on polygon range
+ const basegfx::B2DHomMatrix aObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fPolyWidth, fPolyHeight,
+ aPolyRange.getMinX(), aPolyRange.getMinY()));
+ basegfx::B2DHomMatrix aUnitGradientToObject;
+
+ if(getUseUnitCoordinates())
+ {
+ // interpret in unit coordinate system -> object aspect ratio will scale result
+ // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given
+ // gradient vector defined by Start,End
+ const basegfx::B2DVector aVector(getEnd() - getStart());
+ const double fVectorLength(aVector.getLength());
+
+ aUnitGradientToObject.scale(fVectorLength, 1.0);
+ aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
+ aUnitGradientToObject.translate(getStart().getX(), getStart().getY());
+
+ aUnitGradientToObject *= getGradientTransform();
+
+ // create full transform from unit gradient coordinates to object coordinates
+ // including the SvgGradient transformation
+ aUnitGradientToObject *= aObjectTransform;
+ }
+ else
+ {
+ // interpret in object coordinate system -> object aspect ratio will not scale result
+ const basegfx::B2DPoint aStart(aObjectTransform * getStart());
+ const basegfx::B2DPoint aEnd(aObjectTransform * getEnd());
+ const basegfx::B2DVector aVector(aEnd - aStart);
+
+ aUnitGradientToObject.scale(aVector.getLength(), 1.0);
+ aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX()));
+ aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
+
+ aUnitGradientToObject *= getGradientTransform();
+ }
+
+ // create inverse from it
+ basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
+ aObjectToUnitGradient.invert();
+
+ // back-transform polygon to unit gradient coordinates and get
+ // UnitRage. This is the range the gradient has to cover
+ basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
+ aUnitPoly.transform(aObjectToUnitGradient);
+ const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
+
+ // prepare result vectors
+ Primitive2DContainer aTargetColor;
+ Primitive2DContainer aTargetOpacity;
+
+ if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0))
+ {
+ // add a pre-multiply to aUnitGradientToObject to allow
+ // multiplication of the polygon(xl, 0.0, xr, 1.0)
+ const basegfx::B2DHomMatrix aPreMultiply(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ 1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY()));
+ aUnitGradientToObject = aUnitGradientToObject * aPreMultiply;
+
+ // create full color run, including all SpreadMethod variants
+ createRun(
+ aTargetColor,
+ aTargetOpacity,
+ aUnitRange.getMinX(),
+ aUnitRange.getMaxX());
+ }
+
+ createResult(rContainer, std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject);
+ }
+ }
+
+ SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D(
+ const basegfx::B2DHomMatrix& rGradientTransform,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ SvgGradientEntryVector&& rGradientEntries,
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ bool bUseUnitCoordinates,
+ SpreadMethod aSpreadMethod)
+ : SvgGradientHelper(rGradientTransform, rPolyPolygon, std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod),
+ maEnd(rEnd)
+ {
+ }
+
+ SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D()
+ {
+ }
+
+ bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
+
+ if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper))
+ {
+ const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive);
+
+ return (getEnd() == rCompare.getEnd());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return ObjectRange
+ return getPolyPolygon().getB2DRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgLinearGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+namespace drawinglayer::primitive2d
+{
+ void SvgRadialGradientPrimitive2D::checkPreconditions()
+ {
+ // call parent
+ SvgGradientHelper::checkPreconditions();
+
+ if(getCreatesContent())
+ {
+ // Check Radius
+ if(basegfx::fTools::equalZero(getRadius()))
+ {
+ // fill with single color using last stop color
+ setSingleEntry();
+ }
+ }
+ }
+
+ void SvgRadialGradientPrimitive2D::createAtom(
+ Primitive2DContainer& rTargetColor,
+ Primitive2DContainer& rTargetOpacity,
+ const SvgGradientEntry& rFrom,
+ const SvgGradientEntry& rTo,
+ sal_Int32 nOffsetFrom,
+ sal_Int32 nOffsetTo) const
+ {
+ // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset())
+ if(rFrom.getOffset() == rTo.getOffset())
+ {
+ OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)");
+ }
+ else
+ {
+ const double fScaleFrom(rFrom.getOffset() + nOffsetFrom);
+ const double fScaleTo(rTo.getOffset() + nOffsetTo);
+
+ if(isFocalSet())
+ {
+ const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
+ const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
+
+ rTargetColor.push_back(
+ new SvgRadialAtomPrimitive2D(
+ rFrom.getColor(), fScaleFrom, aTranslateFrom,
+ rTo.getColor(), fScaleTo, aTranslateTo));
+ }
+ else
+ {
+ rTargetColor.push_back(
+ new SvgRadialAtomPrimitive2D(
+ rFrom.getColor(), fScaleFrom,
+ rTo.getColor(), fScaleTo));
+ }
+
+ if(!getFullyOpaque())
+ {
+ const double fTransFrom(1.0 - rFrom.getOpacity());
+ const double fTransTo(1.0 - rTo.getOpacity());
+ const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom);
+ const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo);
+
+ if(isFocalSet())
+ {
+ const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom));
+ const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo));
+
+ rTargetOpacity.push_back(
+ new SvgRadialAtomPrimitive2D(
+ aColorFrom, fScaleFrom, aTranslateFrom,
+ aColorTo, fScaleTo, aTranslateTo));
+ }
+ else
+ {
+ rTargetOpacity.push_back(
+ new SvgRadialAtomPrimitive2D(
+ aColorFrom, fScaleFrom,
+ aColorTo, fScaleTo));
+ }
+ }
+ }
+ }
+
+ void SvgRadialGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(!getPreconditionsChecked())
+ {
+ const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions();
+ }
+
+ if(getSingleEntry())
+ {
+ // fill with last existing color
+ createSingleGradientEntryFill(rContainer);
+ }
+ else if(getCreatesContent())
+ {
+ // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely
+ // invisible, width and height to fill are not empty
+ const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange());
+ const double fPolyWidth(aPolyRange.getWidth());
+ const double fPolyHeight(aPolyRange.getHeight());
+
+ // create ObjectTransform based on polygon range
+ const basegfx::B2DHomMatrix aObjectTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fPolyWidth, fPolyHeight,
+ aPolyRange.getMinX(), aPolyRange.getMinY()));
+ basegfx::B2DHomMatrix aUnitGradientToObject;
+
+ if(getUseUnitCoordinates())
+ {
+ // interpret in unit coordinate system -> object aspect ratio will scale result
+ // create unit transform from unit vector to given linear gradient vector
+ aUnitGradientToObject.scale(getRadius(), getRadius());
+ aUnitGradientToObject.translate(getStart().getX(), getStart().getY());
+
+ if(!getGradientTransform().isIdentity())
+ {
+ aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject;
+ }
+
+ // create full transform from unit gradient coordinates to object coordinates
+ // including the SvgGradient transformation
+ aUnitGradientToObject = aObjectTransform * aUnitGradientToObject;
+ }
+ else
+ {
+ // interpret in object coordinate system -> object aspect ratio will not scale result
+ // use X-Axis with radius, it was already made relative to object width when coming from
+ // SVG import
+ const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength());
+ const basegfx::B2DPoint aStart(aObjectTransform * getStart());
+
+ aUnitGradientToObject.scale(fRadius, fRadius);
+ aUnitGradientToObject.translate(aStart.getX(), aStart.getY());
+
+ aUnitGradientToObject *= getGradientTransform();
+ }
+
+ // create inverse from it
+ basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject);
+ aObjectToUnitGradient.invert();
+
+ // back-transform polygon to unit gradient coordinates and get
+ // UnitRage. This is the range the gradient has to cover
+ basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon());
+ aUnitPoly.transform(aObjectToUnitGradient);
+ const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange());
+
+ // create range which the gradient has to cover to cover the whole given geometry.
+ // For circle, go from 0.0 to max radius in all directions (the corners)
+ double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength());
+ fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength());
+ fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength());
+ fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength());
+
+ // prepare result vectors
+ Primitive2DContainer aTargetColor;
+ Primitive2DContainer aTargetOpacity;
+
+ if(0.0 < fMax)
+ {
+ // prepare maFocalVector
+ if(isFocalSet())
+ {
+ const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax;
+ }
+
+ // create full color run, including all SpreadMethod variants
+ createRun(
+ aTargetColor,
+ aTargetOpacity,
+ 0.0,
+ fMax);
+ }
+
+ createResult(rContainer, std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject, true);
+ }
+ }
+
+ SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D(
+ const basegfx::B2DHomMatrix& rGradientTransform,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ SvgGradientEntryVector&& rGradientEntries,
+ const basegfx::B2DPoint& rStart,
+ double fRadius,
+ bool bUseUnitCoordinates,
+ SpreadMethod aSpreadMethod,
+ const basegfx::B2DPoint* pFocal)
+ : SvgGradientHelper(rGradientTransform, rPolyPolygon, std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod),
+ mfRadius(fRadius),
+ maFocal(rStart),
+ maFocalVector(0.0, 0.0),
+ maFocalLength(0.0),
+ mbFocalSet(false)
+ {
+ if(pFocal && !pFocal->equal(getStart()))
+ {
+ maFocal = *pFocal;
+ maFocalVector = maFocal - getStart();
+ mbFocalSet = true;
+ }
+ }
+
+ SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D()
+ {
+ }
+
+ bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive);
+
+ if(!pSvgGradientHelper || !SvgGradientHelper::operator==(*pSvgGradientHelper))
+ return false;
+
+ const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive);
+
+ if(getRadius() == rCompare.getRadius())
+ {
+ if(isFocalSet() == rCompare.isFocalSet())
+ {
+ if(isFocalSet())
+ {
+ return getFocal() == rCompare.getFocal();
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // return ObjectRange
+ return getPolyPolygon().getB2DRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgRadialGradientPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+// SvgLinearAtomPrimitive2D class
+
+namespace drawinglayer::primitive2d
+{
+ void SvgLinearAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const double fDelta(getOffsetB() - getOffsetA());
+
+ if(basegfx::fTools::equalZero(fDelta))
+ return;
+
+ // use one discrete unit for overlap (one pixel)
+ const double fDiscreteUnit(getDiscreteUnit());
+
+ // use color distance and discrete lengths to calculate step count
+ const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit));
+
+ // HACK: Splitting a gradient into adjacent polygons with gradually changing color is silly.
+ // If antialiasing is used to draw them, the AA-ed adjacent edges won't line up perfectly
+ // because of the AA (see SkiaSalGraphicsImpl::mergePolyPolygonToPrevious()).
+ // Make the polygons a bit wider, so they the partial overlap "fixes" this.
+ const double fixup = SkiaHelper::isVCLSkiaEnabled() ? fDiscreteUnit / 2 : 0;
+
+ // tdf#117949 Use a small amount of discrete overlap at the edges. Usually this
+ // should be exactly 0.0 and 1.0, but there were cases when this gets clipped
+ // against the mask polygon which got numerically problematic.
+ // This change is unnecessary in that respect, but avoids that numerical havoc
+ // by at the same time doing no real harm AFAIK
+ // TTTT: Remove again when clipping is fixed (!)
+
+ // prepare polygon in needed width at start position (with discrete overlap)
+ const basegfx::B2DPolygon aPolygon(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(
+ getOffsetA() - fDiscreteUnit,
+ -0.0001, // TTTT -> should be 0.0, see comment above
+ getOffsetA() + (fDelta / nSteps) + fDiscreteUnit + fixup,
+ 1.0001))); // TTTT -> should be 1.0, see comment above
+
+ // prepare loop (inside to outside, [0.0 .. 1.0[)
+ double fUnitScale(0.0);
+ const double fUnitStep(1.0 / 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(
+ basegfx::B2DPolyPolygon(aNew),
+ basegfx::interpolate(getColorA(), getColorB(), fUnitScale)));
+ }
+ }
+
+ SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D(
+ const basegfx::BColor& aColorA, double fOffsetA,
+ const basegfx::BColor& aColorB, double fOffsetB)
+ : maColorA(aColorA),
+ maColorB(aColorB),
+ mfOffsetA(fOffsetA),
+ mfOffsetB(fOffsetB)
+ {
+ if(mfOffsetA > mfOffsetB)
+ {
+ OSL_ENSURE(false, "Wrong offset order (!)");
+ std::swap(mfOffsetA, mfOffsetB);
+ }
+ }
+
+ bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive);
+
+ return (getColorA() == rCompare.getColorA()
+ && getColorB() == rCompare.getColorB()
+ && getOffsetA() == rCompare.getOffsetA()
+ && getOffsetB() == rCompare.getOffsetB());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgLinearAtomPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+
+// SvgRadialAtomPrimitive2D class
+
+namespace drawinglayer::primitive2d
+{
+ void SvgRadialAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const double fDeltaScale(getScaleB() - getScaleA());
+
+ if(basegfx::fTools::equalZero(fDeltaScale))
+ return;
+
+ // use one discrete unit for overlap (one pixel)
+ const double fDiscreteUnit(getDiscreteUnit());
+
+ // use color distance and discrete lengths to calculate step count
+ const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit));
+
+ // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
+ double fUnitScale(0.0);
+ const double fUnitStep(1.0 / nSteps);
+
+ for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
+ {
+ basegfx::B2DHomMatrix aTransform;
+ const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale));
+
+ if(isTranslateSet())
+ {
+ const basegfx::B2DVector aTranslate(
+ basegfx::interpolate(
+ getTranslateB(),
+ getTranslateA(),
+ fUnitScale));
+
+ aTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ fEndScale,
+ fEndScale,
+ aTranslate.getX(),
+ aTranslate.getY());
+ }
+ else
+ {
+ aTransform = basegfx::utils::createScaleB2DHomMatrix(
+ fEndScale,
+ fEndScale);
+ }
+
+ basegfx::B2DPolygon aNew(basegfx::utils::createPolygonFromUnitCircle());
+
+ aNew.transform(aTransform);
+ rContainer.push_back(new PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aNew),
+ basegfx::interpolate(getColorB(), getColorA(), fUnitScale)));
+ }
+ }
+
+ SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
+ const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA,
+ const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB)
+ : maColorA(aColorA),
+ maColorB(aColorB),
+ mfScaleA(fScaleA),
+ mfScaleB(fScaleB)
+ {
+ // check and evtl. set translations
+ if(!rTranslateA.equal(rTranslateB))
+ {
+ mpTranslate.reset( new VectorPair(rTranslateA, rTranslateB) );
+ }
+
+ // scale A and B have to be positive
+ mfScaleA = std::max(mfScaleA, 0.0);
+ mfScaleB = std::max(mfScaleB, 0.0);
+
+ // scale B has to be bigger than scale A; swap if different
+ if(mfScaleA > mfScaleB)
+ {
+ OSL_ENSURE(false, "Wrong offset order (!)");
+ std::swap(mfScaleA, mfScaleB);
+
+ if(mpTranslate)
+ {
+ std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB);
+ }
+ }
+ }
+
+ SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D(
+ const basegfx::BColor& aColorA, double fScaleA,
+ const basegfx::BColor& aColorB, double fScaleB)
+ : maColorA(aColorA),
+ maColorB(aColorB),
+ mfScaleA(fScaleA),
+ mfScaleB(fScaleB)
+ {
+ // scale A and B have to be positive
+ mfScaleA = std::max(mfScaleA, 0.0);
+ mfScaleB = std::max(mfScaleB, 0.0);
+
+ // scale B has to be bigger than scale A; swap if different
+ if(mfScaleA > mfScaleB)
+ {
+ OSL_ENSURE(false, "Wrong offset order (!)");
+ std::swap(mfScaleA, mfScaleB);
+ }
+ }
+
+ SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D()
+ {
+ }
+
+ bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(!DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ return false;
+
+ 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())
+ {
+ return (getTranslateA() == rCompare.getTranslateA()
+ && getTranslateB() == rCompare.getTranslateB());
+ }
+ else if(!isTranslateSet() && !rCompare.isTranslateSet())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SvgRadialAtomPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textbreakuphelper.cxx b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
new file mode 100644
index 000000000..5ca10ce63
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
@@ -0,0 +1,276 @@
+/* -*- 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/textbreakuphelper.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/CharType.hpp>
+
+
+namespace drawinglayer::primitive2d
+{
+ TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource)
+ : mrSource(rSource),
+ mbNoDXArray(false)
+ {
+ maDecTrans = mrSource.getTextTransform();
+ mbNoDXArray = mrSource.getDXArray().empty();
+
+ if(mbNoDXArray)
+ {
+ // init TextLayouter when no dxarray
+ maTextLayouter.setFontAttribute(
+ mrSource.getFontAttribute(),
+ maDecTrans.getScale().getX(),
+ maDecTrans.getScale().getY(),
+ mrSource.getLocale());
+ }
+ }
+
+ TextBreakupHelper::~TextBreakupHelper()
+ {
+ }
+
+ void TextBreakupHelper::breakupPortion(Primitive2DContainer& rTempResult, sal_Int32 nIndex, sal_Int32 nLength, bool bWordLineMode)
+ {
+ if(!(nLength && (nIndex != mrSource.getTextPosition() || nLength != mrSource.getTextLength())))
+ return;
+
+ // prepare values for new portion
+ basegfx::B2DHomMatrix aNewTransform;
+ std::vector< double > aNewDXArray;
+ const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition());
+
+ if(!mbNoDXArray)
+ {
+ // prepare new DXArray for the single word
+ aNewDXArray = std::vector< double >(
+ mrSource.getDXArray().begin() + (nIndex - mrSource.getTextPosition()),
+ mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition()));
+ }
+
+ if(bNewStartIsNotOldStart)
+ {
+ // needs to be moved to a new start position
+ double fOffset(0.0);
+
+ if(mbNoDXArray)
+ {
+ // evaluate using TextLayouter
+ fOffset = maTextLayouter.getTextWidth(mrSource.getText(), mrSource.getTextPosition(), nIndex);
+ }
+ else
+ {
+ // get from DXArray
+ const sal_Int32 nIndex2(nIndex - mrSource.getTextPosition());
+ fOffset = mrSource.getDXArray()[nIndex2 - 1];
+ }
+
+ // need offset without FontScale for building the new transformation. The
+ // new transformation will be multiplied with the current text transformation
+ // so FontScale would be double
+ double fOffsetNoScale(fOffset);
+ const double fFontScaleX(maDecTrans.getScale().getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ fOffsetNoScale /= fFontScaleX;
+ }
+
+ // apply needed offset to transformation
+ aNewTransform.translate(fOffsetNoScale, 0.0);
+
+ if(!mbNoDXArray)
+ {
+ // DXArray values need to be corrected with the offset, too. Here,
+ // take the scaled offset since the DXArray is scaled
+ const sal_uInt32 nArraySize(aNewDXArray.size());
+
+ for(sal_uInt32 a(0); a < nArraySize; a++)
+ {
+ aNewDXArray[a] -= fOffset;
+ }
+ }
+ }
+
+ // add text transformation to new transformation
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ aNewTransform *= maDecTrans.getB2DHomMatrix();
+
+ // callback to allow evtl. changes
+ const bool bCreate(allowChange(rTempResult.size(), aNewTransform, nIndex, nLength));
+
+ if(!bCreate)
+ return;
+
+ // check if we have a decorated primitive as source
+ const TextDecoratedPortionPrimitive2D* pTextDecoratedPortionPrimitive2D =
+ dynamic_cast< const TextDecoratedPortionPrimitive2D* >(&mrSource);
+
+ if(pTextDecoratedPortionPrimitive2D)
+ {
+ // create a TextDecoratedPortionPrimitive2D
+ rTempResult.push_back(
+ new TextDecoratedPortionPrimitive2D(
+ aNewTransform,
+ mrSource.getText(),
+ nIndex,
+ nLength,
+ std::move(aNewDXArray),
+ mrSource.getFontAttribute(),
+ mrSource.getLocale(),
+ mrSource.getFontColor(),
+ mrSource.getTextFillColor(),
+
+ pTextDecoratedPortionPrimitive2D->getOverlineColor(),
+ pTextDecoratedPortionPrimitive2D->getTextlineColor(),
+ pTextDecoratedPortionPrimitive2D->getFontOverline(),
+ pTextDecoratedPortionPrimitive2D->getFontUnderline(),
+ pTextDecoratedPortionPrimitive2D->getUnderlineAbove(),
+ pTextDecoratedPortionPrimitive2D->getTextStrikeout(),
+
+ // reset WordLineMode when BreakupUnit::Word is executed; else copy original
+ !bWordLineMode && pTextDecoratedPortionPrimitive2D->getWordLineMode(),
+
+ pTextDecoratedPortionPrimitive2D->getTextEmphasisMark(),
+ pTextDecoratedPortionPrimitive2D->getEmphasisMarkAbove(),
+ pTextDecoratedPortionPrimitive2D->getEmphasisMarkBelow(),
+ pTextDecoratedPortionPrimitive2D->getTextRelief(),
+ pTextDecoratedPortionPrimitive2D->getShadow()));
+ }
+ else
+ {
+ // create a SimpleTextPrimitive
+ rTempResult.push_back(
+ new TextSimplePortionPrimitive2D(
+ aNewTransform,
+ mrSource.getText(),
+ nIndex,
+ nLength,
+ std::move(aNewDXArray),
+ mrSource.getFontAttribute(),
+ mrSource.getLocale(),
+ mrSource.getFontColor()));
+ }
+ }
+
+ bool TextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& /*rNewTransform*/, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/)
+ {
+ return true;
+ }
+
+ void TextBreakupHelper::breakup(BreakupUnit aBreakupUnit)
+ {
+ if(!mrSource.getTextLength())
+ return;
+
+ Primitive2DContainer aTempResult;
+ static css::uno::Reference< css::i18n::XBreakIterator > xBreakIterator;
+
+ if(!xBreakIterator.is())
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ xBreakIterator = css::i18n::BreakIterator::create(xContext);
+ }
+
+ const OUString& rTxt = mrSource.getText();
+ const sal_Int32 nTextLength(mrSource.getTextLength());
+ const css::lang::Locale& rLocale = mrSource.getLocale();
+ const sal_Int32 nTextPosition(mrSource.getTextPosition());
+ sal_Int32 nCurrent(nTextPosition);
+
+ switch(aBreakupUnit)
+ {
+ case BreakupUnit::Character:
+ {
+ sal_Int32 nDone;
+ sal_Int32 nNextCellBreak(xBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
+ sal_Int32 a(nTextPosition);
+
+ for(; a < nTextPosition + nTextLength; a++)
+ {
+ if(a == nNextCellBreak)
+ {
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, false);
+ nCurrent = a;
+ nNextCellBreak = xBreakIterator->nextCharacters(rTxt, a, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+ }
+ }
+
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, false);
+ break;
+ }
+ case BreakupUnit::Word:
+ {
+ css::i18n::Boundary nNextWordBoundary(xBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true));
+ sal_Int32 a(nTextPosition);
+
+ for(; a < nTextPosition + nTextLength; a++)
+ {
+ if(a == nNextWordBoundary.endPos)
+ {
+ if(a > nCurrent)
+ {
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
+ }
+
+ nCurrent = a;
+
+ // skip spaces (maybe enhanced with a bool later if needed)
+ {
+ const sal_Int32 nEndOfSpaces(xBreakIterator->endOfCharBlock(rTxt, a, rLocale, css::i18n::CharType::SPACE_SEPARATOR));
+
+ if(nEndOfSpaces > a)
+ {
+ nCurrent = nEndOfSpaces;
+ }
+ }
+
+ nNextWordBoundary = xBreakIterator->getWordBoundary(rTxt, a + 1, rLocale, css::i18n::WordType::ANY_WORD, true);
+ }
+ }
+
+ if(a > nCurrent)
+ {
+ breakupPortion(aTempResult, nCurrent, a - nCurrent, true);
+ }
+ break;
+ }
+ }
+
+ mxResult = aTempResult;
+ }
+
+ Primitive2DContainer TextBreakupHelper::extractResult(BreakupUnit aBreakupUnit)
+ {
+ if(mxResult.empty())
+ {
+ breakup(aBreakupUnit);
+ }
+
+ return std::move(mxResult);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
new file mode 100644
index 000000000..0db26fbeb
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -0,0 +1,388 @@
+/* -*- 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/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <primitive2d/texteffectprimitive2d.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <primitive2d/textlineprimitive2d.hxx>
+#include <primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
+ Primitive2DContainer& rTarget,
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans,
+ const OUString& rText,
+ sal_Int32 nTextPosition,
+ sal_Int32 nTextLength,
+ const std::vector< double >& rDXArray,
+ const attribute::FontAttribute& rFontAttribute) const
+ {
+ // create the SimpleTextPrimitive needed in any case
+ rTarget.push_back(Primitive2DReference(
+ new TextSimplePortionPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ rText,
+ nTextPosition,
+ nTextLength,
+ std::vector(rDXArray),
+ rFontAttribute,
+ getLocale(),
+ getFontColor())));
+
+ // see if something else needs to be done
+ const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
+ const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
+ const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());
+
+ if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
+ return;
+
+ // common preparations
+ TextLayouterDevice aTextLayouter;
+
+ // TextLayouterDevice is needed to get metrics for text decorations like
+ // underline/strikeout/emphasis marks from it. For setup, the font size is needed
+ aTextLayouter.setFontAttribute(
+ getFontAttribute(),
+ rDecTrans.getScale().getX(),
+ rDecTrans.getScale().getY(),
+ getLocale());
+
+ // get text width
+ double fTextWidth(0.0);
+
+ if(rDXArray.empty())
+ {
+ fTextWidth = aTextLayouter.getTextWidth(rText, nTextPosition, nTextLength);
+ }
+ else
+ {
+ fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
+ const double fFontScaleX(rDecTrans.getScale().getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ // need to take FontScaling out of the DXArray
+ fTextWidth /= fFontScaleX;
+ }
+ }
+
+ if(bOverlineUsed)
+ {
+ // create primitive geometry for overline
+ rTarget.push_back(Primitive2DReference(
+ new TextLinePrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ aTextLayouter.getOverlineOffset(),
+ aTextLayouter.getOverlineHeight(),
+ getFontOverline(),
+ getOverlineColor())));
+ }
+
+ if(bUnderlineUsed)
+ {
+ // create primitive geometry for underline
+ rTarget.push_back(Primitive2DReference(
+ new TextLinePrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ aTextLayouter.getUnderlineOffset(),
+ aTextLayouter.getUnderlineHeight(),
+ getFontUnderline(),
+ getTextlineColor())));
+ }
+
+ if(!bStrikeoutUsed)
+ return;
+
+ // create primitive geometry for strikeout
+ if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
+ {
+ // strikeout with character
+ const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
+
+ rTarget.push_back(Primitive2DReference(
+ new TextCharacterStrikeoutPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ getFontColor(),
+ aStrikeoutChar,
+ getFontAttribute(),
+ getLocale())));
+ }
+ else
+ {
+ // strikeout with geometry
+ rTarget.push_back(Primitive2DReference(
+ new TextGeometryStrikeoutPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ getFontColor(),
+ aTextLayouter.getUnderlineHeight(),
+ aTextLayouter.getStrikeoutOffset(),
+ getTextStrikeout())));
+ }
+
+ // TODO: Handle Font Emphasis Above/Below
+ }
+
+ void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getWordLineMode())
+ {
+ // support for single word mode; split to single word primitives
+ // using TextBreakupHelper
+ TextBreakupHelper aTextBreakupHelper(*this);
+ Primitive2DContainer aBroken(aTextBreakupHelper.extractResult(BreakupUnit::Word));
+
+ if(!aBroken.empty())
+ {
+ // was indeed split to several words, use as result
+ rContainer.append(std::move(aBroken));
+ return;
+ }
+ else
+ {
+ // no split, was already a single word. Continue to
+ // decompose local entity
+ }
+ }
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
+ Primitive2DContainer aRetval;
+
+ // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
+ // Strikeout, etc...
+ // prepare new font attributes WITHOUT outline
+ const attribute::FontAttribute aNewFontAttribute(
+ getFontAttribute().getFamilyName(),
+ getFontAttribute().getStyleName(),
+ getFontAttribute().getWeight(),
+ getFontAttribute().getSymbol(),
+ getFontAttribute().getVertical(),
+ getFontAttribute().getItalic(),
+ getFontAttribute().getMonospaced(),
+ false, // no outline anymore, handled locally
+ getFontAttribute().getRTL(),
+ getFontAttribute().getBiDiStrong());
+
+ // handle as one word
+ impCreateGeometryContent(aRetval, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
+
+ // 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)
+ {
+ Primitive2DReference aShadow;
+
+ if(bHasShadow)
+ {
+ // 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())
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault;
+ }
+ else
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
+ }
+ }
+ else
+ {
+ if(TEXT_RELIEF_ENGRAVED == getTextRelief())
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved;
+ }
+ else
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed;
+ }
+ }
+
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ aTextEffectStyle2D));
+ aRetval = Primitive2DContainer { aNewTextEffect };
+ }
+ else if(bHasOutline)
+ {
+ // create outline using an own helper primitive since this will
+ // be view-dependent
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ std::move(aRetval),
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ TextEffectStyle2D::Outline));
+ aRetval = Primitive2DContainer { aNewTextEffect };
+ }
+
+ 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.append(std::move(aRetval));
+ }
+
+ TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
+ // TextSimplePortionPrimitive2D parameters
+ 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,
+ const basegfx::BColor& rFontColor,
+ const Color& rFillColor,
+
+ // local parameters
+ const basegfx::BColor& rOverlineColor,
+ const basegfx::BColor& rTextlineColor,
+ TextLine eFontOverline,
+ TextLine eFontUnderline,
+ bool bUnderlineAbove,
+ TextStrikeout eTextStrikeout,
+ bool bWordLineMode,
+ TextEmphasisMark eTextEmphasisMark,
+ bool bEmphasisMarkAbove,
+ bool bEmphasisMarkBelow,
+ TextRelief eTextRelief,
+ bool bShadow)
+ : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, std::move(rDXArray), rFontAttribute, rLocale, rFontColor, false, 0, rFillColor),
+ maOverlineColor(rOverlineColor),
+ maTextlineColor(rTextlineColor),
+ meFontOverline(eFontOverline),
+ meFontUnderline(eFontUnderline),
+ meTextStrikeout(eTextStrikeout),
+ meTextEmphasisMark(eTextEmphasisMark),
+ meTextRelief(eTextRelief),
+ mbUnderlineAbove(bUnderlineAbove),
+ mbWordLineMode(bWordLineMode),
+ mbEmphasisMarkAbove(bEmphasisMarkAbove),
+ mbEmphasisMarkBelow(bEmphasisMarkBelow),
+ mbShadow(bShadow)
+ {
+ }
+
+ bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextDecoratedPortionPrimitive2D& rCompare = static_cast<const TextDecoratedPortionPrimitive2D&>(rPrimitive);
+
+ return (getOverlineColor() == rCompare.getOverlineColor()
+ && getTextlineColor() == rCompare.getTextlineColor()
+ && getFontOverline() == rCompare.getFontOverline()
+ && getFontUnderline() == rCompare.getFontUnderline()
+ && getTextStrikeout() == rCompare.getTextStrikeout()
+ && getTextEmphasisMark() == rCompare.getTextEmphasisMark()
+ && getTextRelief() == rCompare.getTextRelief()
+ && getUnderlineAbove() == rCompare.getUnderlineAbove()
+ && getWordLineMode() == rCompare.getWordLineMode()
+ && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
+ && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
+ && getShadow() == rCompare.getShadow());
+ }
+
+ return false;
+ }
+
+ // #i96475#
+ // Added missing implementation. Decorations may (will) stick out of the text's
+ // inking area, so add them if needed
+ basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // check if this needs to be a TextDecoratedPortionPrimitive2D or
+ // if a TextSimplePortionPrimitive2D would be sufficient
+ if (TEXT_LINE_NONE != getFontOverline()
+ || TEXT_LINE_NONE != getFontUnderline()
+ || TEXT_STRIKEOUT_NONE != getTextStrikeout()
+ || TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark()
+ || TEXT_RELIEF_NONE != getTextRelief()
+ || getShadow())
+ {
+ // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
+ // the own local decomposition for computation and thus creates all necessary
+ // geometric objects
+ return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
+ return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 TextDecoratedPortionPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
new file mode 100644
index 000000000..dae397e1a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx
@@ -0,0 +1,245 @@
+/* -*- 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 <primitive2d/texteffectprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+namespace drawinglayer::primitive2d
+{
+const double fDiscreteSize(1.1);
+
+void TextEffectPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, 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
+ const basegfx::B2DVector aDistance(rViewInformation.getInverseObjectToViewTransformation()
+ * basegfx::B2DVector(fDiscreteSize, fDiscreteSize));
+ const basegfx::B2DVector aDiagonalDistance(aDistance * (1.0 / 1.44));
+
+ switch (getTextEffectStyle2D())
+ {
+ case TextEffectStyle2D::ReliefEmbossed:
+ case TextEffectStyle2D::ReliefEngraved:
+ case TextEffectStyle2D::ReliefEmbossedDefault:
+ case TextEffectStyle2D::ReliefEngravedDefault:
+ {
+ // prepare transform of sub-group back to (0,0) and align to X-Axis
+ basegfx::B2DHomMatrix aBackTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -getRotationCenter().getX(), -getRotationCenter().getY()));
+ aBackTransform.rotate(-getDirection());
+
+ // prepare transform of sub-group back to its position and rotation
+ basegfx::B2DHomMatrix aForwardTransform(
+ basegfx::utils::createRotateB2DHomMatrix(getDirection()));
+ aForwardTransform.translate(getRotationCenter().getX(), getRotationCenter().getY());
+
+ // create transformation for one discrete unit
+ const bool bEmbossed(TextEffectStyle2D::ReliefEmbossed == getTextEffectStyle2D()
+ || TextEffectStyle2D::ReliefEmbossedDefault
+ == getTextEffectStyle2D());
+ const bool bDefaultTextColor(
+ TextEffectStyle2D::ReliefEmbossedDefault == getTextEffectStyle2D()
+ || TextEffectStyle2D::ReliefEngravedDefault == getTextEffectStyle2D());
+ basegfx::B2DHomMatrix aTransform(aBackTransform);
+
+ if (bEmbossed)
+ {
+ // to bottom-right
+ aTransform.translate(aDiagonalDistance.getX(), aDiagonalDistance.getY());
+ }
+ else
+ {
+ // to top-left
+ aTransform.translate(-aDiagonalDistance.getX(), -aDiagonalDistance.getY());
+ }
+
+ aTransform *= aForwardTransform;
+
+ if (bDefaultTextColor)
+ {
+ // emboss/engrave in black, original forced to white
+ const basegfx::BColorModifierSharedPtr aBColorModifierToGray
+ = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0));
+ const Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getTextContent()), aBColorModifierToGray));
+
+ rContainer.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(
+ Primitive2DContainer(getTextContent()), aBColorModifierToWhite));
+ }
+ else
+ {
+ // emboss/engrave in gray, keep original's color
+ const basegfx::BColorModifierSharedPtr aBColorModifierToGray
+ = std::make_shared<basegfx::BColorModifier_replace>(
+ basegfx::BColor(0.75)); // 192
+ const Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D(
+ Primitive2DContainer(getTextContent()), aBColorModifierToGray));
+
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor }));
+
+ // add original, too
+ rContainer.push_back(new GroupPrimitive2D(Primitive2DContainer(getTextContent())));
+ }
+
+ break;
+ }
+ case TextEffectStyle2D::Outline:
+ {
+ // create transform primitives in all directions
+ basegfx::B2DHomMatrix aTransform;
+
+ aTransform.set(0, 2, aDistance.getX());
+ aTransform.set(1, 2, 0.0);
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, aDiagonalDistance.getX());
+ aTransform.set(1, 2, aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, 0.0);
+ aTransform.set(1, 2, aDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, -aDiagonalDistance.getX());
+ aTransform.set(1, 2, aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, -aDistance.getX());
+ aTransform.set(1, 2, 0.0);
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, -aDiagonalDistance.getX());
+ aTransform.set(1, 2, -aDiagonalDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, 0.0);
+ aTransform.set(1, 2, -aDistance.getY());
+ rContainer.push_back(
+ new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent())));
+
+ aTransform.set(0, 2, aDiagonalDistance.getX());
+ aTransform.set(1, 2, -aDiagonalDistance.getY());
+ rContainer.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(
+ Primitive2DContainer(getTextContent()), aBColorModifierToWhite));
+
+ break;
+ }
+ }
+}
+
+TextEffectPrimitive2D::TextEffectPrimitive2D(Primitive2DContainer&& rTextContent,
+ const basegfx::B2DPoint& rRotationCenter,
+ double fDirection,
+ TextEffectStyle2D eTextEffectStyle2D)
+ : maTextContent(std::move(rTextContent))
+ , maRotationCenter(rRotationCenter)
+ , mfDirection(fDirection)
+ , meTextEffectStyle2D(eTextEffectStyle2D)
+{
+}
+
+bool TextEffectPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BasePrimitive2D::operator==(rPrimitive))
+ {
+ const TextEffectPrimitive2D& rCompare
+ = static_cast<const TextEffectPrimitive2D&>(rPrimitive);
+
+ return (getTextContent() == rCompare.getTextContent()
+ && getRotationCenter() == rCompare.getRotationCenter()
+ && getDirection() == rCompare.getDirection()
+ && getTextEffectStyle2D() == rCompare.getTextEffectStyle2D());
+ }
+
+ return false;
+}
+
+basegfx::B2DRange
+TextEffectPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+{
+ // get range of content and grow by used fDiscreteSize. That way it is not necessary to ask
+ // the whole decomposition for its ranges (which may be expensive with outline mode which
+ // then will ask 9 times at nearly the same content. This may even be refined here using the
+ // TextEffectStyle information, e.g. for TEXTEFFECTSTYLE2D_RELIEF the grow needs only to
+ // be in two directions
+ basegfx::B2DRange aRetval(getTextContent().getB2DRange(rViewInformation));
+ aRetval.grow(fDiscreteSize);
+
+ return aRetval;
+}
+
+void TextEffectPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ if (!getBuffered2DDecomposition().empty())
+ {
+ if (maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation())
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast<TextEffectPrimitive2D*>(this)->setBuffered2DDecomposition(
+ Primitive2DContainer());
+ }
+ }
+
+ if (getBuffered2DDecomposition().empty())
+ {
+ // remember ViewRange and ViewTransformation
+ const_cast<TextEffectPrimitive2D*>(this)->maLastObjectToViewTransformation
+ = rViewInformation.getObjectToViewTransformation();
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+// provide unique ID
+sal_uInt32 TextEffectPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textenumsprimitive2d.cxx b/drawinglayer/source/primitive2d/textenumsprimitive2d.cxx
new file mode 100644
index 000000000..7f13cbbbe
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textenumsprimitive2d.cxx
@@ -0,0 +1,105 @@
+/* -*- 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/textenumsprimitive2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ TextLine mapFontLineStyleToTextLine(FontLineStyle eLineStyle)
+ {
+ switch(eLineStyle)
+ {
+ case LINESTYLE_SINGLE: return TEXT_LINE_SINGLE;
+ case LINESTYLE_DOUBLE: return TEXT_LINE_DOUBLE;
+ case LINESTYLE_DOTTED: return TEXT_LINE_DOTTED;
+ case LINESTYLE_DASH: return TEXT_LINE_DASH;
+ case LINESTYLE_LONGDASH: return TEXT_LINE_LONGDASH;
+ case LINESTYLE_DASHDOT: return TEXT_LINE_DASHDOT;
+ case LINESTYLE_DASHDOTDOT: return TEXT_LINE_DASHDOTDOT;
+ case LINESTYLE_SMALLWAVE: return TEXT_LINE_SMALLWAVE;
+ case LINESTYLE_WAVE: return TEXT_LINE_WAVE;
+ case LINESTYLE_DOUBLEWAVE: return TEXT_LINE_DOUBLEWAVE;
+ case LINESTYLE_BOLD: return TEXT_LINE_BOLD;
+ case LINESTYLE_BOLDDOTTED: return TEXT_LINE_BOLDDOTTED;
+ case LINESTYLE_BOLDDASH: return TEXT_LINE_BOLDDASH;
+ case LINESTYLE_BOLDLONGDASH: return TEXT_LINE_BOLDLONGDASH;
+ case LINESTYLE_BOLDDASHDOT: return TEXT_LINE_BOLDDASHDOT;
+ case LINESTYLE_BOLDDASHDOTDOT: return TEXT_LINE_BOLDDASHDOTDOT;
+ case LINESTYLE_BOLDWAVE: return TEXT_LINE_BOLDWAVE;
+ // FontLineStyle_FORCE_EQUAL_SIZE, LINESTYLE_DONTKNOW, LINESTYLE_NONE
+ default: return TEXT_LINE_NONE;
+ }
+ }
+
+ FontLineStyle mapTextLineToFontLineStyle(TextLine eLineStyle)
+ {
+ switch(eLineStyle)
+ {
+ default: /*TEXT_LINE_NONE*/ return LINESTYLE_NONE;
+ case TEXT_LINE_SINGLE: return LINESTYLE_SINGLE;
+ case TEXT_LINE_DOUBLE: return LINESTYLE_DOUBLE;
+ case TEXT_LINE_DOTTED: return LINESTYLE_DOTTED;
+ case TEXT_LINE_DASH: return LINESTYLE_DASH;
+ case TEXT_LINE_LONGDASH: return LINESTYLE_LONGDASH;
+ case TEXT_LINE_DASHDOT: return LINESTYLE_DASHDOT;
+ case TEXT_LINE_DASHDOTDOT: return LINESTYLE_DASHDOTDOT;
+ case TEXT_LINE_SMALLWAVE: return LINESTYLE_SMALLWAVE;
+ case TEXT_LINE_WAVE: return LINESTYLE_WAVE;
+ case TEXT_LINE_DOUBLEWAVE: return LINESTYLE_DOUBLEWAVE;
+ case TEXT_LINE_BOLD: return LINESTYLE_BOLD;
+ case TEXT_LINE_BOLDDOTTED: return LINESTYLE_BOLDDOTTED;
+ case TEXT_LINE_BOLDDASH: return LINESTYLE_BOLDDASH;
+ case TEXT_LINE_BOLDLONGDASH: return LINESTYLE_LONGDASH;
+ case TEXT_LINE_BOLDDASHDOT: return LINESTYLE_BOLDDASHDOT;
+ case TEXT_LINE_BOLDDASHDOTDOT:return LINESTYLE_BOLDDASHDOTDOT;
+ case TEXT_LINE_BOLDWAVE: return LINESTYLE_BOLDWAVE;
+ }
+ }
+
+ TextStrikeout mapFontStrikeoutToTextStrikeout(FontStrikeout eFontStrikeout)
+ {
+ switch(eFontStrikeout)
+ {
+ case STRIKEOUT_SINGLE: return TEXT_STRIKEOUT_SINGLE;
+ case STRIKEOUT_DOUBLE: return TEXT_STRIKEOUT_DOUBLE;
+ case STRIKEOUT_BOLD: return TEXT_STRIKEOUT_BOLD;
+ case STRIKEOUT_SLASH: return TEXT_STRIKEOUT_SLASH;
+ case STRIKEOUT_X: return TEXT_STRIKEOUT_X;
+ // FontStrikeout_FORCE_EQUAL_SIZE, STRIKEOUT_NONE, STRIKEOUT_DONTKNOW
+ default: return TEXT_STRIKEOUT_NONE;
+ }
+ }
+
+ FontStrikeout mapTextStrikeoutToFontStrikeout(TextStrikeout eTextStrikeout)
+ {
+ switch(eTextStrikeout)
+ {
+ default: /*case primitive2d::TEXT_STRIKEOUT_NONE*/ return STRIKEOUT_NONE;
+ case TEXT_STRIKEOUT_SINGLE: return STRIKEOUT_SINGLE;
+ case TEXT_STRIKEOUT_DOUBLE: return STRIKEOUT_DOUBLE;
+ case TEXT_STRIKEOUT_BOLD: return STRIKEOUT_BOLD;
+ case TEXT_STRIKEOUT_SLASH: return STRIKEOUT_SLASH;
+ case TEXT_STRIKEOUT_X: return STRIKEOUT_X;
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
new file mode 100644
index 000000000..c7af9562a
--- /dev/null
+++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
@@ -0,0 +1,152 @@
+/* -*- 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/texthierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ TextHierarchyLinePrimitive2D::TextHierarchyLinePrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyLinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D;
+ }
+
+
+ TextHierarchyParagraphPrimitive2D::TextHierarchyParagraphPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ sal_Int16 nOutlineLevel)
+ : GroupPrimitive2D(std::move(aChildren)),
+ mnOutlineLevel(nOutlineLevel)
+ {
+ }
+
+ bool TextHierarchyParagraphPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TextHierarchyParagraphPrimitive2D& rCompare = static_cast<const TextHierarchyParagraphPrimitive2D&>(rPrimitive);
+
+ return (getOutlineLevel() == rCompare.getOutlineLevel());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyParagraphPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D;
+ }
+
+
+
+ TextHierarchyBulletPrimitive2D::TextHierarchyBulletPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyBulletPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D;
+ }
+
+
+ TextHierarchyBlockPrimitive2D::TextHierarchyBlockPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyBlockPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D;
+ }
+
+
+ TextHierarchyFieldPrimitive2D::TextHierarchyFieldPrimitive2D(
+ Primitive2DContainer&& aChildren,
+ const FieldType& rFieldType,
+ const std::vector< std::pair< OUString, OUString>>* pNameValue)
+ : GroupPrimitive2D(std::move(aChildren)),
+ meType(rFieldType)
+ {
+ if (nullptr != pNameValue)
+ {
+ meNameValue = *pNameValue;
+ }
+ }
+
+ OUString TextHierarchyFieldPrimitive2D::getValue(const OUString& rName) const
+ {
+ for (const std::pair< OUString, OUString >& candidate : meNameValue)
+ {
+ if (candidate.first.equals(rName))
+ {
+ return candidate.second;
+ }
+ }
+
+ return OUString();
+ }
+
+ bool TextHierarchyFieldPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TextHierarchyFieldPrimitive2D& rCompare = static_cast<const TextHierarchyFieldPrimitive2D&>(rPrimitive);
+
+ return (getType() == rCompare.getType()
+ && meNameValue == rCompare.meNameValue);
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyFieldPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D;
+ }
+
+
+ TextHierarchyEditPrimitive2D::TextHierarchyEditPrimitive2D(Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyEditPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
new file mode 100644
index 000000000..f70f9f63b
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
@@ -0,0 +1,456 @@
+/* -*- 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 <algorithm>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/unique_disposing_ptr.hxx>
+#include <osl/diagnose.h>
+#include <tools/gen.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/font.hxx>
+#include <vcl/metric.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/svapp.hxx>
+
+namespace drawinglayer::primitive2d
+{
+namespace
+{
+class ImpTimedRefDev;
+
+// VDev RevDevice provider
+
+//the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor
+//or disposing of the default XComponentContext which causes the underlying
+//OutputDevice to get released
+
+//The ImpTimerRefDev itself, if the timeout ever gets hit, will call
+//reset on the scoped_timed_RefDev to release the ImpTimerRefDev early
+//if it's unused for a few minutes
+class scoped_timed_RefDev : public comphelper::unique_disposing_ptr<ImpTimedRefDev>
+{
+public:
+ scoped_timed_RefDev()
+ : comphelper::unique_disposing_ptr<ImpTimedRefDev>(
+ (css::uno::Reference<css::lang::XComponent>(
+ ::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW)))
+ {
+ }
+};
+
+class the_scoped_timed_RefDev : public rtl::Static<scoped_timed_RefDev, the_scoped_timed_RefDev>
+{
+};
+
+class ImpTimedRefDev : public Timer
+{
+ scoped_timed_RefDev& mrOwnerOfMe;
+ VclPtr<VirtualDevice> mpVirDev;
+ sal_uInt32 mnUseCount;
+
+public:
+ explicit ImpTimedRefDev(scoped_timed_RefDev& rOwnerofMe);
+ virtual ~ImpTimedRefDev() override;
+ virtual void Invoke() override;
+
+ VirtualDevice& acquireVirtualDevice();
+ void releaseVirtualDevice();
+};
+
+ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev& rOwnerOfMe)
+ : Timer("drawinglayer ImpTimedRefDev destroy mpVirDev")
+ , mrOwnerOfMe(rOwnerOfMe)
+ , mpVirDev(nullptr)
+ , mnUseCount(0)
+{
+ SetTimeout(3L * 60L * 1000L); // three minutes
+ Start();
+}
+
+ImpTimedRefDev::~ImpTimedRefDev()
+{
+ OSL_ENSURE(0 == mnUseCount, "destruction of a still used ImpTimedRefDev (!)");
+ const SolarMutexGuard aSolarGuard;
+ mpVirDev.disposeAndClear();
+}
+
+void ImpTimedRefDev::Invoke()
+{
+ // for obvious reasons, do not call anything after this
+ mrOwnerOfMe.reset();
+}
+
+VirtualDevice& ImpTimedRefDev::acquireVirtualDevice()
+{
+ if (!mpVirDev)
+ {
+ mpVirDev = VclPtr<VirtualDevice>::Create();
+ mpVirDev->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1);
+ }
+
+ if (!mnUseCount)
+ {
+ Stop();
+ }
+
+ mnUseCount++;
+
+ return *mpVirDev;
+}
+
+void ImpTimedRefDev::releaseVirtualDevice()
+{
+ OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)");
+ mnUseCount--;
+
+ if (!mnUseCount)
+ {
+ Start();
+ }
+}
+
+VirtualDevice& acquireGlobalVirtualDevice()
+{
+ scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get();
+
+ if (!rStdRefDevice)
+ rStdRefDevice.reset(new ImpTimedRefDev(rStdRefDevice));
+
+ return rStdRefDevice->acquireVirtualDevice();
+}
+
+void releaseGlobalVirtualDevice()
+{
+ scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get();
+
+ OSL_ENSURE(rStdRefDevice,
+ "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)");
+ rStdRefDevice->releaseVirtualDevice();
+}
+
+} // end of anonymous namespace
+
+TextLayouterDevice::TextLayouterDevice()
+ : mrDevice(acquireGlobalVirtualDevice())
+{
+}
+
+TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE { releaseGlobalVirtualDevice(); }
+
+void TextLayouterDevice::setFont(const vcl::Font& rFont) { mrDevice.SetFont(rFont); }
+
+void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontAttribute,
+ double fFontScaleX, double fFontScaleY,
+ const css::lang::Locale& rLocale)
+{
+ setFont(getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale));
+}
+
+double TextLayouterDevice::getOverlineOffset() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = (rMetric.GetInternalLeading() / 2.0) - rMetric.GetAscent();
+ return fRet;
+}
+
+double TextLayouterDevice::getUnderlineOffset() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = rMetric.GetDescent() / 2.0;
+ return fRet;
+}
+
+double TextLayouterDevice::getStrikeoutOffset() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = (rMetric.GetAscent() - rMetric.GetInternalLeading()) / 3.0;
+ return fRet;
+}
+
+double TextLayouterDevice::getOverlineHeight() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = rMetric.GetInternalLeading() / 2.5;
+ return fRet;
+}
+
+double TextLayouterDevice::getUnderlineHeight() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ double fRet = rMetric.GetDescent() / 4.0;
+ return fRet;
+}
+
+double TextLayouterDevice::getTextHeight() const { return mrDevice.GetTextHeight(); }
+
+double TextLayouterDevice::getTextWidth(const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength) const
+{
+ return mrDevice.GetTextWidth(rText, nIndex, nLength);
+}
+
+void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
+ const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength,
+ const std::vector<double>& rDXArray) const
+{
+ const sal_uInt32 nDXArrayCount(rDXArray.size());
+ sal_uInt32 nTextLength(nLength);
+ const sal_uInt32 nStringLength(rText.getLength());
+
+ if (nTextLength + nIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nIndex;
+ }
+
+ if (nDXArrayCount)
+ {
+ OSL_ENSURE(nDXArrayCount == nTextLength,
+ "DXArray size does not correspond to text portion size (!)");
+ std::vector<sal_Int32> aIntegerDXArray(nDXArrayCount);
+
+ for (sal_uInt32 a(0); a < nDXArrayCount; a++)
+ {
+ aIntegerDXArray[a] = basegfx::fround(rDXArray[a]);
+ }
+
+ mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength, 0,
+ aIntegerDXArray);
+ }
+ else
+ {
+ mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength);
+ }
+}
+
+basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sal_uInt32 nIndex,
+ sal_uInt32 nLength) const
+{
+ sal_uInt32 nTextLength(nLength);
+ const sal_uInt32 nStringLength(rText.getLength());
+
+ if (nTextLength + nIndex > nStringLength)
+ {
+ nTextLength = nStringLength - nIndex;
+ }
+
+ if (nTextLength)
+ {
+ ::tools::Rectangle aRect;
+
+ mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength);
+
+ // #i104432#, #i102556# take empty results into account
+ if (!aRect.IsEmpty())
+ {
+ return vcl::unotools::b2DRectangleFromRectangle(aRect);
+ }
+ }
+
+ return basegfx::B2DRange();
+}
+
+double TextLayouterDevice::getFontAscent() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ return rMetric.GetAscent();
+}
+
+double TextLayouterDevice::getFontDescent() const
+{
+ const ::FontMetric& rMetric = mrDevice.GetFontMetric();
+ return rMetric.GetDescent();
+}
+
+void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle,
+ const OUString& rText, DrawTextFlags nStyle,
+ GDIMetaFile& rGDIMetaFile) const
+{
+ mrDevice.AddTextRectActions(rRectangle, rText, nStyle, rGDIMetaFile);
+}
+
+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, nTextLength);
+ aRetval.assign(aArray.begin(), aArray.end());
+ }
+
+ return aRetval;
+}
+
+std::vector<double> TextLayouterDevice::getCaretPositions(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(2 * nTextLength);
+ std::vector<sal_Int32> aArray(2 * nTextLength);
+ mrDevice.GetCaretPositions(rText, aArray.data(), nIndex, nTextLength);
+ aRetval.assign(aArray.begin(), aArray.end());
+ }
+
+ return aRetval;
+}
+
+// helper methods for vcl font handling
+
+vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute& rFontAttribute,
+ double fFontScaleX, double fFontScaleY, double fFontRotation,
+ const css::lang::Locale& rLocale)
+{
+ // detect FontScaling
+ const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY)));
+ const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX)));
+ const bool bFontIsScaled(nHeight != nWidth);
+
+#ifdef _WIN32
+ // for WIN32 systems, start with creating an unscaled font. If FontScaling
+ // is wanted, that width needs to be adapted using FontMetric again to get a
+ // width of the unscaled font
+ vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(),
+ Size(0, nHeight));
+#else
+ // for non-WIN32 systems things are easier since these accept a Font creation
+ // with initially nWidth != nHeight for FontScaling. Despite that, use zero for
+ // FontWidth when no scaling is used to explicitly have that zero when e.g. the
+ // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a
+ // set FontWidth; import that in a WIN32 system, and trouble is there)
+ vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(),
+ Size(bFontIsScaled ? std::max<sal_uInt32>(nWidth, 1) : 0, nHeight));
+#endif
+ // define various other FontAttribute
+ aRetval.SetAlignment(ALIGN_BASELINE);
+ aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL
+ : RTL_TEXTENCODING_UNICODE);
+ aRetval.SetVertical(rFontAttribute.getVertical());
+ aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight()));
+ aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE);
+ aRetval.SetOutline(rFontAttribute.getOutline());
+ aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE);
+ aRetval.SetLanguage(LanguageTag::convertToLanguageType(rLocale, false));
+
+#ifdef _WIN32
+ // for WIN32 systems, correct the FontWidth if FontScaling is used
+ if (bFontIsScaled && nHeight > 0)
+ {
+ const FontMetric aUnscaledFontMetric(
+ Application::GetDefaultDevice()->GetFontMetric(aRetval));
+
+ if (aUnscaledFontMetric.GetAverageFontWidth() > 0)
+ {
+ const double fScaleFactor(static_cast<double>(nWidth) / static_cast<double>(nHeight));
+ const sal_uInt32 nScaledWidth(basegfx::fround(
+ static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()) * fScaleFactor));
+ aRetval.SetAverageFontWidth(nScaledWidth);
+ }
+ }
+#endif
+ // handle FontRotation (if defined)
+ if (!basegfx::fTools::equalZero(fFontRotation))
+ {
+ int aRotate10th(-basegfx::rad2deg<10>(fFontRotation));
+ aRetval.SetOrientation(Degree10(aRotate10th % 3600));
+ }
+
+ return aRetval;
+}
+
+attribute::FontAttribute getFontAttributeFromVclFont(basegfx::B2DVector& o_rSize,
+ const vcl::Font& rFont, bool bRTL,
+ bool bBiDiStrong)
+{
+ const attribute::FontAttribute aRetval(
+ rFont.GetFamilyName(), rFont.GetStyleName(), static_cast<sal_uInt16>(rFont.GetWeight()),
+ RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(), rFont.IsVertical(),
+ ITALIC_NONE != rFont.GetItalic(), PITCH_FIXED == rFont.GetPitch(), rFont.IsOutline(), bRTL,
+ bBiDiStrong);
+ // TODO: eKerning
+
+ // set FontHeight and init to no FontScaling
+ o_rSize.setY(std::max<tools::Long>(rFont.GetFontSize().getHeight(), 0));
+ o_rSize.setX(o_rSize.getY());
+
+#ifdef _WIN32
+ // for WIN32 systems, the FontScaling at the Font is detected by
+ // checking that FontWidth != 0. When FontScaling is used, WIN32
+ // needs to do extra stuff to detect the correct width (since it's
+ // zero and not equal the font height) and its relationship to
+ // the height
+ if (rFont.GetFontSize().getWidth() > 0)
+ {
+ vcl::Font aUnscaledFont(rFont);
+ aUnscaledFont.SetAverageFontWidth(0);
+ const FontMetric aUnscaledFontMetric(
+ Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont));
+
+ if (aUnscaledFontMetric.GetAverageFontWidth() > 0)
+ {
+ const double fScaleFactor(
+ static_cast<double>(rFont.GetFontSize().getWidth())
+ / static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()));
+ o_rSize.setX(fScaleFactor * o_rSize.getY());
+ }
+ }
+#else
+ // For non-WIN32 systems the detection is the same, but the value
+ // is easier achieved since width == height is interpreted as no
+ // scaling. Ergo, Width == 0 means width == height, and width != 0
+ // means the scaling is in the direct relation of width to height
+ if (rFont.GetFontSize().getWidth() > 0)
+ {
+ o_rSize.setX(static_cast<double>(rFont.GetFontSize().getWidth()));
+ }
+#endif
+ return aRetval;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textlineprimitive2d.cxx b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
new file mode 100644
index 000000000..461a83d75
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx
@@ -0,0 +1,290 @@
+/* -*- 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 <primitive2d/textlineprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void TextLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(TEXT_LINE_NONE == getTextLine())
+ return;
+
+ bool bDoubleLine(false);
+ bool bWaveLine(false);
+ bool bBoldLine(false);
+ const int* pDotDashArray(nullptr);
+ basegfx::B2DLineJoin eLineJoin(basegfx::B2DLineJoin::NONE);
+ double fOffset(getOffset());
+ double fHeight(getHeight());
+
+ static const int aDottedArray[] = { 1, 1, 0}; // DOTTED LINE
+ static const int aDotDashArray[] = { 1, 1, 4, 1, 0}; // DASHDOT
+ static const int aDashDotDotArray[] = { 1, 1, 1, 1, 4, 1, 0}; // DASHDOTDOT
+ static const int aDashedArray[] = { 5, 2, 0}; // DASHED LINE
+ static const int aLongDashArray[] = { 7, 2, 0}; // LONGDASH
+
+ // get decomposition
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getObjectTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ switch(getTextLine())
+ {
+ default: // case TEXT_LINE_SINGLE:
+ {
+ break;
+ }
+ case TEXT_LINE_DOUBLE:
+ {
+ bDoubleLine = true;
+ break;
+ }
+ case TEXT_LINE_DOTTED:
+ {
+ pDotDashArray = aDottedArray;
+ break;
+ }
+ case TEXT_LINE_DASH:
+ {
+ pDotDashArray = aDashedArray;
+ break;
+ }
+ case TEXT_LINE_LONGDASH:
+ {
+ pDotDashArray = aLongDashArray;
+ break;
+ }
+ case TEXT_LINE_DASHDOT:
+ {
+ pDotDashArray = aDotDashArray;
+ break;
+ }
+ case TEXT_LINE_DASHDOTDOT:
+ {
+ pDotDashArray = aDashDotDotArray;
+ break;
+ }
+ case TEXT_LINE_SMALLWAVE:
+ {
+ bWaveLine = true;
+ break;
+ }
+ case TEXT_LINE_WAVE:
+ {
+ bWaveLine = true;
+ break;
+ }
+ case TEXT_LINE_DOUBLEWAVE:
+ {
+ bDoubleLine = true;
+ bWaveLine = true;
+ break;
+ }
+ case TEXT_LINE_BOLD:
+ {
+ bBoldLine = true;
+ break;
+ }
+ case TEXT_LINE_BOLDDOTTED:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDottedArray;
+ break;
+ }
+ case TEXT_LINE_BOLDDASH:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDashedArray;
+ break;
+ }
+ case TEXT_LINE_BOLDLONGDASH:
+ {
+ bBoldLine = true;
+ pDotDashArray = aLongDashArray;
+ break;
+ }
+ case TEXT_LINE_BOLDDASHDOT:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDotDashArray;
+ break;
+ }
+ case TEXT_LINE_BOLDDASHDOTDOT:
+ {
+ bBoldLine = true;
+ pDotDashArray = aDashDotDotArray;
+ break;
+ }
+ case TEXT_LINE_BOLDWAVE:
+ {
+ bWaveLine = true;
+ bBoldLine = true;
+ break;
+ }
+ }
+
+ if(bBoldLine)
+ {
+ fHeight *= 2.0;
+ }
+
+ if(bDoubleLine)
+ {
+ fOffset -= 0.50 * fHeight;
+ fHeight *= 0.64;
+ }
+
+ if(bWaveLine)
+ {
+ eLineJoin = basegfx::B2DLineJoin::Round;
+ fHeight *= 0.25;
+ }
+
+ // prepare Line and Stroke Attributes
+ const attribute::LineAttribute aLineAttribute(getLineColor(), fHeight, eLineJoin);
+ attribute::StrokeAttribute aStrokeAttribute;
+
+ if(pDotDashArray)
+ {
+ std::vector< double > aDoubleArray;
+
+ for(const int* p = pDotDashArray; *p; ++p)
+ {
+ aDoubleArray.push_back(static_cast<double>(*p) * fHeight);
+ }
+
+ aStrokeAttribute = attribute::StrokeAttribute(std::move(aDoubleArray));
+ }
+
+ // create base polygon and new primitive
+ basegfx::B2DPolygon aLine;
+ Primitive2DReference aNewPrimitive;
+
+ aLine.append(basegfx::B2DPoint(0.0, fOffset));
+ aLine.append(basegfx::B2DPoint(getWidth(), fOffset));
+
+ const basegfx::B2DHomMatrix aUnscaledTransform(
+ basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate));
+
+ aLine.transform(aUnscaledTransform);
+
+ if(bWaveLine)
+ {
+ double fWaveWidth(10.6 * fHeight);
+
+ if(TEXT_LINE_SMALLWAVE == getTextLine())
+ {
+ fWaveWidth *= 0.7;
+ }
+ else if(TEXT_LINE_WAVE == getTextLine())
+ {
+ // extra multiply to get the same WaveWidth as with the bold version
+ fWaveWidth *= 2.0;
+ }
+
+ aNewPrimitive = Primitive2DReference(new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5));
+ }
+ else
+ {
+ aNewPrimitive = Primitive2DReference(new PolygonStrokePrimitive2D(aLine, aLineAttribute, aStrokeAttribute));
+ }
+
+ // add primitive
+ rContainer.push_back(aNewPrimitive);
+
+ if(!bDoubleLine)
+ return;
+
+ // double line, create 2nd primitive with offset using TransformPrimitive based on
+ // already created NewPrimitive
+ double fLineDist(2.3 * fHeight);
+
+ if(bWaveLine)
+ {
+ fLineDist = 6.3 * fHeight;
+ }
+
+ // move base point of text to 0.0 and de-rotate
+ basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTranslate.getX(), -aTranslate.getY()));
+ aTransform.rotate(-fRotate);
+
+ // translate in Y by offset
+ aTransform.translate(0.0, fLineDist);
+
+ // move back and rotate
+ aTransform.rotate(fRotate);
+ aTransform.translate(aTranslate.getX(), aTranslate.getY());
+
+ // add transform primitive
+ Primitive2DContainer aContent { aNewPrimitive };
+ rContainer.push_back( new TransformPrimitive2D(aTransform, std::move(aContent)) );
+ }
+
+ TextLinePrimitive2D::TextLinePrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ double fOffset,
+ double fHeight,
+ TextLine eTextLine,
+ const basegfx::BColor& rLineColor)
+ : maObjectTransformation(rObjectTransformation),
+ mfWidth(fWidth),
+ mfOffset(fOffset),
+ mfHeight(fHeight),
+ meTextLine(eTextLine),
+ maLineColor(rLineColor)
+ {
+ }
+
+ bool TextLinePrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextLinePrimitive2D& rCompare = static_cast<const TextLinePrimitive2D&>(rPrimitive);
+
+ return (getObjectTransformation() == rCompare.getObjectTransformation()
+ && getWidth() == rCompare.getWidth()
+ && getOffset() == rCompare.getOffset()
+ && getHeight() == rCompare.getHeight()
+ && getTextLine() == rCompare.getTextLine()
+ && getLineColor() == rCompare.getLineColor());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextLinePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx b/drawinglayer/source/primitive2d/textprimitive2d.cxx
new file mode 100644
index 000000000..00135a3aa
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -0,0 +1,309 @@
+/* -*- 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/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <primitive2d/texteffectprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+namespace drawinglayer::primitive2d
+{
+namespace
+{
+// adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted
+// scale from a text transformation. A copy is modified so that it contains only positive
+// scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter.
+// rScale is adapted accordingly to contain the corrected scale which would need to be
+// applied to e.g. outlines received from TextLayouter under usage of fontScale. This
+// includes Y-Scale, X-Scale-correction and mirrorings.
+basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale)
+{
+ // copy input value
+ basegfx::B2DVector aFontScale(rScale);
+
+ // correct FontHeight settings
+ if (basegfx::fTools::equalZero(aFontScale.getY()))
+ {
+ // no font height; choose one and adapt scale to get back to original scaling
+ static const double fDefaultFontScale(100.0);
+ rScale.setY(1.0 / fDefaultFontScale);
+ aFontScale.setY(fDefaultFontScale);
+ }
+ else if (basegfx::fTools::less(aFontScale.getY(), 0.0))
+ {
+ // negative font height; invert and adapt scale to get back to original scaling
+ aFontScale.setY(-aFontScale.getY());
+ rScale.setY(-1.0);
+ }
+ else
+ {
+ // positive font height; adapt scale; scaling will be part of the polygons
+ rScale.setY(1.0);
+ }
+
+ // correct FontWidth settings
+ if (basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY()))
+ {
+ // no FontScale, adapt scale
+ rScale.setX(1.0);
+ }
+ else
+ {
+ // If FontScale is used, force to no FontScale to get a non-scaled VCL font.
+ // Adapt scaling in X accordingly.
+ rScale.setX(aFontScale.getX() / aFontScale.getY());
+ aFontScale.setX(aFontScale.getY());
+ }
+
+ return aFontScale;
+}
+} // end of anonymous namespace
+
+void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
+ basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const
+{
+ if (!getTextLength())
+ return;
+
+ // decompose object transformation to single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ // if decomposition returns false, create no geometry since e.g. scaling may
+ // be zero
+ if (!(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX)
+ && aScale.getX() != 0.0))
+ return;
+
+ // 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))
+ {
+ aScale = basegfx::absolute(aScale);
+ fRotate += M_PI;
+ }
+
+ // for the TextLayouterDevice, it is necessary to have a scaling representing
+ // the font size. Since we want to extract polygons here, it is okay to
+ // work just with scaling and to ignore shear, rotation and translation,
+ // all that can be applied to the polygons later
+ const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
+
+ // prepare textlayoutdevice
+ TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(),
+ getLocale());
+
+ // When getting outlines from stretched text (aScale.getX() != 1.0) it
+ // is necessary to inverse-scale the DXArray (if used) to not get the
+ // outlines already aligned to given, but wrong DXArray
+ if (!getDXArray().empty() && !basegfx::fTools::equal(aScale.getX(), 1.0))
+ {
+ std::vector<double> aScaledDXArray = getDXArray();
+ const double fDXArrayScale(1.0 / aScale.getX());
+
+ for (double& a : aScaledDXArray)
+ {
+ a *= fDXArrayScale;
+ }
+
+ // get the text outlines
+ aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(),
+ aScaledDXArray);
+ }
+ else
+ {
+ // get the text outlines
+ aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(),
+ getDXArray());
+ }
+
+ // create primitives for the outlines
+ const sal_uInt32 nCount(rTarget.size());
+
+ if (nCount)
+ {
+ // prepare object transformation for polygons
+ rTransformation = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate);
+ }
+}
+
+void TextSimplePortionPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (!getTextLength())
+ return;
+
+ Primitive2DContainer aRetval;
+ basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
+ basegfx::B2DHomMatrix aPolygonTransform;
+
+ // get text outlines and their object transformation
+ getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
+
+ // create primitives for the outlines
+ const sal_uInt32 nCount(aB2DPolyPolyVector.size());
+
+ if (!nCount)
+ return;
+
+ // alloc space for the primitives
+ aRetval.resize(nCount);
+
+ // color-filled polypolygons
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ // prepare polypolygon
+ basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a];
+ rPolyPolygon.transform(aPolygonTransform);
+ aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor());
+ }
+
+ if (getFontAttribute().getOutline())
+ {
+ // decompose polygon transformation to single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ 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 };
+ }
+
+ rContainer.append(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,
+ const basegfx::BColor& rFontColor, bool bFilled, tools::Long nWidthToFill,
+ const Color& rTextFillColor)
+ : maTextTransform(rNewTransform)
+ , maText(rText)
+ , mnTextPosition(nTextPosition)
+ , mnTextLength(nTextLength)
+ , maDXArray(std::move(rDXArray))
+ , maFontAttribute(rFontAttribute)
+ , maLocale(rLocale)
+ , maFontColor(rFontColor)
+ , mbFilled(bFilled)
+ , mnWidthToFill(nWidthToFill)
+ , maTextFillColor(rTextFillColor)
+{
+#if OSL_DEBUG_LEVEL > 0
+ const sal_Int32 aStringLength(getText().getLength());
+ OSL_ENSURE(aStringLength >= getTextPosition()
+ && aStringLength >= getTextPosition() + getTextLength(),
+ "TextSimplePortionPrimitive2D with text out of range (!)");
+#endif
+}
+
+bool LocalesAreEqual(const css::lang::Locale& rA, const css::lang::Locale& rB)
+{
+ return (rA.Language == rB.Language && rA.Country == rB.Country && rA.Variant == rB.Variant);
+}
+
+bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextSimplePortionPrimitive2D& rCompare
+ = static_cast<const TextSimplePortionPrimitive2D&>(rPrimitive);
+
+ return (getTextTransform() == rCompare.getTextTransform() && getText() == rCompare.getText()
+ && getTextPosition() == rCompare.getTextPosition()
+ && getTextLength() == rCompare.getTextLength()
+ && getDXArray() == rCompare.getDXArray()
+ && getFontAttribute() == rCompare.getFontAttribute()
+ && LocalesAreEqual(getLocale(), rCompare.getLocale())
+ && getFontColor() == rCompare.getFontColor() && mbFilled == rCompare.mbFilled
+ && mnWidthToFill == rCompare.mnWidthToFill
+ && maTextFillColor == rCompare.maTextFillColor);
+ }
+
+ return false;
+}
+
+basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if (maB2DRange.isEmpty() && getTextLength())
+ {
+ // get TextBoundRect as base size
+ // decompose object transformation to single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ if (getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX))
+ {
+ // for the TextLayouterDevice, it is necessary to have a scaling representing
+ // the font size. Since we want to extract polygons here, it is okay to
+ // work just with scaling and to ignore shear, rotation and translation,
+ // all that can be applied to the polygons later
+ const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale));
+
+ // prepare textlayoutdevice
+ TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(),
+ getLocale());
+
+ // get basic text range
+ basegfx::B2DRange aNewRange(
+ aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength()));
+
+ // #i104432#, #i102556# take empty results into account
+ if (!aNewRange.isEmpty())
+ {
+ // prepare object transformation for range
+ const basegfx::B2DHomMatrix aRangeTransformation(
+ basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate));
+
+ // apply range transformation to it
+ aNewRange.transform(aRangeTransformation);
+
+ // assign to buffered value
+ const_cast<TextSimplePortionPrimitive2D*>(this)->maB2DRange = aNewRange;
+ }
+ }
+ }
+
+ return maB2DRange;
+}
+
+// provide unique ID
+sal_uInt32 TextSimplePortionPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
new file mode 100644
index 000000000..ecd66b7a8
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
@@ -0,0 +1,259 @@
+/* -*- 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 <primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ BaseTextStrikeoutPrimitive2D::BaseTextStrikeoutPrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ const basegfx::BColor& rFontColor)
+ : maObjectTransformation(rObjectTransformation),
+ mfWidth(fWidth),
+ maFontColor(rFontColor)
+ {
+ }
+
+ bool BaseTextStrikeoutPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const BaseTextStrikeoutPrimitive2D& rCompare = static_cast<const BaseTextStrikeoutPrimitive2D&>(rPrimitive);
+
+ return (getObjectTransformation() == rCompare.getObjectTransformation()
+ && getWidth() == rCompare.getWidth()
+ && getFontColor() == rCompare.getFontColor());
+ }
+
+ return false;
+ }
+
+
+ void TextCharacterStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // strikeout with character
+ const OUString aSingleCharString(getStrikeoutChar());
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ // get decomposition
+ getObjectTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // prepare TextLayouter
+ TextLayouterDevice aTextLayouter;
+
+ aTextLayouter.setFontAttribute(
+ getFontAttribute(),
+ aScale.getX(),
+ aScale.getY(),
+ getLocale());
+
+ const double fStrikeCharWidth(aTextLayouter.getTextWidth(aSingleCharString, 0, 1));
+ const double fStrikeCharCount(fabs(getWidth()/fStrikeCharWidth));
+ const sal_uInt32 nStrikeCharCount(static_cast< sal_uInt32 >(fStrikeCharCount + 0.5));
+ std::vector<double> aDXArray(nStrikeCharCount);
+ OUStringBuffer aStrikeoutString;
+
+ for(sal_uInt32 a(0); a < nStrikeCharCount; a++)
+ {
+ aStrikeoutString.append(aSingleCharString);
+ aDXArray[a] = (a + 1) * fStrikeCharWidth;
+ }
+
+ auto len = aStrikeoutString.getLength();
+ rContainer.push_back(
+ new TextSimplePortionPrimitive2D(
+ getObjectTransformation(),
+ aStrikeoutString.makeStringAndClear(),
+ 0,
+ len,
+ std::move(aDXArray),
+ getFontAttribute(),
+ getLocale(),
+ getFontColor()));
+ }
+
+ TextCharacterStrikeoutPrimitive2D::TextCharacterStrikeoutPrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ const basegfx::BColor& rFontColor,
+ sal_Unicode aStrikeoutChar,
+ const attribute::FontAttribute& rFontAttribute,
+ const css::lang::Locale& rLocale)
+ : BaseTextStrikeoutPrimitive2D(rObjectTransformation, fWidth, rFontColor),
+ maStrikeoutChar(aStrikeoutChar),
+ maFontAttribute(rFontAttribute),
+ maLocale(rLocale)
+ {
+ }
+
+ bool TextCharacterStrikeoutPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BaseTextStrikeoutPrimitive2D::operator==(rPrimitive))
+ {
+ const TextCharacterStrikeoutPrimitive2D& rCompare = static_cast<const TextCharacterStrikeoutPrimitive2D&>(rPrimitive);
+
+ return (getStrikeoutChar() == rCompare.getStrikeoutChar()
+ && getFontAttribute() == rCompare.getFontAttribute()
+ && LocalesAreEqual(getLocale(), rCompare.getLocale()));
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextCharacterStrikeoutPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D;
+ }
+
+ void TextGeometryStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ OSL_ENSURE(TEXT_STRIKEOUT_SLASH != getTextStrikeout() && TEXT_STRIKEOUT_X != getTextStrikeout(),
+ "Wrong TEXT_STRIKEOUT type; a TextCharacterStrikeoutPrimitive2D should be used (!)");
+
+ // strikeout with geometry
+ double fStrikeoutHeight(getHeight());
+ double fStrikeoutOffset(getOffset());
+ bool bDoubleLine(false);
+
+ // get decomposition
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getObjectTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // set line attribute
+ switch(getTextStrikeout())
+ {
+ default : // case primitive2d::TEXT_STRIKEOUT_SINGLE:
+ {
+ break;
+ }
+ case primitive2d::TEXT_STRIKEOUT_DOUBLE:
+ {
+ bDoubleLine = true;
+ break;
+ }
+ case primitive2d::TEXT_STRIKEOUT_BOLD:
+ {
+ fStrikeoutHeight *= 2.0;
+ break;
+ }
+ }
+
+ if(bDoubleLine)
+ {
+ fStrikeoutOffset -= 0.50 * fStrikeoutHeight;
+ fStrikeoutHeight *= 0.64;
+ }
+
+ // create base polygon and new primitive
+ basegfx::B2DPolygon aStrikeoutLine;
+
+ aStrikeoutLine.append(basegfx::B2DPoint(0.0, -fStrikeoutOffset));
+ aStrikeoutLine.append(basegfx::B2DPoint(getWidth(), -fStrikeoutOffset));
+
+ const basegfx::B2DHomMatrix aUnscaledTransform(
+ basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate));
+
+ aStrikeoutLine.transform(aUnscaledTransform);
+
+ // add primitive
+ const attribute::LineAttribute aLineAttribute(getFontColor(), fStrikeoutHeight, basegfx::B2DLineJoin::NONE);
+ Primitive2DContainer xRetval(1);
+ xRetval[0] = new PolygonStrokePrimitive2D(aStrikeoutLine, aLineAttribute);
+
+ if(bDoubleLine)
+ {
+ // double line, create 2nd primitive with offset using TransformPrimitive based on
+ // already created NewPrimitive
+ const double fLineDist(2.0 * fStrikeoutHeight);
+
+ // move base point of text to 0.0 and de-rotate
+ basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTranslate.getX(), -aTranslate.getY()));
+ aTransform.rotate(-fRotate);
+
+ // translate in Y by offset
+ aTransform.translate(0.0, -fLineDist);
+
+ // move back and rotate
+ aTransform.rotate(fRotate);
+ aTransform.translate(aTranslate.getX(), aTranslate.getY());
+
+ // add transform primitive
+ xRetval.push_back(
+ new TransformPrimitive2D(
+ aTransform,
+ Primitive2DContainer(xRetval)));
+ }
+
+ rContainer.append(std::move(xRetval));
+ }
+
+ TextGeometryStrikeoutPrimitive2D::TextGeometryStrikeoutPrimitive2D(
+ const basegfx::B2DHomMatrix& rObjectTransformation,
+ double fWidth,
+ const basegfx::BColor& rFontColor,
+ double fHeight,
+ double fOffset,
+ TextStrikeout eTextStrikeout)
+ : BaseTextStrikeoutPrimitive2D(rObjectTransformation, fWidth, rFontColor),
+ mfHeight(fHeight),
+ mfOffset(fOffset),
+ meTextStrikeout(eTextStrikeout)
+ {
+ }
+
+ bool TextGeometryStrikeoutPrimitive2D::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(BaseTextStrikeoutPrimitive2D::operator==(rPrimitive))
+ {
+ const TextGeometryStrikeoutPrimitive2D& rCompare = static_cast<const TextGeometryStrikeoutPrimitive2D&>(rPrimitive);
+
+ return (getHeight() == rCompare.getHeight()
+ && getOffset() == rCompare.getOffset()
+ && getTextStrikeout() == rCompare.getTextStrikeout());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TextGeometryStrikeoutPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/transformprimitive2d.cxx b/drawinglayer/source/primitive2d/transformprimitive2d.cxx
new file mode 100644
index 000000000..ddd1594e2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/transformprimitive2d.cxx
@@ -0,0 +1,64 @@
+/* -*- 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/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ TransformPrimitive2D::TransformPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransformation,
+ Primitive2DContainer&& aChildren)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maTransformation(rTransformation)
+ {
+ }
+
+ bool TransformPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TransformPrimitive2D& rCompare = static_cast< const TransformPrimitive2D& >(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange TransformPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation));
+ aRetval.transform(getTransformation());
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 TransformPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx
new file mode 100644
index 000000000..8a86b1b29
--- /dev/null
+++ b/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx
@@ -0,0 +1,57 @@
+/* -*- 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/transparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ TransparencePrimitive2D::TransparencePrimitive2D(
+ Primitive2DContainer&& aChildren,
+ Primitive2DContainer&& aTransparence)
+ : GroupPrimitive2D(std::move(aChildren)),
+ maTransparence(std::move(aTransparence))
+ {
+ }
+
+ bool TransparencePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const TransparencePrimitive2D& rCompare = static_cast<const TransparencePrimitive2D&>(rPrimitive);
+
+ return (getTransparence() == rCompare.getTransparence());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 TransparencePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
new file mode 100644
index 000000000..a2ba512be
--- /dev/null
+++ b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx
@@ -0,0 +1,112 @@
+/* -*- 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/unifiedtransparenceprimitive2d.hxx>
+#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>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ UnifiedTransparencePrimitive2D::UnifiedTransparencePrimitive2D(
+ Primitive2DContainer&& aChildren,
+ double fTransparence)
+ : GroupPrimitive2D(std::move(aChildren)),
+ mfTransparence(fTransparence)
+ {
+ }
+
+ bool UnifiedTransparencePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(GroupPrimitive2D::operator==(rPrimitive))
+ {
+ const UnifiedTransparencePrimitive2D& rCompare = static_cast<const UnifiedTransparencePrimitive2D&>(rPrimitive);
+
+ return (getTransparence() == rCompare.getTransparence());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange UnifiedTransparencePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // do not use the fallback to decomposition here since for a correct BoundRect we also
+ // need invisible (1.0 == getTransparence()) geometry; these would be deleted in the decomposition
+ return getChildren().getB2DRange( rViewInformation);
+ }
+
+ void UnifiedTransparencePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(0.0 == getTransparence())
+ {
+ // no transparence used, so just use the content
+ getChildren(rVisitor);
+ }
+ else if(getTransparence() > 0.0 && getTransparence() < 1.0)
+ {
+ // The idea is to create a TransparencePrimitive2D with transparent content using a fill color
+ // corresponding to the transparence value. Problem is that in most systems, the right
+ // and bottom pixel array is not filled when filling polygons, thus this would not
+ // always produce a complete transparent bitmap. There are some solutions:
+
+ // - Grow the used polygon range by one discrete unit in X and Y. This
+ // will make the decomposition view-dependent.
+
+ // - For all filled polygon renderings, draw the polygon outline extra. This
+ // would lead to unwanted side effects when using concatenated polygons.
+
+ // - At this decomposition, add a filled polygon and a hairline polygon. This
+ // solution stays view-independent.
+
+ // 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));
+ 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));
+
+ // create sub-transparence group with a gray-colored rectangular fill polygon
+ rVisitor.visit(new TransparencePrimitive2D(Primitive2DContainer(getChildren()), std::move(aTransparenceContent)));
+ }
+ else
+ {
+ // completely transparent or invalid definition, add nothing
+ }
+ }
+
+ // provide unique ID
+ sal_uInt32 UnifiedTransparencePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
new file mode 100644
index 000000000..6e7e134f2
--- /dev/null
+++ b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx
@@ -0,0 +1,257 @@
+/* -*- 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 <primitive2d/wallpaperprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/graph.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void WallpaperBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ Primitive2DReference aRetval;
+
+ if(!getLocalObjectRange().isEmpty() && !getBitmapEx().IsEmpty())
+ {
+ // get bitmap PIXEL size
+ const Size& rPixelSize = getBitmapEx().GetSizePixel();
+
+ if(rPixelSize.Width() > 0 && rPixelSize.Height() > 0)
+ {
+ 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());
+
+ Primitive2DReference xReference(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
+ aObjectTransform));
+
+ aRetval = xReference;
+ }
+ 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())
+ {
+ 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;
+ }
+ }
+ }
+ }
+
+ if (aRetval.is())
+ rContainer.push_back(aRetval);
+ }
+
+ WallpaperBitmapPrimitive2D::WallpaperBitmapPrimitive2D(
+ const basegfx::B2DRange& rObjectRange,
+ const BitmapEx& rBitmapEx,
+ WallpaperStyle eWallpaperStyle)
+ : maObjectRange(rObjectRange),
+ maBitmapEx(rBitmapEx),
+ meWallpaperStyle(eWallpaperStyle)
+ {
+ }
+
+ bool WallpaperBitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(ViewTransformationDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const WallpaperBitmapPrimitive2D& rCompare = static_cast<const WallpaperBitmapPrimitive2D&>(rPrimitive);
+
+ return (getLocalObjectRange() == rCompare.getLocalObjectRange()
+ && getBitmapEx() == rCompare.getBitmapEx()
+ && getWallpaperStyle() == rCompare.getWallpaperStyle());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange WallpaperBitmapPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ return getLocalObjectRange();
+ }
+
+ // provide unique ID
+ sal_uInt32 WallpaperBitmapPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
new file mode 100644
index 000000000..5249cdfb3
--- /dev/null
+++ b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx
@@ -0,0 +1,105 @@
+/* -*- 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/wrongspellprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void WrongSpellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, 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
+ // it similar to the usage in e.g. HelplinePrimitive2D. Remembering the ViewTransformation
+ // should be enough then.
+ // The view-independent wavelines work well (if You ask me). Maybe the old VCL-Behaviour is only
+ // in place because it was not possible/too expensive at that time to scale the wavelines with the
+ // view...
+ // With the VCL-PixelRenderer this will not even be used since it implements WrongSpellPrimitive2D
+ // directly and mimics the old VCL-Display there. If You implemented a new renderer without
+ // direct WrongSpellPrimitive2D support, You may want to do the described change here.
+
+ // get the font height (part of scale), so decompose the matrix
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // calculate distances based on a static default (to allow testing in debugger)
+ static const double fDefaultDistance(0.03);
+ const double fFontHeight(aScale.getY());
+ const double fUnderlineDistance(fFontHeight * fDefaultDistance);
+ const double fWaveWidth(2.0 * fUnderlineDistance);
+
+ // the Y-distance needs to be relative to FontHeight since the points get
+ // transformed with the transformation containing that scale already.
+ const double fRelativeUnderlineDistance(basegfx::fTools::equalZero(aScale.getY()) ? 0.0 : fUnderlineDistance / aScale.getY());
+ basegfx::B2DPoint aStart(getStart(), fRelativeUnderlineDistance);
+ basegfx::B2DPoint aStop(getStop(), fRelativeUnderlineDistance);
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(getTransformation() * aStart);
+ aPolygon.append(getTransformation() * aStop);
+
+ // prepare line attribute
+ const attribute::LineAttribute aLineAttribute(getColor());
+
+ // create the waveline primitive
+ rContainer.push_back(new PolygonWavePrimitive2D(aPolygon, aLineAttribute, fWaveWidth, 0.5 * fWaveWidth));
+ }
+
+ WrongSpellPrimitive2D::WrongSpellPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransformation,
+ double fStart,
+ double fStop,
+ const basegfx::BColor& rColor)
+ : maTransformation(rTransformation),
+ mfStart(fStart),
+ mfStop(fStop),
+ maColor(rColor)
+ {
+ }
+
+ bool WrongSpellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const WrongSpellPrimitive2D& rCompare = static_cast<const WrongSpellPrimitive2D&>(rPrimitive);
+
+ return (getTransformation() == rCompare.getTransformation()
+ && getStart() == rCompare.getStart()
+ && getStop() == rCompare.getStop()
+ && getColor() == rCompare.getColor());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 WrongSpellPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */