Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/drawinglayer/source/tools/emfppath.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <basegfx/point/b2dpoint.hxx>
21
#include <basegfx/polygon/b2dpolygon.hxx>
22
#include <basegfx/polygon/b2dpolypolygon.hxx>
23
#include <sal/log.hxx>
24
#include "emfppath.hxx"
25
26
namespace
27
{
28
    const unsigned char nTopBitInt7 = 0x80;
29
    const unsigned char nSignBitInt7 = 0x40;
30
    // include the sign bit so if it's negative we get
31
    // that "missing" bit pre-set to 1
32
    const unsigned char nValueMaskInt7 = 0x7F;
33
}
34
35
namespace emfplushelper
36
{
37
    typedef double matrix [4][4];
38
39
    constexpr sal_uInt32 nDetails = 8;
40
    constexpr double alpha[nDetails]
41
        = { 1. / nDetails, 2. / nDetails, 3. / nDetails, 4. / nDetails,
42
            5. / nDetails, 6. / nDetails, 7. / nDetails, 8. / nDetails };
43
44
    // see 2.2.2.21 EmfPlusInteger7
45
    //     2.2.2.22 EmfPlusInteger15
46
    // and 2.2.2.37 EmfPlusPointR Object
47
    static sal_Int16 GetEmfPlusInteger(SvStream& s)
48
3.36k
    {
49
3.36k
        unsigned char u8(0);
50
3.36k
        s.ReadUChar(u8);
51
52
3.36k
        bool bIsEmfPlusInteger15 = u8 & nTopBitInt7;
53
3.36k
        bool bNegative = u8 & nSignBitInt7;
54
3.36k
        unsigned char val1 = u8 & nValueMaskInt7;
55
3.36k
        if (bNegative)
56
1.14k
            val1 |= nTopBitInt7;
57
3.36k
        if (!bIsEmfPlusInteger15)
58
2.54k
        {
59
2.54k
            return static_cast<signed char>(val1);
60
2.54k
        }
61
62
819
        s.ReadUChar(u8);
63
819
        sal_uInt16 nRet = (val1 << 8) | u8;
64
819
        return static_cast<sal_Int16>(nRet);
65
3.36k
    }
66
67
    EMFPPath::EMFPPath (sal_uInt32 _nPoints, bool _bHasPointTypes)
68
1.59k
        : nPoints(_nPoints)
69
1.59k
        , bHasPointTypes(_bHasPointTypes)
70
1.59k
    {
71
1.59k
    }
72
73
    EMFPPath::~EMFPPath ()
74
1.59k
    {
75
1.59k
    }
76
77
    void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags)
78
1.59k
    {
79
        // A point requires at least this many bytes in the stream, so the
80
        // stream that is left says how many points there can really be,
81
        // whatever count the file gave.
82
1.59k
        sal_uInt32 nBytesPerPoint;
83
1.59k
        if (pathFlags & 0x800)
84
175
            nBytesPerPoint = 2; // EmfPlusPointR, one byte for each of the two coordinates
85
1.41k
        else if (pathFlags & 0x4000)
86
27
            nBytesPerPoint = 4; // EmfPlusPoint
87
1.38k
        else
88
1.38k
            nBytesPerPoint = 8; // EmfPlusPointF
89
1.59k
        if (bHasPointTypes)
90
1.21k
            nBytesPerPoint += 1;
91
92
1.59k
        const sal_uInt64 nMaxPoints = s.remainingSize() / nBytesPerPoint;
93
1.59k
        if (nPoints > nMaxPoints)
94
535
        {
95
535
            SAL_WARN("drawinglayer.emf", "EMF+\tpath wants " << nPoints
96
535
                     << " points, the stream holds " << nMaxPoints);
97
535
            nPoints = nMaxPoints;
98
535
        }
99
100
1.59k
        if (bHasPointTypes)
101
1.21k
            pPointTypes.reset( new sal_uInt8 [nPoints] );
102
103
1.59k
        float fx(0), fy(0);
104
9.95k
        for (sal_uInt32 i = 0; i < nPoints; i++)
105
8.36k
        {
106
8.36k
            if (pathFlags & 0x800)
107
1.68k
            {
108
                // EMFPlusPointR: points are stored in EMFPlusInteger7 or
109
                // EMFPlusInteger15 objects, see section 2.2.2.21/22
110
                // If 0x800 bit is set, the 0x4000 bit is undefined and must be ignored
111
1.68k
                sal_Int32 x = GetEmfPlusInteger(s);
112
1.68k
                sal_Int32 y = GetEmfPlusInteger(s);
113
1.68k
                xPoints.push_back(x);
114
1.68k
                yPoints.push_back(y);
115
1.68k
                SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPointR [x,y]: " << x << ", " << y);
116
1.68k
            }
117
6.68k
            else if (pathFlags & 0x4000)
118
184
            {
119
                // EMFPlusPoint: stored in signed short 16bit integer format
120
184
                sal_Int16 x(0), y(0);
121
122
184
                s.ReadInt16(x).ReadInt16(y);
123
184
                SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y);
124
184
                xPoints.push_back(x);
125
184
                yPoints.push_back(y);
126
184
            }
127
6.50k
            else
128
6.50k
            {
129
                // EMFPlusPointF: stored in Single (float) format
130
6.50k
                s.ReadFloat(fx).ReadFloat(fy);
131
6.50k
                SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << fx << ", " << fy);
132
6.50k
                xPoints.push_back(fx);
133
6.50k
                yPoints.push_back(fy);
134
6.50k
            }
