[Libreoffice-commits] core.git: basegfx/source include/basegfx

Armin Le Grand (via logerrit) logerrit at kemper.freedesktop.org
Fri Feb 14 18:20:23 UTC 2020


 basegfx/source/polygon/b2dpolygontools.cxx  |   36 +++
 basegfx/source/polygon/b3dpolygontools.cxx  |  255 +++++++++++++++++++---------
 include/basegfx/polygon/b2dpolygontools.hxx |    2 
 include/basegfx/polygon/b3dpolygontools.hxx |   12 +
 4 files changed, 216 insertions(+), 89 deletions(-)

New commits:
commit 62ac8333999c661432adb0a18245a399daa89dcb
Author:     Armin Le Grand <Armin.Le.Grand at me.com>
AuthorDate: Fri Feb 14 16:47:14 2020 +0100
Commit:     Armin Le Grand <Armin.Le.Grand at me.com>
CommitDate: Fri Feb 14 19:19:49 2020 +0100

    tdf#130655 callback interface for 3D and secure dash
    
    Added same interface for 3D but just for lines, it uses no
    gaps. Added the security mechanism mentioned in the task
    in comment (2) to 2D and 3D
    
    Change-Id: I5da303c01562088682d95ee4f294c99e1f17bf6b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/88728
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <Armin.Le.Grand at me.com>

diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx
index 68d1120bc2cb..c194a38dc9d2 100644
--- a/basegfx/source/polygon/b2dpolygontools.cxx
+++ b/basegfx/source/polygon/b2dpolygontools.cxx
@@ -16,6 +16,8 @@
  *   except in compliance with the License. You may obtain a copy of
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
+#include <numeric>
+#include <algorithm>
 
 #include <basegfx/numeric/ftools.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
@@ -32,8 +34,6 @@
 #include <basegfx/curve/b2dbeziertools.hxx>
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
 
-#include <numeric>
-
 // #i37443#
 #define ANGLE_BOUND_START_VALUE     (2.25)
 #define ANGLE_BOUND_MINIMUM_VALUE   (0.1)
@@ -1231,6 +1231,32 @@ namespace basegfx::utils
                 return;
             }
 
+            // precalculate maximal acceptable length of candidate polygon assuming
+            // we want to create a maximum of fNumberOfAllowedSnippets. For
+            // fNumberOfAllowedSnippets use ca. 65536, double due to line & gap.
+            static double fNumberOfAllowedSnippets(65535.0 * 2.0);
+            const double fAllowedLength((fNumberOfAllowedSnippets * fDotDashLength) / double(rDotDashArray.size()));
+            const double fCandidateLength(basegfx::utils::getLength(rCandidate));
+            std::vector<double> aDotDashArray(rDotDashArray);
+
+            if(fCandidateLength > fAllowedLength)
+            {
+                // we would produce more than fNumberOfAllowedSnippets, so
+                // adapt aDotDashArray to exactly produce assumed number. Also
+                // assert this to let the caller know about it.
+                // If this asserts: Please think about checking your DotDashArray
+                // before calling this function or evtl. use the callback version
+                // to *not* produce that much of data. Even then, you may still
+                // think about producing too much runtime (!)
+                assert(true && "applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)");
+
+                // calculate correcting factor, apply to aDotDashArray and fDotDashLength
+                // to enlarge these as needed
+                const double fFactor(fCandidateLength / fAllowedLength);
+                std::for_each(aDotDashArray.begin(), aDotDashArray.end(), [&fFactor](double &f){ f *= fFactor; });
+                fDotDashLength *= fFactor;
+            }
+
             // prepare current edge's start
             B2DCubicBezier aCurrentEdge;
             const bool bIsClosed(rCandidate.isClosed());
@@ -1240,7 +1266,7 @@ namespace basegfx::utils
             // prepare DotDashArray iteration and the line/gap switching bool
             sal_uInt32 nDotDashIndex(0);
             bool bIsLine(true);
-            double fDotDashMovingLength(rDotDashArray[0]);
+            double fDotDashMovingLength(aDotDashArray[0]);
             B2DPolygon aSnippet;
 
             // remember 1st and last snippets to try to merge after execution
@@ -1303,7 +1329,7 @@ namespace basegfx::utils
 
                             // prepare next DotDashArray step and flip line/gap flag
                             fLastDotDashMovingLength = fDotDashMovingLength;
-                            fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+                            fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount];
                             bIsLine = !bIsLine;
                         }
 
