tdf#154789 EMF+ Performance boost of the EmfPlusRecordTypeDrawBeziers

There is several benefits of such performance optimization:
 1. We are drawing single curve instead of hundreds of small curves.
    In the loop we are creating single Polygon and outside
    of the loop we are invoking EMFPPlusDrawPolygon drawing method only
    once. As https://bugs.documentfoundation.org/attachment.cgi?id=186725
    image is using single EmfPlusRecordTypeDrawBeziers EMF+ record with
    hundreds of curves, with using single EMFPPlusDrawPolygon call,
    there is no lnger need for individual line creation (e.g. line
    color, weight, line caps, line joints, line dashes, etc.)

    - The PDF export performance without optimizations of the https://bugs.documentfoundation.org/attachment.cgi?id=186725:

        time ./instdir/program/soffice --headless --convert-to "pdf:writer_pdf_Export" --outdir ~ ~/Pobrane/problem.docx
        real 24m18,471s
        user 2m56,004s
        sys 1m37,816

    - The PDF export performance with optimizations:

        real 0m37,527s
        user 0m37,004s
        sys 0m0,531s

    - With Libreoffice 7.5.2 from Ubuntu 22.04, the conversion was
    crashed.

 2. The PDF export for document: https://bugs.documentfoundation.org/attachment.cgi?id=186725
    was not working correctly for me. The original image is containing
    chart. Without optimization, the exported chart was empty.
    Current export is working correctly, and graph is visible.

 3. The standard opening of the document from https://bugs.documentfoundation.org/attachment.cgi?id=186725
    is now much faster. The zooming in, move image operations are also
    noticible faster.

 4. Implementation is according to [MS-EMFPLUS] documentation of the
    EmfPlusDrawBeziers, which states:
    "The ending coordinate of one Bezier curve is the starting coordinate of the
next. The control points are used for producing the Bezier effect."

Change-Id: Ic77d4c20a462587bb5da4a4df757e30c5ca04fc9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150821
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
(cherry picked from commit ce008fa9d8f2752bdfeaeff763aafc774a4b4fb2)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150788
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
(cherry picked from commit 1328e2b7eb5251162834d7c0f953c6334686e95e)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150911
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
index b8a3abb..94c4c32 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -1345,11 +1345,15 @@ namespace emfplushelper
                        rMS.ReadUInt32(aCount);
                        SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
                        SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
                        SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16...");
                        SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
                                    "EMF+\t Bezier Draw not support number of points other than 4, 7, "
                                    "10, 13, 16...");

                        if (aCount < 4)
                        {
                            SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount);
                            SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
                                                         "than 4 points. Number of points: "
                                                             << aCount);
                            break;
                        }

@@ -1357,29 +1361,26 @@ namespace emfplushelper
                        // We need to add first starting point
                        aStartPoint = Map(x1, y1);
                        aPolygon.append(aStartPoint);

                        SAL_INFO("drawinglayer.emf",
                                 "EMF+\t Bezier starting point: " << x1 << "," << y1);
                        for (sal_uInt32 i = 4; i <= aCount; i += 3)
                        {
                            ReadPoint(rMS, x2, y2, flags);
                            ReadPoint(rMS, x3, y3, flags);
                            ReadPoint(rMS, x4, y4, flags);

                            SAL_INFO("drawinglayer.emf", "EMF+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4);
                            SAL_INFO("drawinglayer.emf",
                                     "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
                                                              << y3 << " " << x4 << "," << y4);

                            aStartPoint = Map(x1, y1);
                            aControlPointA = Map(x2, y2);
                            aControlPointB = Map(x3, y3);
                            aEndPoint = Map(x4, y4);

                            ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint);
                            cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0);

                            EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);

                            aPolygon.appendBezierSegment(aControlPointA, aControlPointB, aEndPoint);
                            // The ending coordinate of one Bezier curve is the starting coordinate of the next.
                            x1 = x4;
                            y1 = y4;
                            aStartPoint = aEndPoint;
                        }
                        EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
                        break;
                    }
                    case EmfPlusRecordTypeDrawClosedCurve:
diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx
index e858fd7..930e4f0 100644
--- a/emfio/qa/cppunit/emf/EmfImportTest.cxx
+++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx
@@ -47,6 +47,7 @@ class Test : public UnoApiXmlTest
    void TestDrawStringAlign();
    void TestDrawStringTransparent();
    void TestDrawStringWithBrush();
    void TestEmfPlusDrawBeziers();
    void TestDrawLine();
    void TestDrawLineWithCaps();
    void TestDrawLineWithDash();
@@ -101,6 +102,7 @@ public:
    CPPUNIT_TEST(TestDrawStringAlign);
    CPPUNIT_TEST(TestDrawStringTransparent);
    CPPUNIT_TEST(TestDrawStringWithBrush);
    CPPUNIT_TEST(TestEmfPlusDrawBeziers);
    CPPUNIT_TEST(TestDrawLine);
    CPPUNIT_TEST(TestDrawLineWithCaps);
    CPPUNIT_TEST(TestDrawLineWithDash);
@@ -376,6 +378,34 @@ void Test::TestDrawStringWithBrush()
                "TIMES NEW ROMAN");
}

void Test::TestEmfPlusDrawBeziers()
{
    // tdf#107019 tdf#154789 EMF+ records: DrawBeziers
    // Check if DrawBeziers is displayed correctly and text is rotated
    Primitive2DSequence aSequence
        = parseEmf(u"emfio/qa/cppunit/emf/data/TestEmfPlusDrawBeziers.emf");
    CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
    drawinglayer::Primitive2dXmlDump dumper;
    xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence));
    CPPUNIT_ASSERT(pDocument);

    assertXPath(pDocument, aXPathPrefix + "polypolygoncolor", 4);
    assertXPath(pDocument, aXPathPrefix + "polypolygoncolor[1]", "color", "#000000");

    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow", 9);
    assertXPath(pDocument, aXPathPrefix + "polygonstrokearrow[9]/line", "color", "#00ff00");

    assertXPath(pDocument, aXPathPrefix + "transform", 5);
    assertXPath(pDocument, aXPathPrefix + "transform[1]/textsimpleportion", "fontcolor", "#000000");
    assertXPath(pDocument, aXPathPrefix + "transform[1]/textsimpleportion", "text", "% Efficiency");
    assertXPath(pDocument, aXPathPrefix + "transform[1]", "xy11", "0");
    assertXPath(pDocument, aXPathPrefix + "transform[1]", "xy12", "4");
    assertXPath(pDocument, aXPathPrefix + "transform[1]", "xy13", "800");
    assertXPath(pDocument, aXPathPrefix + "transform[1]", "xy21", "-4");
    assertXPath(pDocument, aXPathPrefix + "transform[1]", "xy22", "0");
    assertXPath(pDocument, aXPathPrefix + "transform[1]", "xy23", "3195");
}

void Test::TestDrawLine()
{
    // EMF+ with records: DrawLine
diff --git a/emfio/qa/cppunit/emf/data/TestEmfPlusDrawBeziers.emf b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawBeziers.emf
new file mode 100644
index 0000000..11f0741
--- /dev/null
+++ b/emfio/qa/cppunit/emf/data/TestEmfPlusDrawBeziers.emf
Binary files differ