135
8.36k
        }
136
137
1.59k
        if (pPointTypes)
138
1.21k
        {
139
4.91k
            for (sal_uInt32 i = 0; i < nPoints; i++)
140
3.70k
            {
141
3.70k
                s.ReadUChar(pPointTypes[i]);
142
3.70k
                SAL_INFO("drawinglayer.emf", "EMF+\tpoint type: 0x" << std::hex << static_cast<int>(pPointTypes[i]) << std::dec);
143
3.70k
            }
144
1.21k
        }
145
146
1.59k
        aPolygon.clear();
147
1.59k
    }
148
149
    ::basegfx::B2DPolyPolygon& EMFPPath::GetPolygon (EmfPlusHelperData const & rR, bool bMapIt, bool bAddLineToCloseShape)
150
1.26k
    {
151
1.26k
        ::basegfx::B2DPolygon polygon;
152
1.26k
        aPolygon.clear ();
153
1.26k
        sal_uInt32 last_normal = 0, p = 0;
154
1.26k
        ::basegfx::B2DPoint prev, mapped;
155
1.26k
        bool hasPrev = false;
156
157
7.02k
        for (sal_uInt32 i = 0; i < nPoints; i++)
158
5.76k
        {
159
5.76k
            if (p && pPointTypes && (pPointTypes[i] == 0))
160
720
            {
161
720
                aPolygon.append (polygon);
162
720
                last_normal = i;
163
720
                p = 0;
164
720
                polygon.clear ();
165
720
            }
166
167
5.76k
            if (bMapIt)
168
5.49k
                mapped = rR.Map(xPoints[i], yPoints[i]);
169
265
            else
170
265
                mapped = ::basegfx::B2DPoint(xPoints[i], yPoints[i]);
171
172
5.76k
            if (pPointTypes)
173
3.72k
            {
174
3.72k
                if ((pPointTypes[i] & 0x07) == 3)
175
586
                {
176
586
                    if (((i - last_normal) % 3) == 1)
177
251
                    {
178
251
                        assert(p != 0);
179
251
                        polygon.setNextControlPoint (p - 1, mapped);
180
251
                        SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon append next: " << p - 1 << " mapped: " << mapped.getX () << "," << mapped.getY ());
181
251
                        continue;
182
251
                    }
183
335
                    else if (((i - last_normal) % 3) == 2)
184
141
                    {
185
141
                        prev = mapped;
186
141
                        hasPrev = true;
187
141
                        continue;
188
141
                    }
189
586
                }
190
3.13k
                else
191
3.13k
                {
192
3.13k
                    last_normal = i;
193
3.13k
                }
194
3.72k
            }
195
196
5.36k
            polygon.append (mapped);
197
5.36k
            SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << xPoints[i] << "," << yPoints[i] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
198
199
5.36k
            if (hasPrev)
200
133
            {
201
133
                polygon.setPrevControlPoint (p, prev);
202
133
                SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ());
203
133
                hasPrev = false;
204
133
            }
205
206
5.36k
            p++;
207
208
5.36k
            if (pPointTypes && (pPointTypes[i] & 0x80)) // closed polygon
209
234
            {
210
234
                polygon.setClosed (true);
211
234
                aPolygon.append (polygon);
212
234
                SAL_INFO ("drawinglayer.emf", "EMF+\t\tClose polygon");
213
234
                last_normal = i + 1;
214
234
                p = 0;
215
234
                polygon.clear ();
216
234
            }
217
5.36k
        }
218
219
        // Draw an extra line between the last point and the first point, to close the shape.
220
1.26k
        if (bAddLineToCloseShape)
221
2
        {
222
2
            polygon.setClosed (true);
223
2
        }
224
225
1.26k
        if (polygon.count ())
226
1.05k
        {
227
1.05k
            aPolygon.append (polygon);
228
229
#if OSL_DEBUG_LEVEL > 1
230
            for (unsigned int i=0; i<aPolygon.count(); i++) {
231
                polygon = aPolygon.getB2DPolygon(i);
232
                SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon: " << i);
233
                for (unsigned int j=0; j<polygon.count(); j++) {
234
                    ::basegfx::B2DPoint point = polygon.getB2DPoint(j);
235
                    SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tPoint: " << point.getX() << "," << point.getY());
236
                    if (polygon.isPrevControlPointUsed(j)) {
237
                        point = polygon.getPrevControlPoint(j);
238
                        SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tPrev: " << point.getX() << "," << point.getY());
239
                    }
240
                    if (polygon.isNextControlPointUsed(j)) {
241
                        point = polygon.getNextControlPoint(j);
242
                        SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tNext: " << point.getX() << "," << point.getY());
243
                    }
244
                }
245
            }
246
#endif
247
1.05k
        }