@@ -1367,7 +1393,7 @@ namespace basegfx::utils
 
                             // prepare next DotDashArray step and flip line/gap flag
                             fLastDotDashMovingLength = fDotDashMovingLength;
-                            fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
+                            fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount];
                             bIsLine = !bIsLine;
                         }
 
diff --git a/basegfx/source/polygon/b3dpolygontools.cxx b/basegfx/source/polygon/b3dpolygontools.cxx
index 1d97d4d43f0e..a0349a156626 100644
--- a/basegfx/source/polygon/b3dpolygontools.cxx
+++ b/basegfx/source/polygon/b3dpolygontools.cxx
@@ -90,7 +90,90 @@ namespace basegfx::utils
             return fRetval;
         }
 
-        void applyLineDashing(const B3DPolygon& rCandidate, const std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, double fDotDashLength)
+        void applyLineDashing(
+            const B3DPolygon& rCandidate,
+            const std::vector<double>& rDotDashArray,
+            B3DPolyPolygon* pLineTarget,
+            double fDotDashLength)
+        {
+            // clear targets in any case
+            if(pLineTarget)
+            {
+                pLineTarget->clear();
+            }
+
+            // provide callback as lambda
+            auto aLineCallback(
+                nullptr == pLineTarget
+                ? std::function<void(const basegfx::B3DPolygon&)>()
+                : [&pLineTarget](const basegfx::B3DPolygon& rSnippet){ pLineTarget->append(rSnippet); });
+
+            // call version that uses callbacks
+            applyLineDashing(
+                rCandidate,
+                rDotDashArray,
+                aLineCallback,
+                fDotDashLength);
+        }
+
+        static void implHandleSnippet(
+            const B3DPolygon& rSnippet,
+            std::function<void(const basegfx::B3DPolygon& rSnippet)>& rTargetCallback,
+            B3DPolygon& rFirst,
+            B3DPolygon& rLast)
+        {
+            if(rSnippet.isClosed())
+            {
+                if(!rFirst.count())
+                {
+                    rFirst = rSnippet;
+                }
+                else
+                {
+                    if(rLast.count())
+                    {
+                        rTargetCallback(rLast);
+                    }
+
+                    rLast = rSnippet;
+                }
+            }
+            else
+            {
+                rTargetCallback(rSnippet);
+            }
+        }
+
+        static void implHandleFirstLast(
+            std::function<void(const basegfx::B3DPolygon& rSnippet)>& rTargetCallback,
+            B3DPolygon& rFirst,
+            B3DPolygon& rLast)
+        {
+            if(rFirst.count() && rLast.count()
+                && rFirst.getB3DPoint(0).equal(rLast.getB3DPoint(rLast.count() - 1)))
+            {
+                // start of first and end of last are the same -> merge them
+                rLast.append(rFirst);
+                rLast.removeDoublePoints();
+                rFirst.clear();
+            }
+
+            if(rLast.count())
+            {
+                rTargetCallback(rLast);
+            }
+
+            if(rFirst.count())
+            {
+                rTargetCallback(rFirst);
+            }
+        }
+
+        void applyLineDashing(
+            const B3DPolygon& rCandidate,
+            const std::vector<double>& rDotDashArray,
+            std::function<void(const basegfx::B3DPolygon& rSnippet)> aLineTargetCallback,
+            double fDotDashLength)
         {
             const sal_uInt32 nPointCount(rCandidate.count());
             const sal_uInt32 nDotDashCount(rDotDashArray.size());
@@ -100,62 +183,74 @@ namespace basegfx::utils
                 fDotDashLength = std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
             }
 
-            if(fTools::more(fDotDashLength, 0.0) && pLineTarget && nPointCount)
+            if(fTools::lessOrEqual(fDotDashLength, 0.0) || !aLineTargetCallback || !nPointCount)
             {
-                // clear targets
-                if(pLineTarget)
+                // parameters make no sense, just add source to targets
+                if(aLineTargetCallback)
                 {
-                    pLineTarget->clear();
+                    aLineTargetCallback(rCandidate);
                 }
 
-                // prepare current edge's start
-                B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
-                const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
-
-                // prepare DotDashArray iteration and the line/gap switching bool
-                sal_uInt32 nDotDashIndex(0);
-                bool bIsLine(true);
-                double fDotDashMovingLength(rDotDashArray[0]);
-                B3DPolygon aSnippet;
-
-                // iterate over all edges
-                for(sal_uInt32 a(0); a < nEdgeCount; a++)
-                {
-                    // update current edge
-                    const sal_uInt32 nNextIndex((a + 1) % nPointCount);
-                    const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
-                    const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength());
+                return;
+            }
 
-                    if(!fTools::equalZero(fEdgeLength))
-                    {
-                        double fLastDotDashMovingLength(0.0);
-                        while(fTools::less(fDotDashMovingLength, fEdgeLength))
-                        {
-                            // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
-                            const bool bHandleLine(bIsLine && pLineTarget);
+            // precalculate maximal acceptable length of candidate polygon assuming
+            // we want to create a maximum of fNumberOfAllowedSnippets. In 3D
+            // use less for fNumberOfAllowedSnippets, ca. 6553.6, double due to line & gap.
+            // Less in 3D due to potentially blowing up to rounded line segments.
+            static double fNumberOfAllowedSnippets(6553.5 * 2.0);
+            const double fAllowedLength((fNumberOfAllowedSnippets * fDotDashLength) / double(rDotDashArray.size()));
+            const double fCandidateLength(basegfx::utils::getLength(rCandidate));
+            std::vector<double> aDotDashArray(rDotDashArray);
 
-                            if(bHandleLine)
-                            {
-                                if(!aSnippet.count())
-                                {
-                                    aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
-                                }
+            if(fCandidateLength > fAllowedLength)
+            {
+                // we would produce more than fNumberOfAllowedSnippets, so
+                // adapt aDotDashArray to exactly produce assumed number. Also
+                // assert this to let the caller know about it.
+                // If this asserts: Please think about checking your DotDashArray
+                // before calling this function or evtl. use the callback version
+                // to *not* produce that much of data. Even then, you may still
+                // think about producing too much runtime (!)
+                assert(true && "applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)");
+
+                // calculate correcting factor, apply to aDotDashArray and fDotDashLength
+                // to enlarge these as needed
+                const double fFactor(fCandidateLength / fAllowedLength);
+                std::for_each(aDotDashArray.begin(), aDotDashArray.end(), [&fFactor](double &f){ f *= fFactor; });
+                fDotDashLength *= fFactor;
+            }
 
-                                aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
+            // prepare current edge's start
+            B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
+            const bool bIsClosed(rCandidate.isClosed());
+            const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
 
-                                pLineTarget->append(aSnippet);
+            // prepare DotDashArray iteration and the line/gap switching bool
+            sal_uInt32 nDotDashIndex(0);
+            bool bIsLine(true);
+            double fDotDashMovingLength(aDotDashArray[0]);
+            B3DPolygon aSnippet;
 
-                                aSnippet.clear();
-                            }
+            // remember 1st and last snippets to try to merge after execution
+            // is complete and hand to callback
+            B3DPolygon aFirstLine, aLastLine;
 
-                            // prepare next DotDashArray step and flip line/gap flag
-                            fLastDotDashMovingLength = fDotDashMovingLength;
-                            fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
-                            bIsLine = !bIsLine;
-                        }
+            // iterate over all edges
+            for(sal_uInt32 a(0); a < nEdgeCount; a++)
+            {
+                // update current edge
+                const sal_uInt32 nNextIndex((a + 1) % nPointCount);
+                const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
+                const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength());
 
-                        // append snippet [fLastDotDashMovingLength, fEdgeLength]
-                        const bool bHandleLine(bIsLine && pLineTarget);
+                if(!fTools::equalZero(fEdgeLength))
+                {
+                    double fLastDotDashMovingLength(0.0);
+                    while(fTools::less(fDotDashMovingLength, fEdgeLength))
+                    {
+                        // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
+                        const bool bHandleLine(bIsLine && aLineTargetCallback);
 
                         if(bHandleLine)
                         {
@@ -164,57 +259,55 @@ namespace basegfx::utils
                                 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
                             }
 
-                            aSnippet.append(aNextPoint);
-                        }
+                            aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
 
-                        // prepare move to next edge
-                        fDotDashMovingLength -= fEdgeLength;
-                    }
+                            implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine);
 
-                    // prepare next edge step (end point gets new start point)
-                    aCurrentPoint = aNextPoint;
-                }
+                            aSnippet.clear();
+                        }
 
-                // append last intermediate results (if exists)
-                if(aSnippet.count())
-                {
-                    if(bIsLine && pLineTarget)
-                    {
-                        pLineTarget->append(aSnippet);
+                        // prepare next DotDashArray step and flip line/gap flag
+                        fLastDotDashMovingLength = fDotDashMovingLength;
+                        fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount];
+                        bIsLine = !bIsLine;
                     }
-                }
 
-                // check if start and end polygon may be merged
-                if(pLineTarget)
-                {
-                    const sal_uInt32 nCount(pLineTarget->count());
+                    // append snippet [fLastDotDashMovingLength, fEdgeLength]
+                    const bool bHandleLine(bIsLine && aLineTargetCallback);
 
-                    if(nCount > 1)
+                    if(bHandleLine)
                     {
-                        // these polygons were created above, there exists none with less than two points,
-                        // thus direct point access below is allowed
-                        const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0));
-                        B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1));
-
-                        if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
+                        if(!aSnippet.count())
                         {
-                            // start of first and end of last are the same -> merge them
-                            aLast.append(aFirst);
-                            aLast.removeDoublePoints();
-                            pLineTarget->setB3DPolygon(0, aLast);
-                            pLineTarget->remove(nCount - 1);
+                            aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
                         }
+
+                        aSnippet.append(aNextPoint);
                     }
+
+                    // prepare move to next edge
+                    fDotDashMovingLength -= fEdgeLength;
                 }
+
+                // prepare next edge step (end point gets new start point)
+                aCurrentPoint = aNextPoint;
             }
-            else
+
+            // append last intermediate results (if exists)
+            if(aSnippet.count())
             {
-                // parameters make no sense, just add source to targets
-                if(pLineTarget)
+                const bool bHandleLine(bIsLine && aLineTargetCallback);
+
+                if(bHandleLine)
                 {
-                    pLineTarget->append(rCandidate);
+                    implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine);
                 }
             }
+
+            if(bIsClosed && aLineTargetCallback)
+            {
+                implHandleFirstLast(aLineTargetCallback, aFirstLine, aLastLine);
+            }
         }
 
         B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter)
diff --git a/include/basegfx/polygon/b2dpolygontools.hxx b/include/basegfx/polygon/b2dpolygontools.hxx
index 2ec15714088b..90001bae7131 100644
--- a/include/basegfx/polygon/b2dpolygontools.hxx
+++ b/include/basegfx/polygon/b2dpolygontools.hxx
@@ -216,7 +216,7 @@ namespace basegfx
             const ::std::vector<double>& rDotDashArray,
             B2DPolyPolygon* pLineTarget,
             B2DPolyPolygon* pGapTarget = nullptr,
-            double fFullDashDotLen = 0.0);
+            double fDotDashLength = 0.0);
 
         // test if point is inside epsilon-range around an edge defined
         // by the two given points. Can be used for HitTesting. The epsilon-range
diff --git a/include/basegfx/polygon/b3dpolygontools.hxx b/include/basegfx/polygon/b3dpolygontools.hxx
index 2e37b64e2516..80383c29625e 100644
--- a/include/basegfx/polygon/b3dpolygontools.hxx
+++ b/include/basegfx/polygon/b3dpolygontools.hxx
@@ -20,9 +20,11 @@
 #ifndef INCLUDED_BASEGFX_POLYGON_B3DPOLYGONTOOLS_HXX
 #define INCLUDED_BASEGFX_POLYGON_B3DPOLYGONTOOLS_HXX
 
+#include <vector>
+#include <functional>
+
 #include <basegfx/point/b3dpoint.hxx>
 #include <basegfx/vector/b3dvector.hxx>
-#include <vector>
 #include <basegfx/basegfxdllapi.h>
 
 
@@ -60,12 +62,18 @@ namespace basegfx
         /** Apply given LineDashing to given polygon
 
             For a description see applyLineDashing in b2dpolygontoos.hxx
+            Also 2nd version with callbacks, see comments in 2D version
         */
+        BASEGFX_DLLPUBLIC void applyLineDashing(
+            const B3DPolygon& rCandidate,
+            const std::vector<double>& rDotDashArray,
+            std::function<void(const basegfx::B3DPolygon& rSnippet)> aLineTargetCallback,
+            double fDotDashLength = 0.0);
         BASEGFX_DLLPUBLIC void applyLineDashing(
             const B3DPolygon& rCandidate,
             const ::std::vector<double>& rDotDashArray,
             B3DPolyPolygon* pLineTarget,
-            double fFullDashDotLen);
+            double fDotDashLength = 0.0);
 
         /** Create/replace normals for given 3d geometry with default normals from given center to outside.
             rCandidate: the 3d geometry to change


More information about the Libreoffice-commits mailing list