summaryrefslogtreecommitdiff
path: root/drawinglayer
diff options
context:
space:
mode:
authorBartosz Kosiorek <gang65@poczta.onet.pl>2023-06-01 21:16:06 +0200
committerBartosz Kosiorek <gang65@poczta.onet.pl>2023-06-01 21:17:06 +0200
commit0b5c0d9e9d6c71d3531a7e1020af1753a50e873e (patch)
treea2985a86eb829107f78437f3f9e74dfe8739e8a7 /drawinglayer
parent0fff72154d0dbb44e09546ff59f8a5ec288dfd2e (diff)
tdf#143877 EMF+ Implement EmfPlusDrawCurve with cardinal spline
Change-Id: I98d30b2a8ba63fdddc08668f453c5f0feeb452db Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152288 Tested-by: Jenkins Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/source/tools/emfphelperdata.cxx56
-rw-r--r--drawinglayer/source/tools/emfphelperdata.hxx2
-rw-r--r--drawinglayer/source/tools/emfppath.cxx126
-rw-r--r--drawinglayer/source/tools/emfppath.hxx5
4 files changed, 158 insertions, 31 deletions
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx
index 879d40b925d5..b5b7d911476f 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -81,6 +81,8 @@ namespace emfplushelper
case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
+ case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
+ case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve";
case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
@@ -90,7 +92,6 @@ namespace emfplushelper
case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
- case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
@@ -1374,6 +1375,30 @@ namespace emfplushelper
EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
break;
}
+ case EmfPlusRecordTypeDrawCurve:
+ {
+ sal_uInt32 aOffset, aNumSegments, points;
+ float aTension;
+ rMS.ReadFloat(aTension);
+ rMS.ReadUInt32(aOffset);
+ rMS.ReadUInt32(aNumSegments);
+ rMS.ReadUInt32(points);
+ SAL_WARN("drawinglayer.emf",
+ "EMF+\t Tension: " << aTension << " Offset: " << aOffset
+ << " NumSegments: " << aNumSegments
+ << " Points: " << points);
+
+ EMFPPath path(points, true);
+ path.Read(rMS, flags);
+
+ if (points >= 2)
+ EMFPPlusDrawPolygon(
+ path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments),
+ flags & 0xff);
+ else
+ SAL_WARN("drawinglayer.emf", "Not enough number of points");
+ break;
+ }
case EmfPlusRecordTypeDrawClosedCurve:
case EmfPlusRecordTypeFillClosedCurve:
{
@@ -1383,28 +1408,29 @@ namespace emfplushelper
if (type == EmfPlusRecordTypeFillClosedCurve)
{
rMS.ReadUInt32(brushIndexOrColor);
- SAL_INFO("drawinglayer.emf",
+ SAL_INFO(
+ "drawinglayer.emf",
"EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
}
rMS.ReadFloat(aTension);
rMS.ReadUInt32(points);
SAL_WARN("drawinglayer.emf",
- "EMF+\t Tension: " << aTension << " Points: " << points);
- SAL_WARN_IF(aTension != 0, "drawinglayer.emf",
- "EMF+\t TODO Add support for tension different than 0");
+ "EMF+\t Tension: " << aTension << " Points: " << points);
SAL_INFO("drawinglayer.emf",
- "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
- << std::hex << brushIndexOrColor << std::dec);
-
+ "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
+ << std::hex << brushIndexOrColor << std::dec);
+ if (points < 3)
+ {
+ SAL_WARN("drawinglayer.emf", "Not enough number of points");
+ break;
+ }
EMFPPath path(points, true);
path.Read(rMS, flags);
if (type == EmfPlusRecordTypeFillClosedCurve)
- EMFPPlusFillPolygon(path.GetPolygon(*this, /* bMapIt */ true,
- /*bAddLineToCloseShape */ true),
+ EMFPPlusFillPolygon(path.GetClosedCardinalSpline(*this, aTension),
flags & 0x8000, brushIndexOrColor);
else
- EMFPPlusDrawPolygon(path.GetPolygon(*this, /* bMapIt */ true,
- /*bAddLineToCloseShape */ true),
+ EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension),
flags & 0xff);
break;
}
@@ -1431,7 +1457,7 @@ namespace emfplushelper
::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
SAL_INFO("drawinglayer.emf",
- "EMF+\t "
+ "EMF+\t "
<< (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
: "DrawImagePoints")
<< " source rectangle: " << sx << "," << sy << " " << sw << "x"
@@ -1509,8 +1535,8 @@ namespace emfplushelper
SAL_INFO(
"drawinglayer.emf",
"EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
- ::basegfx::B2DPoint aDstPoint(dx, dy);
- ::basegfx::B2DSize aDstSize(dw, dh);
+ const ::basegfx::B2DPoint aDstPoint(dx, dy);
+ const ::basegfx::B2DSize aDstSize(dw, dh);
const basegfx::B2DHomMatrix aTransformMatrix
= maMapTransform
diff --git a/drawinglayer/source/tools/emfphelperdata.hxx b/drawinglayer/source/tools/emfphelperdata.hxx
index ef870f31e687..cf9e3b8855d6 100644
--- a/drawinglayer/source/tools/emfphelperdata.hxx
+++ b/drawinglayer/source/tools/emfphelperdata.hxx
@@ -56,7 +56,7 @@ namespace emfplushelper
#define EmfPlusRecordTypeDrawPath 0x4015
#define EmfPlusRecordTypeFillClosedCurve 0x4016
#define EmfPlusRecordTypeDrawClosedCurve 0x4017
- //TODO EmfPlusRecordTypeDrawCurve 0x4018
+ #define EmfPlusRecordTypeDrawCurve 0x4018
#define EmfPlusRecordTypeDrawBeziers 0x4019
#define EmfPlusRecordTypeDrawImage 0x401A
#define EmfPlusRecordTypeDrawImagePoints 0x401B
diff --git a/drawinglayer/source/tools/emfppath.cxx b/drawinglayer/source/tools/emfppath.cxx
index 4217e3616087..bd5b2d357b0f 100644
--- a/drawinglayer/source/tools/emfppath.cxx
+++ b/drawinglayer/source/tools/emfppath.cxx
@@ -34,6 +34,8 @@ namespace
namespace emfplushelper
{
+ typedef float matrix [4][4];
+
// see 2.2.2.21 EmfPlusInteger7
// 2.2.2.22 EmfPlusInteger15
// and 2.2.2.37 EmfPlusPointR Object
@@ -65,7 +67,6 @@ namespace emfplushelper
}
nPoints = _nPoints;
- pPoints.reset( new float [nPoints*2] );
if (!bLines)
pPointTypes.reset( new sal_uInt8 [_nPoints] );
@@ -77,7 +78,8 @@ namespace emfplushelper
void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags)
{
- for (sal_uInt32 i = 0; i < nPoints; i ++)
+ float fx, fy;
+ for (sal_uInt32 i = 0; i < nPoints; i++)
{
if (pathFlags & 0x800)
{
@@ -86,8 +88,8 @@ namespace emfplushelper
// If 0x800 bit is set, the 0x4000 bit is undefined and must be ignored
sal_Int32 x = GetEmfPlusInteger(s);
sal_Int32 y = GetEmfPlusInteger(s);
- pPoints [i*2] = x;
- pPoints [i*2 + 1] = y;
+ xPoints.push_back(x);
+ yPoints.push_back(y);
SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPointR [x,y]: " << x << ", " << y);
}
else if (pathFlags & 0x4000)
@@ -95,16 +97,18 @@ namespace emfplushelper
// EMFPlusPoint: stored in signed short 16bit integer format
sal_Int16 x, y;
- s.ReadInt16( x ).ReadInt16( y );
- SAL_INFO ("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y);
- pPoints [i*2] = x;
- pPoints [i*2 + 1] = y;
+ s.ReadInt16(x).ReadInt16(y);
+ SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y);
+ xPoints.push_back(x);
+ yPoints.push_back(y);
}
else
{
// EMFPlusPointF: stored in Single (float) format
- s.ReadFloat( pPoints [i*2] ).ReadFloat( pPoints [i*2 + 1] );
- SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << pPoints [i * 2] << ", " << pPoints [i * 2 + 1]);
+ s.ReadFloat(fx).ReadFloat(fy);
+ SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << fx << ", " << fy);
+ xPoints.push_back(fx);
+ yPoints.push_back(fy);
}
}
@@ -128,7 +132,7 @@ namespace emfplushelper
::basegfx::B2DPoint prev, mapped;
bool hasPrev = false;
- for (sal_uInt32 i = 0; i < nPoints; i ++)
+ for (sal_uInt32 i = 0; i < nPoints; i++)
{
if (p && pPointTypes && (pPointTypes [i] == 0))
{
@@ -139,9 +143,9 @@ namespace emfplushelper
}
if (bMapIt)
- mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]);
+ mapped = rR.Map(xPoints[i], yPoints [i]);
else
- mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]);
+ mapped = ::basegfx::B2DPoint(xPoints[i], yPoints[i]);
if (pPointTypes)
{
@@ -167,7 +171,7 @@ namespace emfplushelper
}
polygon.append (mapped);
- SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
+ SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << xPoints[i] << "," << yPoints[i] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
if (hasPrev)
{
@@ -221,6 +225,100 @@ namespace emfplushelper
return aPolygon;
}
+
+ static void GetCardinalMatrix(float tension, matrix& m)
+ {
+ m[0][1] = 2. - tension;
+ m[0][2] = tension - 2.;
+ m[1][0] = 2. * tension;
+ m[1][1] = tension - 3.;
+ m[1][2] = 3. - 2 * tension;
+ m[3][1] = 1.;
+ m[0][3] = m[2][2] = tension;
+ m[0][0] = m[1][3] = m[2][0] = -tension;
+ m[2][1] = m[2][3] = m[3][0] = m[3][2] = m[3][3] = 0.;
+ }
+
+ static float calculateSplineCoefficients(float p0, float p1, float p2, float p3, float alpha, matrix m)
+ {
+ float a, b, c, d;
+ a = m[0][0] * p0 + m[0][1] * p1 + m[0][2] * p2 + m[0][3] * p3;
+ b = m[1][0] * p0 + m[1][1] * p1 + m[1][2] * p2 + m[1][3] * p3;
+ c = m[2][0] * p0 + m[2][2] * p2;
+ d = p1;
+ return (d + alpha * (c + alpha * (b + alpha * a)));
+ }
+
+ ::basegfx::B2DPolyPolygon& EMFPPath::GetCardinalSpline(EmfPlusHelperData const& rR, float fTension,
+ sal_uInt32 aOffset, sal_uInt32 aNumSegments)
+ {
+ ::basegfx::B2DPolygon polygon;
+ matrix mat;
+ float x, y;
+ constexpr sal_uInt32 nDetails = 8;
+ constexpr float alpha[nDetails]
+ = { 1. / nDetails, 2. / nDetails, 3. / nDetails, 4. / nDetails,
+ 5. / nDetails, 6. / nDetails, 7. / nDetails, 8. / nDetails };
+ if (aNumSegments >= nPoints)
+ aNumSegments = nPoints - 1;
+ GetCardinalMatrix(fTension, mat);
+ // duplicate first point
+ xPoints.push_front(xPoints.front());
+ yPoints.push_front(yPoints.front());
+ // duplicate last point
+ xPoints.push_back(xPoints.back());
+ yPoints.push_back(yPoints.back());
+
+ for (sal_uInt32 i = 3 + aOffset; i < aNumSegments + 3; i++)
+ {
+ for (sal_uInt32 s = 0; s < nDetails; s++)
+ {
+ x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1],
+ xPoints[i], alpha[s], mat);
+ y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1],
+ yPoints[i], alpha[s], mat);
+ polygon.append(rR.Map(x, y));
+ }
+ }
+ if (polygon.count())
+ aPolygon.append(polygon);
+ return aPolygon;
+ }
+
+ ::basegfx::B2DPolyPolygon& EMFPPath::GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension)
+ {
+ ::basegfx::B2DPolygon polygon;
+ matrix mat;
+ float x, y;
+ constexpr sal_uInt32 nDetails = 8;
+ constexpr float alpha[nDetails]
+ = { 1. / nDetails, 2. / nDetails, 3. / nDetails, 4. / nDetails,
+ 5. / nDetails, 6. / nDetails, 7. / nDetails, 8. / nDetails };
+ GetCardinalMatrix(fTension, mat);
+ // add three first points at the end
+ xPoints.push_back(xPoints[0]);
+ yPoints.push_back(yPoints[0]);
+ xPoints.push_back(xPoints[1]);
+ yPoints.push_back(yPoints[1]);
+ xPoints.push_back(xPoints[2]);
+ yPoints.push_back(yPoints[2]);
+
+ for (sal_uInt32 i = 3; i < nPoints + 3; i++)
+ {
+ for (sal_uInt32 s = 0; s < nDetails; s++)
+ {
+ x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1],
+ xPoints[i], alpha[s], mat);
+ y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1],
+ yPoints[i], alpha[s], mat);
+ polygon.append(rR.Map(x, y));
+ }
+ }
+ polygon.setClosed(true);
+ if (polygon.count())
+ aPolygon.append(polygon);
+ return aPolygon;
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/emfppath.hxx b/drawinglayer/source/tools/emfppath.hxx
index 94a3d342f887..e6104fcb1fc7 100644
--- a/drawinglayer/source/tools/emfppath.hxx
+++ b/drawinglayer/source/tools/emfppath.hxx
@@ -27,7 +27,7 @@ namespace emfplushelper
{
::basegfx::B2DPolyPolygon aPolygon;
sal_uInt32 nPoints;
- std::unique_ptr<float[]> pPoints;
+ std::deque<float> xPoints, yPoints;
std::unique_ptr<sal_uInt8[]> pPointTypes;
public:
@@ -38,6 +38,9 @@ namespace emfplushelper
void Read(SvStream& s, sal_uInt32 pathFlags);
::basegfx::B2DPolyPolygon& GetPolygon(EmfPlusHelperData const & rR, bool bMapIt = true, bool bAddLineToCloseShape = false);
+ ::basegfx::B2DPolyPolygon& GetCardinalSpline(EmfPlusHelperData const& rR, float fTension,
+ sal_uInt32 aOffset, sal_uInt32 aNumSegments);
+ ::basegfx::B2DPolyPolygon& GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension);
};
}