248
249
1.26k
        return aPolygon;
250
1.26k
    }
251
252
    static void GetCardinalMatrix(float tension, matrix& m)
253
141
    {
254
141
        m[0][1] = 2. - tension;
255
141
        m[0][2] = tension - 2.;
256
141
        m[1][0] = 2. * tension;
257
141
        m[1][1] = tension - 3.;
258
141
        m[1][2] = 3. - 2. * tension;
259
141
        m[3][1] = 1.;
260
141
        m[0][3] = m[2][2] = tension;
261
141
        m[0][0] = m[1][3] = m[2][0] = -tension;
262
141
        m[2][1] = m[2][3] = m[3][0] = m[3][2] = m[3][3] = 0.;
263
141
    }
264
265
    static double calculateSplineCoefficients(float p0, float p1, float p2, float p3, sal_uInt32 step, matrix m)
266
36.5k
    {
267
36.5k
        double a = m[0][0] * p0 + m[0][1] * p1 + m[0][2] * p2 + m[0][3] * p3;
268
36.5k
        double b = m[1][0] * p0 + m[1][1] * p1 + m[1][2] * p2 + m[1][3] * p3;
269
36.5k
        double c = m[2][0] * p0 + m[2][2] * p2;
270
36.5k
        double d = p1;
271
36.5k
        return (d + alpha[step] * (c + alpha[step] * (b + alpha[step] * a)));
272
36.5k
    }
273
274
    ::basegfx::B2DPolyPolygon& EMFPPath::GetCardinalSpline(EmfPlusHelperData const& rR, float fTension,
275
                                                           sal_uInt32 aOffset, sal_uInt32 aNumSegments)
276
63
    {
277
63
        ::basegfx::B2DPolygon polygon;
278
63
        matrix mat;
279
63
        double x, y;
280
63
        if (nPoints < 1)
281
34
            return aPolygon;
282
29
        if (aNumSegments >= nPoints)
283
27
            aNumSegments = nPoints - 1;
284
29
        if (aOffset >= aNumSegments)
285
9
            return aPolygon;
286
20
        GetCardinalMatrix(fTension, mat);
287
        // duplicate first point
288
20
        xPoints.push_front(xPoints.front());
289
20
        yPoints.push_front(yPoints.front());
290
        // duplicate last point
291
20
        xPoints.push_back(xPoints.back());
292
20
        yPoints.push_back(yPoints.back());
293
294
238
        for (sal_uInt32 i = 3 + aOffset; i < aNumSegments + 3; i++)
295
218
        {
296
1.96k
            for (sal_uInt32 s = 0; s < nDetails; s++)
297
1.74k
            {
298
1.74k
                x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1],
299
1.74k
                                                xPoints[i], s, mat);
300
1.74k
                y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1],
301
1.74k
                                                yPoints[i], s, mat);
302
1.74k
                polygon.append(rR.Map(x, y));
303
1.74k
            }
304
218
        }
305
20
        if (polygon.count())
306
20
            aPolygon.append(polygon);
307
20
        return aPolygon;
308
29
    }
309
310
    ::basegfx::B2DPolyPolygon& EMFPPath::GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension)
311
137
    {
312
137
        ::basegfx::B2DPolygon polygon;
313
137
        matrix mat;
314
137
        double x, y;
315
137
        if (nPoints < 3)
316
16
            return aPolygon;
317
121
        GetCardinalMatrix(fTension, mat);
318
        // add three first points at the end
319
121
        xPoints.push_back(xPoints[0]);
320
121
        yPoints.push_back(yPoints[0]);
321
121
        xPoints.push_back(xPoints[1]);
322
121
        yPoints.push_back(yPoints[1]);
323
121
        xPoints.push_back(xPoints[2]);
324
121
        yPoints.push_back(yPoints[2]);
325
326
2.19k
        for (sal_uInt32 i = 3; i < nPoints + 3; i++)
327
2.06k
        {
328
18.6k
            for (sal_uInt32 s = 0; s < nDetails; s++)
329
16.5k
            {
330
16.5k
                x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1],
331
16.5k
                                                xPoints[i], s, mat);
332
16.5k
                y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1],
333
16.5k
                                                yPoints[i], s, mat);
334
16.5k
                polygon.append(rR.Map(x, y));
335
16.5k
            }
336
2.06k
        }
337
121
        polygon.setClosed(true);
338
121
        if (polygon.count())
339
121
            aPolygon.append(polygon);
340
121
        return aPolygon;
341
137
    }
342
} // namespace emfplushelper
343
344
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */