Bug Summary

File:home/maarten/src/libreoffice/core/include/o3tl/cow_wrapper.hxx
Warning:line 291, column 17
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name poly.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D TOOLS_DLLIMPLEMENTATION -D SYSTEM_ZLIB -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/tools/inc -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx

/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx

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 <osl/endian.h>
21#include <osl/diagnose.h>
22#include <sal/log.hxx>
23#include <tools/bigint.hxx>
24#include <tools/debug.hxx>
25#include <tools/helpers.hxx>
26#include <tools/stream.hxx>
27#include <tools/vcompat.hxx>
28#include <tools/gen.hxx>
29#include <poly.h>
30#include <o3tl/safeint.hxx>
31#include <tools/line.hxx>
32#include <tools/poly.hxx>
33#include <basegfx/polygon/b2dpolygon.hxx>
34#include <basegfx/point/b2dpoint.hxx>
35#include <basegfx/vector/b2dvector.hxx>
36#include <basegfx/polygon/b2dpolygontools.hxx>
37#include <basegfx/curve/b2dcubicbezier.hxx>
38
39#include <memory>
40#include <vector>
41#include <iterator>
42#include <algorithm>
43#include <cstring>
44#include <limits.h>
45#include <cmath>
46
47#define EDGE_LEFT1 1
48#define EDGE_TOP2 2
49#define EDGE_RIGHT4 4
50#define EDGE_BOTTOM8 8
51#define EDGE_HORZ(4 | 1) (EDGE_RIGHT4 | EDGE_LEFT1)
52#define EDGE_VERT(2 | 8) (EDGE_TOP2 | EDGE_BOTTOM8)
53#define SMALL_DVALUE0.0000001 0.0000001
54#define FSQRT21.4142135623730950488016887242097 1.4142135623730950488016887242097
55
56static double ImplGetParameter( const Point& rCenter, const Point& rPt, double fWR, double fHR )
57{
58 const long nDX = rPt.X() - rCenter.X();
59 double fAngle = atan2( -rPt.Y() + rCenter.Y(), ( ( nDX == 0 ) ? 0.000000001 : nDX ) );
60
61 return atan2(fWR*sin(fAngle), fHR*cos(fAngle));
62}
63
64ImplPolygon::ImplPolygon( sal_uInt16 nInitSize )
65{
66 ImplInitSize(nInitSize, false);
67}
68
69ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly )
70{
71 if ( rImpPoly.mnPoints )
72 {
73 mxPointAry.reset(new Point[rImpPoly.mnPoints]);
74 memcpy(mxPointAry.get(), rImpPoly.mxPointAry.get(), rImpPoly.mnPoints * sizeof(Point));
75
76 if( rImpPoly.mxFlagAry )
77 {
78 mxFlagAry.reset(new PolyFlags[rImpPoly.mnPoints]);
79 memcpy(mxFlagAry.get(), rImpPoly.mxFlagAry.get(), rImpPoly.mnPoints);
80 }
81 }
82
83 mnPoints = rImpPoly.mnPoints;
84}
85
86ImplPolygon::ImplPolygon( sal_uInt16 nInitSize, const Point* pInitAry, const PolyFlags* pInitFlags )
87{
88 if ( nInitSize )
89 {
90 mxPointAry.reset(new Point[nInitSize]);
91 memcpy(mxPointAry.get(), pInitAry, nInitSize * sizeof(Point));
92
93 if( pInitFlags )
94 {
95 mxFlagAry.reset(new PolyFlags[nInitSize]);
96 memcpy(mxFlagAry.get(), pInitFlags, nInitSize);
97 }
98 }
99
100 mnPoints = nInitSize;
101}
102
103ImplPolygon::ImplPolygon( const tools::Rectangle& rRect )
104{
105 if ( !rRect.IsEmpty() )
106 {
107 ImplInitSize(5);
108 mxPointAry[0] = rRect.TopLeft();
109 mxPointAry[1] = rRect.TopRight();
110 mxPointAry[2] = rRect.BottomRight();
111 mxPointAry[3] = rRect.BottomLeft();
112 mxPointAry[4] = rRect.TopLeft();
113 }
114 else
115 mnPoints = 0;
116}
117
118ImplPolygon::ImplPolygon( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
119{
120 if ( !rRect.IsEmpty() )
121 {
122 tools::Rectangle aRect( rRect );
123 aRect.Justify(); // SJ: i9140
124
125 nHorzRound = std::min( nHorzRound, static_cast<sal_uInt32>(labs( aRect.GetWidth() >> 1 )) );
126 nVertRound = std::min( nVertRound, static_cast<sal_uInt32>(labs( aRect.GetHeight() >> 1 )) );
127
128 if( !nHorzRound && !nVertRound )
129 {
130 ImplInitSize(5);
131 mxPointAry[0] = aRect.TopLeft();
132 mxPointAry[1] = aRect.TopRight();
133 mxPointAry[2] = aRect.BottomRight();
134 mxPointAry[3] = aRect.BottomLeft();
135 mxPointAry[4] = aRect.TopLeft();
136 }
137 else
138 {
139 const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound );
140 const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound );
141 const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound );
142 const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound );
143 std::unique_ptr<tools::Polygon> pEllipsePoly( new tools::Polygon( Point(), nHorzRound, nVertRound ) );
144 sal_uInt16 i, nEnd, nSize4 = pEllipsePoly->GetSize() >> 2;
145
146 ImplInitSize((pEllipsePoly->GetSize() + 1));
147
148 const Point* pSrcAry = pEllipsePoly->GetConstPointAry();
149 Point* pDstAry = mxPointAry.get();
150
151 for( i = 0, nEnd = nSize4; i < nEnd; i++ )
152 pDstAry[ i ] = pSrcAry[ i ] + aTR;
153
154 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
155 pDstAry[ i ] = pSrcAry[ i ] + aTL;
156
157 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
158 pDstAry[ i ] = pSrcAry[ i ] + aBL;
159
160 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
161 pDstAry[ i ] = pSrcAry[ i ] + aBR;
162
163 pDstAry[ nEnd ] = pDstAry[ 0 ];
164 }
165 }
166 else
167 mnPoints = 0;
168}
169
170ImplPolygon::ImplPolygon( const Point& rCenter, long nRadX, long nRadY )
171{
172 if( nRadX && nRadY )
173 {
174 sal_uInt16 nPoints;
175 // Compute default (depends on size)
176 long nRadXY;
177 const bool bOverflow = o3tl::checked_multiply(nRadX, nRadY, nRadXY);
178 if (!bOverflow)
179 {
180 nPoints = static_cast<sal_uInt16>(MinMax(
181 ( F_PI3.14159265358979323846 * ( 1.5 * ( nRadX + nRadY ) -
182 sqrt( static_cast<double>(labs(nRadXY)) ) ) ),
183 32, 256 ));
184 }
185 else
186 {
187 nPoints = 256;
188 }
189
190 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 )
191 nPoints >>= 1;
192
193 // Ceil number of points until divisible by four
194 nPoints = (nPoints + 3) & ~3;
195 ImplInitSize(nPoints);
196
197 sal_uInt16 i;
198 sal_uInt16 nPoints2 = nPoints >> 1;
199 sal_uInt16 nPoints4 = nPoints >> 2;
200 double nAngle;
201 double nAngleStep = F_PI21.57079632679489661923 / ( nPoints4 - 1 );
202
203 for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep )
204 {
205 long nX = FRound( nRadX * cos( nAngle ) );
206 long nY = FRound( -nRadY * sin( nAngle ) );
207
208 Point* pPt = &(mxPointAry[i]);
209 pPt->setX( nX + rCenter.X() );
210 pPt->setY( nY + rCenter.Y() );
211 pPt = &(mxPointAry[nPoints2-i-1]);
212 pPt->setX( -nX + rCenter.X() );
213 pPt->setY( nY + rCenter.Y() );
214 pPt = &(mxPointAry[i+nPoints2]);
215 pPt->setX( -nX + rCenter.X() );
216 pPt->setY( -nY + rCenter.Y() );
217 pPt = &(mxPointAry[nPoints-i-1]);
218 pPt->setX( nX + rCenter.X() );
219 pPt->setY( -nY + rCenter.Y() );
220 }
221 }
222 else
223 mnPoints = 0;
224}
225
226ImplPolygon::ImplPolygon( const tools::Rectangle& rBound, const Point& rStart, const Point& rEnd,
227 PolyStyle eStyle )
228{
229 const long nWidth = rBound.GetWidth();
230 const long nHeight = rBound.GetHeight();
231
232 if( ( nWidth > 1 ) && ( nHeight > 1 ) )
233 {
234 const Point aCenter( rBound.Center() );
235 const long nRadX = aCenter.X() - rBound.Left();
236 const long nRadY = aCenter.Y() - rBound.Top();
237 sal_uInt16 nPoints;
238
239 long nRadXY;
240 const bool bOverflow = o3tl::checked_multiply(nRadX, nRadY, nRadXY);
241 if (!bOverflow)
242 {
243 nPoints = static_cast<sal_uInt16>(MinMax(
244 ( F_PI3.14159265358979323846 * ( 1.5 * ( nRadX + nRadY ) -
245 sqrt( static_cast<double>(labs(nRadXY)) ) ) ),
246 32, 256 ));
247 }
248 else
249 {
250 nPoints = 256;
251 }
252
253
254 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 )
255 nPoints >>= 1;
256
257 // compute threshold
258 const double fRadX = nRadX;
259 const double fRadY = nRadY;
260 const double fCenterX = aCenter.X();
261 const double fCenterY = aCenter.Y();
262 double fStart = ImplGetParameter( aCenter, rStart, fRadX, fRadY );
263 double fEnd = ImplGetParameter( aCenter, rEnd, fRadX, fRadY );
264 double fDiff = fEnd - fStart;
265 double fStep;
266 sal_uInt16 nStart;
267 sal_uInt16 nEnd;
268
269 if( fDiff < 0. )
270 fDiff += F_2PI(2.0*3.14159265358979323846);
271
272 // Proportionally shrink number of points( fDiff / (2PI) );
273 nPoints = std::max( static_cast<sal_uInt16>( ( fDiff * 0.1591549 ) * nPoints ), sal_uInt16(16) );
274 fStep = fDiff / ( nPoints - 1 );
275
276 if( PolyStyle::Pie == eStyle )
277 {
278 const Point aCenter2( FRound( fCenterX ), FRound( fCenterY ) );
279
280 nStart = 1;
281 nEnd = nPoints + 1;
282 ImplInitSize((nPoints + 2));
283 mxPointAry[0] = aCenter2;
284 mxPointAry[nEnd] = aCenter2;
285 }
286 else
287 {
288 ImplInitSize( ( PolyStyle::Chord == eStyle ) ? ( nPoints + 1 ) : nPoints );
289 nStart = 0;
290 nEnd = nPoints;
291 }
292
293 for(; nStart < nEnd; nStart++, fStart += fStep )
294 {
295 Point& rPt = mxPointAry[nStart];
296
297 rPt.setX( FRound( fCenterX + fRadX * cos( fStart ) ) );
298 rPt.setY( FRound( fCenterY - fRadY * sin( fStart ) ) );
299 }
300
301 if( PolyStyle::Chord == eStyle )
302 mxPointAry[nPoints] = mxPointAry[0];
303 }
304 else
305 mnPoints = 0;
306}
307
308ImplPolygon::ImplPolygon( const Point& rBezPt1, const Point& rCtrlPt1,
309 const Point& rBezPt2, const Point& rCtrlPt2, sal_uInt16 nPoints )
310{
311 nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints );
312
313 const double fInc = 1.0 / ( nPoints - 1 );
314 double fK_1 = 0.0, fK1_1 = 1.0;
315 double fK_2, fK_3, fK1_2, fK1_3;
316 const double fX0 = rBezPt1.X();
317 const double fY0 = rBezPt1.Y();
318 const double fX1 = 3.0 * rCtrlPt1.X();
319 const double fY1 = 3.0 * rCtrlPt1.Y();
320 const double fX2 = 3.0 * rCtrlPt2.X();
321 const double fY2 = 3.0 * rCtrlPt2.Y();
322 const double fX3 = rBezPt2.X();
323 const double fY3 = rBezPt2.Y();
324
325 ImplInitSize(nPoints);
326
327 for( sal_uInt16 i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc )
328 {
329 Point& rPt = mxPointAry[i];
330
331 fK_2 = fK_1;
332 fK_2 *= fK_1;
333 fK_3 = fK_2;
334 fK_3 *= fK_1;
335 fK1_2 = fK1_1;
336 fK1_2 *= fK1_1;
337 fK1_3 = fK1_2;
338 fK1_3 *= fK1_1;
339 double fK12 = fK_1 * fK1_2;
340 double fK21 = fK_2 * fK1_1;
341
342 rPt.setX( FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ) );
343 rPt.setY( FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ) );
344 }
345}
346
347// constructor to convert from basegfx::B2DPolygon
348// #i76891# Needed to change from adding all control points (even for unused
349// edges) and creating a fixed-size Polygon in the first run to creating the
350// minimal Polygon. This requires a temporary Point- and Flag-Array for curves
351// and a memcopy at ImplPolygon creation, but contains no zero-controlpoints
352// for straight edges.
353ImplPolygon::ImplPolygon(const basegfx::B2DPolygon& rPolygon)
354 : mnPoints(0)
355{
356 const bool bCurve(rPolygon.areControlPointsUsed());
357 const bool bClosed(rPolygon.isClosed());
358 sal_uInt32 nB2DLocalCount(rPolygon.count());
359
360 if(bCurve)
361 {
362 // #127979# Reduce source point count hard to the limit of the tools Polygon
363 if(nB2DLocalCount > ((0x0000ffff / 3) - 1))
364 {
365 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "365" ": "), "%s", "Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"
); } } while (false)
;
366 nB2DLocalCount = ((0x0000ffff / 3) - 1);
367 }
368
369 // calculate target point count
370 const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1 : 0 ));
371
372 if(nLoopCount)
373 {
374 // calculate maximum array size and allocate; prepare insert index
375 const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1);
376 ImplInitSize(static_cast< sal_uInt16 >(nMaxTargetCount), true);
377
378 // prepare insert index and current point
379 sal_uInt32 nArrayInsert(0);
380 basegfx::B2DCubicBezier aBezier;
381 aBezier.setStartPoint(rPolygon.getB2DPoint(0));
382
383 for(sal_uInt32 a(0); a < nLoopCount; a++)
384 {
385 // add current point (always) and remember StartPointIndex for evtl. later corrections
386 const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY()));
387 const sal_uInt32 nStartPointIndex(nArrayInsert);
388 mxPointAry[nStartPointIndex] = aStartPoint;
389 mxFlagAry[nStartPointIndex] = PolyFlags::Normal;
390 nArrayInsert++;
391
392 // prepare next segment
393 const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount);
394 aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex));
395 aBezier.setControlPointA(rPolygon.getNextControlPoint(a));
396 aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex));
397
398 if(aBezier.isBezier())
399 {
400 // if one is used, add always two control points due to the old schema
401 mxPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY()));
402 mxFlagAry[nArrayInsert] = PolyFlags::Control;
403 nArrayInsert++;
404
405 mxPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY()));
406 mxFlagAry[nArrayInsert] = PolyFlags::Control;
407 nArrayInsert++;
408 }
409
410 // test continuity with previous control point to set flag value
411 if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a))
412 {
413 const basegfx::B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a));
414
415 if(basegfx::B2VectorContinuity::C1 == eCont)
416 {
417 mxFlagAry[nStartPointIndex] = PolyFlags::Smooth;
418 }
419 else if(basegfx::B2VectorContinuity::C2 == eCont)
420 {
421 mxFlagAry[nStartPointIndex] = PolyFlags::Symmetric;
422 }
423 }
424
425 // prepare next polygon step
426 aBezier.setStartPoint(aBezier.getEndPoint());
427 }
428
429 if(bClosed)
430 {
431 // add first point again as closing point due to old definition
432 mxPointAry[nArrayInsert] = mxPointAry[0];
433 mxFlagAry[nArrayInsert] = PolyFlags::Normal;
434 nArrayInsert++;
435 }
436 else
437 {
438 // add last point as closing point
439 const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1));
440 const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY()));
441 mxPointAry[nArrayInsert] = aEnd;
442 mxFlagAry[nArrayInsert] = PolyFlags::Normal;
443 nArrayInsert++;
444 }
445
446 DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)")do { if (true && (!(nArrayInsert <= nMaxTargetCount
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "446" ": "), "%s", "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)"
); } } while (false)
;
447
448 if(nArrayInsert != nMaxTargetCount)
449 {
450 ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert));
451 }
452 }
453 }
454 else
455 {
456 // #127979# Reduce source point count hard to the limit of the tools Polygon
457 if(nB2DLocalCount > (0x0000ffff - 1))
458 {
459 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "459" ": "), "%s", "Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"
); } } while (false)
;
460 nB2DLocalCount = (0x0000ffff - 1);
461 }
462
463 if(nB2DLocalCount)
464 {
465 // point list creation
466 const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1 : 0));
467 ImplInitSize(static_cast< sal_uInt16 >(nTargetCount));
468 sal_uInt16 nIndex(0);
469
470 for(sal_uInt32 a(0); a < nB2DLocalCount; a++)
471 {
472 basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a));
473 Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
474 mxPointAry[nIndex++] = aPoint;
475 }
476
477 if(bClosed)
478 {
479 // add first point as closing point
480 mxPointAry[nIndex] = mxPointAry[0];
481 }
482 }
483 }
484}
485
486bool ImplPolygon::operator==( const ImplPolygon& rCandidate) const
487{
488 return mnPoints == rCandidate.mnPoints &&
489 mxFlagAry.get() == rCandidate.mxFlagAry.get() &&
490 mxPointAry.get() == rCandidate.mxPointAry.get();
491}
492
493void ImplPolygon::ImplInitSize(sal_uInt16 nInitSize, bool bFlags)
494{
495 if (nInitSize)
496 {
497 mxPointAry.reset(new Point[nInitSize]);
498 }
499
500 if (bFlags)
501 {
502 mxFlagAry.reset(new PolyFlags[nInitSize]);
503 memset(mxFlagAry.get(), 0, nInitSize);
504 }
505
506 mnPoints = nInitSize;
507}
508
509void ImplPolygon::ImplSetSize( sal_uInt16 nNewSize, bool bResize )
510{
511 if( mnPoints == nNewSize )
512 return;
513
514 std::unique_ptr<Point[]> xNewAry;
515
516 if (nNewSize)
517 {
518 const std::size_t nNewSz(static_cast<std::size_t>(nNewSize)*sizeof(Point));
519 xNewAry.reset(new Point[nNewSize]);
520
521 if ( bResize )
522 {
523 // Copy the old points
524 if ( mnPoints < nNewSize )
525 {
526 // New points are already implicitly initialized to zero
527 const std::size_t nOldSz(mnPoints * sizeof(Point));
528 if (mxPointAry)
529 memcpy(xNewAry.get(), mxPointAry.get(), nOldSz);
530 }
531 else
532 {
533 if (mxPointAry)
534 memcpy(xNewAry.get(), mxPointAry.get(), nNewSz);
535 }
536 }
537 }
538
539 mxPointAry = std::move(xNewAry);
540
541 // take FlagArray into account, if applicable
542 if( mxFlagAry )
543 {
544 std::unique_ptr<PolyFlags[]> xNewFlagAry;
545
546 if( nNewSize )
547 {
548 xNewFlagAry.reset(new PolyFlags[nNewSize]);
549
550 if( bResize )
551 {
552 // copy the old flags
553 if ( mnPoints < nNewSize )
554 {
555 // initialize new flags to zero
556 memset(xNewFlagAry.get() + mnPoints, 0, nNewSize-mnPoints);
557 memcpy(xNewFlagAry.get(), mxFlagAry.get(), mnPoints);
558 }
559 else
560 memcpy(xNewFlagAry.get(), mxFlagAry.get(), nNewSize);
561 }
562 }
563
564 mxFlagAry = std::move(xNewFlagAry);
565 }
566
567 mnPoints = nNewSize;
568}
569
570bool ImplPolygon::ImplSplit( sal_uInt16 nPos, sal_uInt16 nSpace, ImplPolygon const * pInitPoly )
571{
572 //Can't fit this in :-(, throw ?
573 if (mnPoints + nSpace > USHRT_MAX(32767 *2 +1))
574 {
575 SAL_WARN("tools", "Polygon needs " << mnPoints + nSpace << " points, but only " << USHRT_MAX << " possible")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "tools")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Polygon needs " << mnPoints + nSpace <<
" points, but only " << (32767 *2 +1) << " possible"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "575" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Polygon needs " << mnPoints + nSpace
<< " points, but only " << (32767 *2 +1) <<
" possible"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "Polygon needs " << mnPoints
+ nSpace << " points, but only " << (32767 *2 +1
) << " possible"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "575" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Polygon needs " << mnPoints + nSpace <<
" points, but only " << (32767 *2 +1) << " possible"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "575" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Polygon needs " << mnPoints + nSpace
<< " points, but only " << (32767 *2 +1) <<
" possible"), 0); } else { ::std::ostringstream sal_detail_stream
; sal_detail_stream << "Polygon needs " << mnPoints
+ nSpace << " points, but only " << (32767 *2 +1
) << " possible"; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "575" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
576 return false;
577 }
578
579 const sal_uInt16 nNewSize = mnPoints + nSpace;
580 const std::size_t nSpaceSize = static_cast<std::size_t>(nSpace) * sizeof(Point);
581
582 if( nPos >= mnPoints )
583 {
584 // Append at the back
585 nPos = mnPoints;
586 ImplSetSize( nNewSize );
587
588 if( pInitPoly )
589 {
590 memcpy(mxPointAry.get() + nPos, pInitPoly->mxPointAry.get(), nSpaceSize);
591
592 if (pInitPoly->mxFlagAry)
593 memcpy(mxFlagAry.get() + nPos, pInitPoly->mxFlagAry.get(), nSpace);
594 }
595 }
596 else
597 {
598 const sal_uInt16 nSecPos = nPos + nSpace;
599 const sal_uInt16 nRest = mnPoints - nPos;
600
601 std::unique_ptr<Point[]> xNewAry(new Point[nNewSize]);
602 memcpy(xNewAry.get(), mxPointAry.get(), nPos * sizeof(Point));
603
604 if( pInitPoly )
605 memcpy(xNewAry.get() + nPos, pInitPoly->mxPointAry.get(), nSpaceSize);
606
607 memcpy(xNewAry.get() + nSecPos, mxPointAry.get() + nPos, nRest * sizeof(Point));
608 mxPointAry = std::move(xNewAry);
609
610 // consider FlagArray
611 if (mxFlagAry)
612 {
613 std::unique_ptr<PolyFlags[]> xNewFlagAry(new PolyFlags[nNewSize]);
614
615 memcpy(xNewFlagAry.get(), mxFlagAry.get(), nPos);
616
617 if (pInitPoly && pInitPoly->mxFlagAry)
618 memcpy(xNewFlagAry.get() + nPos, pInitPoly->mxFlagAry.get(), nSpace);
619 else
620 memset(xNewFlagAry.get() + nPos, 0, nSpace);
621
622 memcpy(xNewFlagAry.get() + nSecPos, mxFlagAry.get() + nPos, nRest);
623 mxFlagAry = std::move(xNewFlagAry);
624 }
625
626 mnPoints = nNewSize;
627 }
628
629 return true;
630}
631
632void ImplPolygon::ImplCreateFlagArray()
633{
634 if (!mxFlagAry)
635 {
636 mxFlagAry.reset(new PolyFlags[mnPoints]);
637 memset(mxFlagAry.get(), 0, mnPoints);
638 }
639}
640
641namespace {
642
643class ImplPointFilter
644{
645public:
646 virtual void LastPoint() = 0;
647 virtual void Input( const Point& rPoint ) = 0;
648
649protected:
650 ~ImplPointFilter() {}
651};
652
653class ImplPolygonPointFilter : public ImplPointFilter
654{
655 ImplPolygon maPoly;
656 sal_uInt16 mnSize;
657public:
658 explicit ImplPolygonPointFilter(sal_uInt16 nDestSize)
659 : maPoly(nDestSize)
660 , mnSize(0)
661 {
662 }
663
664 virtual ~ImplPolygonPointFilter()
665 {
666 }
667
668 virtual void LastPoint() override;
669 virtual void Input( const Point& rPoint ) override;
670
671 ImplPolygon& get() { return maPoly; }
672};
673
674}
675
676void ImplPolygonPointFilter::Input( const Point& rPoint )
677{
678 if ( !mnSize || (rPoint != maPoly.mxPointAry[mnSize-1]) )
679 {
680 mnSize++;
681 if ( mnSize > maPoly.mnPoints )
682 maPoly.ImplSetSize( mnSize );
683 maPoly.mxPointAry[mnSize-1] = rPoint;
684 }
685}
686
687void ImplPolygonPointFilter::LastPoint()
688{
689 if ( mnSize < maPoly.mnPoints )
690 maPoly.ImplSetSize( mnSize );
691};
692
693namespace {
694
695class ImplEdgePointFilter : public ImplPointFilter
696{
697 Point maFirstPoint;
698 Point maLastPoint;
699 ImplPointFilter& mrNextFilter;
700 const long mnLow;
701 const long mnHigh;
702 const int mnEdge;
703 int mnLastOutside;
704 bool mbFirst;
705
706public:
707 ImplEdgePointFilter( int nEdge, long nLow, long nHigh,
708 ImplPointFilter& rNextFilter ) :
709 mrNextFilter( rNextFilter ),
710 mnLow( nLow ),
711 mnHigh( nHigh ),
712 mnEdge( nEdge ),
713 mnLastOutside( 0 ),
714 mbFirst( true )
715 {
716 }
717
718 virtual ~ImplEdgePointFilter() {}
719
720 Point EdgeSection( const Point& rPoint, int nEdge ) const;
721 int VisibleSide( const Point& rPoint ) const;
722 bool IsPolygon() const
723 { return maFirstPoint == maLastPoint; }
724
725 virtual void Input( const Point& rPoint ) override;
726 virtual void LastPoint() override;
727};
728
729}
730
731inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const
732{
733 if ( mnEdge & EDGE_HORZ(4 | 1) )
734 {
735 return rPoint.X() < mnLow ? EDGE_LEFT1 :
736 rPoint.X() > mnHigh ? EDGE_RIGHT4 : 0;
737 }
738 else
739 {
740 return rPoint.Y() < mnLow ? EDGE_TOP2 :
741 rPoint.Y() > mnHigh ? EDGE_BOTTOM8 : 0;
742 }
743}
744
745Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const
746{
747 long lx = maLastPoint.X();
748 long ly = maLastPoint.Y();
749 long md = rPoint.X() - lx;
750 long mn = rPoint.Y() - ly;
751 long nNewX;
752 long nNewY;
753
754 if ( nEdge & EDGE_VERT(2 | 8) )
755 {
756 nNewY = (nEdge == EDGE_TOP2) ? mnLow : mnHigh;
757 long dy = nNewY - ly;
758 if ( !md )
759 nNewX = lx;
760 else if ( (LONG_MAX9223372036854775807L / std::abs(md)) >= std::abs(dy) )
761 nNewX = (dy * md) / mn + lx;
762 else
763 {
764 BigInt ady = dy;
765 ady *= md;
766 if( ady.IsNeg() )
767 if( mn < 0 )
768 ady += mn/2;
769 else
770 ady -= (mn-1)/2;
771 else
772 if( mn < 0 )
773 ady -= (mn+1)/2;
774 else
775 ady += mn/2;
776 ady /= mn;
777 nNewX = static_cast<long>(ady) + lx;
778 }
779 }
780 else
781 {
782 nNewX = (nEdge == EDGE_LEFT1) ? mnLow : mnHigh;
783 long dx = nNewX - lx;
784 if ( !mn )
785 nNewY = ly;
786 else if ( (LONG_MAX9223372036854775807L / std::abs(mn)) >= std::abs(dx) )
787 nNewY = (dx * mn) / md + ly;
788 else
789 {
790 BigInt adx = dx;
791 adx *= mn;
792 if( adx.IsNeg() )
793 if( md < 0 )
794 adx += md/2;
795 else
796 adx -= (md-1)/2;
797 else
798 if( md < 0 )
799 adx -= (md+1)/2;
800 else
801 adx += md/2;
802 adx /= md;
803 nNewY = static_cast<long>(adx) + ly;
804 }
805 }
806
807 return Point( nNewX, nNewY );
808}
809
810void ImplEdgePointFilter::Input( const Point& rPoint )
811{
812 int nOutside = VisibleSide( rPoint );
813
814 if ( mbFirst )
815 {
816 maFirstPoint = rPoint;
817 mbFirst = false;
818 if ( !nOutside )
819 mrNextFilter.Input( rPoint );
820 }
821 else if ( rPoint == maLastPoint )
822 return;
823 else if ( !nOutside )
824 {
825 if ( mnLastOutside )
826 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) );
827 mrNextFilter.Input( rPoint );
828 }
829 else if ( !mnLastOutside )
830 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) );
831 else if ( nOutside != mnLastOutside )
832 {
833 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) );
834 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) );
835 }
836
837 maLastPoint = rPoint;
838 mnLastOutside = nOutside;
839}
840
841void ImplEdgePointFilter::LastPoint()
842{
843 if ( !mbFirst )
844 {
845 int nOutside = VisibleSide( maFirstPoint );
846
847 if ( nOutside != mnLastOutside )
848 Input( maFirstPoint );
849 mrNextFilter.LastPoint();
850 }
851}
852
853namespace tools
854{
855
856tools::Polygon Polygon::SubdivideBezier( const tools::Polygon& rPoly )
857{
858 tools::Polygon aPoly;
859
860 // #100127# Use adaptive subdivide instead of fixed 25 segments
861 rPoly.AdaptiveSubdivide( aPoly );
862
863 return aPoly;
864}
865
866Polygon::Polygon() : mpImplPolygon(ImplPolygon())
867{
868}
869
870Polygon::Polygon( sal_uInt16 nSize ) : mpImplPolygon(ImplPolygon(nSize))
871{
872}
873
874Polygon::Polygon( sal_uInt16 nPoints, const Point* pPtAry, const PolyFlags* pFlagAry ) : mpImplPolygon(ImplPolygon(nPoints, pPtAry, pFlagAry))
875{
876}
877
878Polygon::Polygon( const tools::Polygon& rPoly ) : mpImplPolygon(rPoly.mpImplPolygon)
879{
880}
881
882Polygon::Polygon( tools::Polygon&& rPoly) noexcept
883 : mpImplPolygon(std::move(rPoly.mpImplPolygon))
884{
885}
886
887Polygon::Polygon( const tools::Rectangle& rRect ) : mpImplPolygon(ImplPolygon(rRect))
888{
889}
890
891Polygon::Polygon( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
892 : mpImplPolygon(ImplPolygon(rRect, nHorzRound, nVertRound))
893{
894}
895
896Polygon::Polygon( const Point& rCenter, long nRadX, long nRadY )
897 : mpImplPolygon(ImplPolygon(rCenter, nRadX, nRadY))
898{
899}
900
901Polygon::Polygon( const tools::Rectangle& rBound, const Point& rStart, const Point& rEnd,
902 PolyStyle eStyle ) : mpImplPolygon(ImplPolygon(rBound, rStart, rEnd, eStyle))
903{
904}
905
906Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1,
907 const Point& rBezPt2, const Point& rCtrlPt2,
908 sal_uInt16 nPoints ) : mpImplPolygon(ImplPolygon(rBezPt1, rCtrlPt1, rBezPt2, rCtrlPt2, nPoints))
909{
910}
911
912Polygon::~Polygon()
913{
914}
17
Calling '~cow_wrapper'
22
Returning from '~cow_wrapper'
915
916Point * Polygon::GetPointAry()
917{
918 return mpImplPolygon->mxPointAry.get();
919}
920
921const Point* Polygon::GetConstPointAry() const
922{
923 return mpImplPolygon->mxPointAry.get();
924}
925
926const PolyFlags* Polygon::GetConstFlagAry() const
927{
928 return mpImplPolygon->mxFlagAry.get();
929}
930
931void Polygon::SetPoint( const Point& rPt, sal_uInt16 nPos )
932{
933 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "934" ": "), "%s", "Polygon::SetPoint(): nPos >= nPoints"
); } } while (false)
934 "Polygon::SetPoint(): nPos >= nPoints" )do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "934" ": "), "%s", "Polygon::SetPoint(): nPos >= nPoints"
); } } while (false)
;
935
936 mpImplPolygon->mxPointAry[nPos] = rPt;
937}
938
939void Polygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
940{
941 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "942" ": "), "%s", "Polygon::SetFlags(): nPos >= nPoints"
); } } while (false)
942 "Polygon::SetFlags(): nPos >= nPoints" )do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "942" ": "), "%s", "Polygon::SetFlags(): nPos >= nPoints"
); } } while (false)
;
943
944 // we do only want to create the flag array if there
945 // is at least one flag different to PolyFlags::Normal
946 if ( eFlags != PolyFlags::Normal )
947 {
948 mpImplPolygon->ImplCreateFlagArray();
949 mpImplPolygon->mxFlagAry[ nPos ] = eFlags;
950 }
951}
952
953const Point& Polygon::GetPoint( sal_uInt16 nPos ) const
954{
955 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "956" ": "), "%s", "Polygon::GetPoint(): nPos >= nPoints"
); } } while (false)
956 "Polygon::GetPoint(): nPos >= nPoints" )do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "956" ": "), "%s", "Polygon::GetPoint(): nPos >= nPoints"
); } } while (false)
;
957
958 return mpImplPolygon->mxPointAry[nPos];
959}
960
961PolyFlags Polygon::GetFlags( sal_uInt16 nPos ) const
962{
963 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "964" ": "), "%s", "Polygon::GetFlags(): nPos >= nPoints"
); } } while (false)
964 "Polygon::GetFlags(): nPos >= nPoints" )do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "964" ": "), "%s", "Polygon::GetFlags(): nPos >= nPoints"
); } } while (false)
;
965 return mpImplPolygon->mxFlagAry
966 ? mpImplPolygon->mxFlagAry[ nPos ]
967 : PolyFlags::Normal;
968}
969
970bool Polygon::HasFlags() const
971{
972 return bool(mpImplPolygon->mxFlagAry);
973}
974
975bool Polygon::IsRect() const
976{
977 bool bIsRect = false;
978 if (!mpImplPolygon->mxFlagAry)
979 {
980 if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mxPointAry[ 0 ] == mpImplPolygon->mxPointAry[ 4 ] ) ) ||
981 ( mpImplPolygon->mnPoints == 4 ) )
982 {
983 if ( ( mpImplPolygon->mxPointAry[ 0 ].X() == mpImplPolygon->mxPointAry[ 3 ].X() ) &&
984 ( mpImplPolygon->mxPointAry[ 0 ].Y() == mpImplPolygon->mxPointAry[ 1 ].Y() ) &&
985 ( mpImplPolygon->mxPointAry[ 1 ].X() == mpImplPolygon->mxPointAry[ 2 ].X() ) &&
986 ( mpImplPolygon->mxPointAry[ 2 ].Y() == mpImplPolygon->mxPointAry[ 3 ].Y() ) )
987 bIsRect = true;
988 }
989 }
990 return bIsRect;
991}
992
993void Polygon::SetSize( sal_uInt16 nNewSize )
994{
995 if( nNewSize != mpImplPolygon->mnPoints )
996 {
997 mpImplPolygon->ImplSetSize( nNewSize );
998 }
999}
1000
1001sal_uInt16 Polygon::GetSize() const
1002{
1003 return mpImplPolygon->mnPoints;
1004}
1005
1006void Polygon::Clear()
1007{
1008 mpImplPolygon = ImplType(ImplPolygon());
1009}
1010
1011double Polygon::CalcDistance( sal_uInt16 nP1, sal_uInt16 nP2 ) const
1012{
1013 DBG_ASSERT( nP1 < mpImplPolygon->mnPoints,do { if (true && (!(nP1 < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1014" ": "), "%s", "Polygon::CalcDistance(): nPos1 >= nPoints"
); } } while (false)
1014 "Polygon::CalcDistance(): nPos1 >= nPoints" )do { if (true && (!(nP1 < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1014" ": "), "%s", "Polygon::CalcDistance(): nPos1 >= nPoints"
); } } while (false)
;
1015 DBG_ASSERT( nP2 < mpImplPolygon->mnPoints,do { if (true && (!(nP2 < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1016" ": "), "%s", "Polygon::CalcDistance(): nPos2 >= nPoints"
); } } while (false)
1016 "Polygon::CalcDistance(): nPos2 >= nPoints" )do { if (true && (!(nP2 < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1016" ": "), "%s", "Polygon::CalcDistance(): nPos2 >= nPoints"
); } } while (false)
;
1017
1018 const Point& rP1 = mpImplPolygon->mxPointAry[ nP1 ];
1019 const Point& rP2 = mpImplPolygon->mxPointAry[ nP2 ];
1020 const double fDx = rP2.X() - rP1.X();
1021 const double fDy = rP2.Y() - rP1.Y();
1022
1023 return sqrt( fDx * fDx + fDy * fDy );
1024}
1025
1026void Polygon::Optimize( PolyOptimizeFlags nOptimizeFlags )
1027{
1028 DBG_ASSERT( !mpImplPolygon->mxFlagAry, "Optimizing could fail with beziers!" )do { if (true && (!(!mpImplPolygon->mxFlagAry))) {
sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1028" ": "), "%s", "Optimizing could fail with beziers!"
); } } while (false)
;
1
Taking true branch
2
Loop condition is false. Exiting loop
1029
1030 sal_uInt16 nSize = mpImplPolygon->mnPoints;
1031
1032 if( !(bool(nOptimizeFlags) && nSize) )
3
Assuming 'nOptimizeFlags' is not equal to 0
4
Assuming the condition is false
5
Taking false branch
1033 return;
1034
1035 if( nOptimizeFlags & PolyOptimizeFlags::EDGES )
6
Taking false branch
1036 {
1037 const tools::Rectangle aBound( GetBoundRect() );
1038 const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5;
1039 const sal_uInt16 nPercent = 50;
1040
1041 Optimize( PolyOptimizeFlags::NO_SAME );
1042 ImplReduceEdges( *this, fArea, nPercent );
1043 }
1044 else if( nOptimizeFlags & PolyOptimizeFlags::NO_SAME )
7
Taking true branch
1045 {
1046 tools::Polygon aNewPoly;
1047 const Point& rFirst = mpImplPolygon->mxPointAry[ 0 ];
1048
1049 while( nSize
7.1
'nSize' is not equal to 0
7.1
'nSize' is not equal to 0
&& ( mpImplPolygon->mxPointAry[ nSize - 1 ] == rFirst ) )
8
Loop condition is false. Execution continues on line 1052
1050 nSize--;
1051
1052 if( nSize > 1 )
9
Assuming 'nSize' is > 1
10
Taking true branch
1053 {
1054 sal_uInt16 nLast = 0, nNewCount = 1;
1055
1056 aNewPoly.SetSize( nSize );
1057 aNewPoly[ 0 ] = rFirst;
1058
1059 for( sal_uInt16 i = 1; i
10.1
'i' is < 'nSize'
10.1
'i' is < 'nSize'
< nSize
; i++ )
11
Loop condition is true. Entering loop body
13
Assuming 'i' is >= 'nSize'
14
Loop condition is false. Execution continues on line 1068
1060 {
1061 if( mpImplPolygon->mxPointAry[ i ] != mpImplPolygon->mxPointAry[ nLast ])
12
Taking true branch
1062 {
1063 nLast = i;
1064 aNewPoly[ nNewCount++ ] = mpImplPolygon->mxPointAry[ i ];
1065 }
1066 }
1067
1068 if( nNewCount
14.1
'nNewCount' is not equal to 1
14.1
'nNewCount' is not equal to 1
== 1 )
15
Taking false branch
1069 aNewPoly.Clear();
1070 else
1071 aNewPoly.SetSize( nNewCount );
1072 }
1073
1074 *this = aNewPoly;
1075 }
16
Calling '~Polygon'
23
Returning from '~Polygon'
1076
1077 nSize = mpImplPolygon->mnPoints;
24
Calling 'cow_wrapper::operator->'
1078
1079 if( nSize > 1 )
1080 {
1081 if( ( nOptimizeFlags & PolyOptimizeFlags::CLOSE ) &&
1082 ( mpImplPolygon->mxPointAry[ 0 ] != mpImplPolygon->mxPointAry[ nSize - 1 ] ) )
1083 {
1084 SetSize( mpImplPolygon->mnPoints + 1 );
1085 mpImplPolygon->mxPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mxPointAry[ 0 ];
1086 }
1087 }
1088}
1089
1090
1091/** Recursively subdivide cubic bezier curve via deCasteljau.
1092
1093 @param rPointIter
1094 Output iterator, where the subdivided polylines are written to.
1095
1096 @param d
1097 Squared difference of curve to a straight line
1098
1099 @param P*
1100 Exactly four points, interpreted as support and control points of
1101 a cubic bezier curve. Must be in device coordinates, since stop
1102 criterion is based on the following assumption: the device has a
1103 finite resolution, it is thus sufficient to stop subdivision if the
1104 curve does not deviate more than one pixel from a straight line.
1105
1106*/
1107static void ImplAdaptiveSubdivide( ::std::back_insert_iterator< ::std::vector< Point > >& rPointIter,
1108 const double old_d2,
1109 int recursionDepth,
1110 const double d2,
1111 const double P1x, const double P1y,
1112 const double P2x, const double P2y,
1113 const double P3x, const double P3y,
1114 const double P4x, const double P4y )
1115{
1116 // Hard limit on recursion depth, empiric number.
1117 enum {maxRecursionDepth=128};
1118
1119 // Perform bezier flatness test (lecture notes from R. Schaback,
1120 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
1121
1122 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
1123 // 0<=j<=n
1124
1125 // What is calculated here is an upper bound to the distance from
1126 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
1127 // curve. We can drop 0 and n from the running indices, since the
1128 // argument of max becomes zero for those cases.
1129 const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) );
1130 const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) );
1131 const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) );
1132 const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) );
1133 const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y,
1134 fJ2x*fJ2x + fJ2y*fJ2y) );
1135
1136 // stop if error measure does not improve anymore. This is a
1137 // safety guard against floating point inaccuracies.
1138 // stop at recursion level 128. This is a safety guard against
1139 // floating point inaccuracies.
1140 // stop if distance from line is guaranteed to be bounded by d
1141 if( old_d2 > d2 &&
1142 recursionDepth < maxRecursionDepth &&
1143 distance2 >= d2 )
1144 {
1145 // deCasteljau bezier arc, split at t=0.5
1146 // Foley/vanDam, p. 508
1147 const double L1x( P1x ), L1y( P1y );
1148 const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 );
1149 const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 );
1150 const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 );
1151 const double R4x( P4x ), R4y( P4y );
1152 const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 );
1153 const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 );
1154 const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 );
1155 const double L4x( R1x ), L4y( R1y );
1156
1157 // subdivide further
1158 ++recursionDepth;
1159 ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y);
1160 ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y);
1161 }
1162 else
1163 {
1164 // requested resolution reached.
1165 // Add end points to output iterator.
1166 // order is preserved, since this is so to say depth first traversal.
1167 *rPointIter++ = Point( FRound(P1x), FRound(P1y) );
1168 }
1169}
1170
1171void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const
1172{
1173 if (!mpImplPolygon->mxFlagAry)
1174 {
1175 rResult = *this;
1176 }
1177 else
1178 {
1179 sal_uInt16 i;
1180 sal_uInt16 nPts( GetSize() );
1181 ::std::vector< Point > aPoints;
1182 aPoints.reserve( nPts );
1183 ::std::back_insert_iterator< ::std::vector< Point > > aPointIter( aPoints );
1184
1185 for(i=0; i<nPts;)
1186 {
1187 if( ( i + 3 ) < nPts )
1188 {
1189 PolyFlags P1( mpImplPolygon->mxFlagAry[ i ] );
1190 PolyFlags P4( mpImplPolygon->mxFlagAry[ i + 3 ] );
1191
1192 if( ( PolyFlags::Normal == P1 || PolyFlags::Smooth == P1 || PolyFlags::Symmetric == P1 ) &&
1193 ( PolyFlags::Control == mpImplPolygon->mxFlagAry[ i + 1 ] ) &&
1194 ( PolyFlags::Control == mpImplPolygon->mxFlagAry[ i + 2 ] ) &&
1195 ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
1196 {
1197 ImplAdaptiveSubdivide( aPointIter, d*d+1.0, 0, d*d,
1198 mpImplPolygon->mxPointAry[ i ].X(), mpImplPolygon->mxPointAry[ i ].Y(),
1199 mpImplPolygon->mxPointAry[ i+1 ].X(), mpImplPolygon->mxPointAry[ i+1 ].Y(),
1200 mpImplPolygon->mxPointAry[ i+2 ].X(), mpImplPolygon->mxPointAry[ i+2 ].Y(),
1201 mpImplPolygon->mxPointAry[ i+3 ].X(), mpImplPolygon->mxPointAry[ i+3 ].Y() );
1202 i += 3;
1203 continue;
1204 }
1205 }
1206
1207 *aPointIter++ = mpImplPolygon->mxPointAry[ i++ ];
1208
1209 if (aPoints.size() >= SAL_MAX_UINT16((sal_uInt16) 0xFFFF))
1210 {
1211 OSL_ENSURE(aPoints.size() < SAL_MAX_UINT16,do { if (true && (!(aPoints.size() < ((sal_uInt16)
0xFFFF)))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1213" ": "), "%s", "Polygon::AdaptiveSubdivision created polygon too many points;"
" using original polygon instead"); } } while (false)
1212 "Polygon::AdaptiveSubdivision created polygon too many points;"do { if (true && (!(aPoints.size() < ((sal_uInt16)
0xFFFF)))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1213" ": "), "%s", "Polygon::AdaptiveSubdivision created polygon too many points;"
" using original polygon instead"); } } while (false)
1213 " using original polygon instead")do { if (true && (!(aPoints.size() < ((sal_uInt16)
0xFFFF)))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1213" ": "), "%s", "Polygon::AdaptiveSubdivision created polygon too many points;"
" using original polygon instead"); } } while (false)
;
1214
1215 // The resulting polygon can not hold all the points
1216 // that we have created so far. Stop the subdivision
1217 // and return a copy of the unmodified polygon.
1218 rResult = *this;
1219 return;
1220 }
1221 }
1222
1223 // fill result polygon
1224 rResult = tools::Polygon( static_cast<sal_uInt16>(aPoints.size()) ); // ensure sufficient size for copy
1225 ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mxPointAry.get());
1226 }
1227}
1228
1229namespace {
1230
1231class Vector2D
1232{
1233private:
1234 double mfX;
1235 double mfY;
1236public:
1237 explicit Vector2D( const Point& rPoint ) : mfX( rPoint.X() ), mfY( rPoint.Y() ) {};
1238 double GetLength() const { return hypot( mfX, mfY ); }
1239 Vector2D& operator-=( const Vector2D& rVec ) { mfX -= rVec.mfX; mfY -= rVec.mfY; return *this; }
1240 double Scalar( const Vector2D& rVec ) const { return mfX * rVec.mfX + mfY * rVec.mfY ; }
1241 Vector2D& Normalize();
1242 bool IsPositive( Vector2D const & rVec ) const { return ( mfX * rVec.mfY - mfY * rVec.mfX ) >= 0.0; }
1243 bool IsNegative( Vector2D const & rVec ) const { return !IsPositive( rVec ); }
1244};
1245
1246}
1247
1248Vector2D& Vector2D::Normalize()
1249{
1250 double fLen = Scalar( *this );
1251
1252 if( ( fLen != 0.0 ) && ( fLen != 1.0 ) )
1253 {
1254 fLen = sqrt( fLen );
1255 if( fLen != 0.0 )
1256 {
1257 mfX /= fLen;
1258 mfY /= fLen;
1259 }
1260 }
1261
1262 return *this;
1263}
1264
1265void Polygon::ImplReduceEdges( tools::Polygon& rPoly, const double& rArea, sal_uInt16 nPercent )
1266{
1267 const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01;
1268 sal_uInt16 nNumNoChange = 0,
1269 nNumRuns = 0;
1270
1271 while( nNumNoChange < 2 )
1272 {
1273 sal_uInt16 nPntCnt = rPoly.GetSize(), nNewPos = 0;
1274 tools::Polygon aNewPoly( nPntCnt );
1275 bool bChangeInThisRun = false;
1276
1277 for( sal_uInt16 n = 0; n < nPntCnt; n++ )
1278 {
1279 bool bDeletePoint = false;
1280
1281 if( ( n + nNumRuns ) % 2 )
1282 {
1283 sal_uInt16 nIndPrev = !n ? nPntCnt - 1 : n - 1;
1284 sal_uInt16 nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1;
1285 sal_uInt16 nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1;
1286 sal_uInt16 nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1;
1287 Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= Vector2D(rPoly[ nIndPrevPrev ]);
1288 Vector2D aVec2( rPoly[ n ] ); aVec2 -= Vector2D(rPoly[ nIndPrev ]);
1289 Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= Vector2D(rPoly[ n ]);
1290 Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= Vector2D(rPoly[ nIndNext ]);
1291 double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength();
1292 double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength();
1293 double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() );
1294
1295 if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE0.0000001 ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE0.0000001 ) )
1296 bDeletePoint = true;
1297 else
1298 {
1299 Vector2D aVecB( rPoly[ nIndNext ] );
1300 aVecB -= Vector2D(rPoly[ nIndPrev ] );
1301 double fDistB = aVecB.GetLength();
1302 double fLenWithB = fDist2 + fDist3;
1303 double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0;
1304 double fTurnPrev = aVec1.Normalize().Scalar( aVec2 );
1305 double fTurnNext = aVec3.Scalar( aVec4.Normalize() );
1306 double fGradPrev, fGradB, fGradNext;
1307
1308 if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE0.0000001 ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE0.0000001 ) )
1309 fGradPrev = 0.0;
1310 else
1311 fGradPrev = basegfx::rad2deg(acos(fTurnPrev)) * (aVec1.IsNegative(aVec2) ? -1 : 1);
1312
1313 fGradB = basegfx::rad2deg(acos(fTurnB)) * (aVec2.IsNegative(aVec3) ? -1 : 1);
1314
1315 if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE0.0000001 ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE0.0000001 ) )
1316 fGradNext = 0.0;
1317 else
1318 fGradNext = basegfx::rad2deg(acos(fTurnNext)) * (aVec3.IsNegative(aVec4) ? -1 : 1);
1319
1320 if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) ||
1321 ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) )
1322 {
1323 if( ( fLenFact < ( FSQRT21.4142135623730950488016887242097 + SMALL_DVALUE0.0000001 ) ) &&
1324 ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound )
1325 {
1326 bDeletePoint = true;
1327 }
1328 }
1329 else
1330 {
1331 double fRelLen = 1.0 - sqrt( fDistB / rArea );
1332
1333 if( fRelLen < 0.0 )
1334 fRelLen = 0.0;
1335 else if( fRelLen > 1.0 )
1336 fRelLen = 1.0;
1337
1338 if( ( std::round( ( fLenFact - 1.0 ) * 1000000.0 ) < fBound ) &&
1339 ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) )
1340 {
1341 bDeletePoint = true;
1342 }
1343 }
1344 }
1345 }
1346
1347 if( !bDeletePoint )
1348 aNewPoly[ nNewPos++ ] = rPoly[ n ];
1349 else
1350 bChangeInThisRun = true;
1351 }
1352
1353 if( bChangeInThisRun && nNewPos )
1354 {
1355 aNewPoly.SetSize( nNewPos );
1356 rPoly = aNewPoly;
1357 nNumNoChange = 0;
1358 }
1359 else
1360 nNumNoChange++;
1361
1362 nNumRuns++;
1363 }
1364}
1365
1366void Polygon::Move( long nHorzMove, long nVertMove )
1367{
1368 // This check is required for DrawEngine
1369 if ( !nHorzMove && !nVertMove )
1370 return;
1371
1372 // Move points
1373 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1374 for ( sal_uInt16 i = 0; i < nCount; i++ )
1375 {
1376 Point& rPt = mpImplPolygon->mxPointAry[i];
1377 rPt.AdjustX(nHorzMove );
1378 rPt.AdjustY(nVertMove );
1379 }
1380}
1381
1382void Polygon::Translate(const Point& rTrans)
1383{
1384 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1385 mpImplPolygon->mxPointAry[ i ] += rTrans;
1386}
1387
1388void Polygon::Scale( double fScaleX, double fScaleY )
1389{
1390 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1391 {
1392 Point& rPnt = mpImplPolygon->mxPointAry[i];
1393 rPnt.setX( static_cast<long>( fScaleX * rPnt.X() ) );
1394 rPnt.setY( static_cast<long>( fScaleY * rPnt.Y() ) );
1395 }
1396}
1397
1398void Polygon::Rotate( const Point& rCenter, sal_uInt16 nAngle10 )
1399{
1400 nAngle10 %= 3600;
1401
1402 if( nAngle10 )
1403 {
1404 const double fAngle = F_PI1800(3.14159265358979323846/1800.0) * nAngle10;
1405 Rotate( rCenter, sin( fAngle ), cos( fAngle ) );
1406 }
1407}
1408
1409void Polygon::Rotate( const Point& rCenter, double fSin, double fCos )
1410{
1411 long nCenterX = rCenter.X();
1412 long nCenterY = rCenter.Y();
1413
1414 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1415 {
1416 Point& rPt = mpImplPolygon->mxPointAry[ i ];
1417
1418 const long nX = rPt.X() - nCenterX;
1419 const long nY = rPt.Y() - nCenterY;
1420 rPt.setX( FRound( fCos * nX + fSin * nY ) + nCenterX );
1421 rPt.setY( - FRound( fSin * nX - fCos * nY ) + nCenterY );
1422 }
1423}
1424
1425void Polygon::Clip( const tools::Rectangle& rRect )
1426{
1427 // #105251# Justify rect before edge filtering
1428 tools::Rectangle aJustifiedRect( rRect );
1429 aJustifiedRect.Justify();
1430
1431 sal_uInt16 nSourceSize = mpImplPolygon->mnPoints;
1432 ImplPolygonPointFilter aPolygon( nSourceSize );
1433 ImplEdgePointFilter aHorzFilter( EDGE_HORZ(4 | 1), aJustifiedRect.Left(), aJustifiedRect.Right(),
1434 aPolygon );
1435 ImplEdgePointFilter aVertFilter( EDGE_VERT(2 | 8), aJustifiedRect.Top(), aJustifiedRect.Bottom(),
1436 aHorzFilter );
1437
1438 for ( sal_uInt16 i = 0; i < nSourceSize; i++ )
1439 aVertFilter.Input( mpImplPolygon->mxPointAry[i] );
1440 if ( aVertFilter.IsPolygon() )
1441 aVertFilter.LastPoint();
1442 else
1443 aPolygon.LastPoint();
1444
1445 mpImplPolygon = ImplType(aPolygon.get());
1446}
1447
1448tools::Rectangle Polygon::GetBoundRect() const
1449{
1450 // Removing the assert. Bezier curves have the attribute that each single
1451 // curve segment defined by four points can not exit the four-point polygon
1452 // defined by that points. This allows to say that the curve segment can also
1453 // never leave the Range of its defining points.
1454 // The result is that Polygon::GetBoundRect() may not create the minimal
1455 // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes),
1456 // but will always create a valid BoundRect, at least as long as this method
1457 // 'blindly' travels over all points, including control points.
1458
1459 // DBG_ASSERT( !mpImplPolygon->mxFlagAry.get(), "GetBoundRect could fail with beziers!" );
1460
1461 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1462 if( ! nCount )
1463 return tools::Rectangle();
1464
1465 long nXMin, nXMax, nYMin, nYMax;
1466
1467 const Point& pFirstPt = mpImplPolygon->mxPointAry[0];
1468 nXMin = nXMax = pFirstPt.X();
1469 nYMin = nYMax = pFirstPt.Y();
1470
1471 for ( sal_uInt16 i = 0; i < nCount; i++ )
1472 {
1473 const Point& rPt = mpImplPolygon->mxPointAry[i];
1474
1475 if (rPt.X() < nXMin)
1476 nXMin = rPt.X();
1477 if (rPt.X() > nXMax)
1478 nXMax = rPt.X();
1479 if (rPt.Y() < nYMin)
1480 nYMin = rPt.Y();
1481 if (rPt.Y() > nYMax)
1482 nYMax = rPt.Y();
1483 }
1484
1485 return tools::Rectangle( nXMin, nYMin, nXMax, nYMax );
1486}
1487
1488bool Polygon::IsInside( const Point& rPoint ) const
1489{
1490 DBG_ASSERT( !mpImplPolygon->mxFlagAry, "IsInside could fail with beziers!" )do { if (true && (!(!mpImplPolygon->mxFlagAry))) {
sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1490" ": "), "%s", "IsInside could fail with beziers!")
; } } while (false)
;
1491
1492 const tools::Rectangle aBound( GetBoundRect() );
1493 const Line aLine( rPoint, Point( aBound.Right() + 100, rPoint.Y() ) );
1494 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1495 sal_uInt16 nPCounter = 0;
1496
1497 if ( ( nCount > 2 ) && aBound.IsInside( rPoint ) )
1498 {
1499 Point aPt1( mpImplPolygon->mxPointAry[ 0 ] );
1500 Point aIntersection;
1501 Point aLastIntersection;
1502
1503 while ( ( aPt1 == mpImplPolygon->mxPointAry[ nCount - 1 ] ) && ( nCount > 3 ) )
1504 nCount--;
1505
1506 for ( sal_uInt16 i = 1; i <= nCount; i++ )
1507 {
1508 const Point& rPt2 = mpImplPolygon->mxPointAry[ ( i < nCount ) ? i : 0 ];
1509
1510 if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) )
1511 {
1512 // This avoids insertion of double intersections
1513 if ( nPCounter )
1514 {
1515 if ( aIntersection != aLastIntersection )
1516 {
1517 aLastIntersection = aIntersection;
1518 nPCounter++;
1519 }
1520 }
1521 else
1522 {
1523 aLastIntersection = aIntersection;
1524 nPCounter++;
1525 }
1526 }
1527
1528 aPt1 = rPt2;
1529 }
1530 }
1531
1532 // is inside, if number of intersection points is odd
1533 return ( ( nPCounter & 1 ) == 1 );
1534}
1535
1536void Polygon::Insert( sal_uInt16 nPos, const Point& rPt )
1537{
1538 if( nPos >= mpImplPolygon->mnPoints )
1539 nPos = mpImplPolygon->mnPoints;
1540
1541 if (mpImplPolygon->ImplSplit(nPos, 1))
1542 mpImplPolygon->mxPointAry[ nPos ] = rPt;
1543}
1544
1545void Polygon::Insert( sal_uInt16 nPos, const tools::Polygon& rPoly )
1546{
1547 const sal_uInt16 nInsertCount = rPoly.mpImplPolygon->mnPoints;
1548
1549 if( nInsertCount )
1550 {
1551 if( nPos >= mpImplPolygon->mnPoints )
1552 nPos = mpImplPolygon->mnPoints;
1553
1554 if (rPoly.mpImplPolygon->mxFlagAry)
1555 mpImplPolygon->ImplCreateFlagArray();
1556
1557 mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon.get() );
1558 }
1559}
1560
1561Point& Polygon::operator[]( sal_uInt16 nPos )
1562{
1563 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, "Polygon::[]: nPos >= nPoints" )do { if (true && (!(nPos < mpImplPolygon->mnPoints
))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.tools"
), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1563" ": "), "%s", "Polygon::[]: nPos >= nPoints"); }
} while (false)
;
1564
1565 return mpImplPolygon->mxPointAry[nPos];
1566}
1567
1568tools::Polygon& Polygon::operator=( const tools::Polygon& rPoly )
1569{
1570 mpImplPolygon = rPoly.mpImplPolygon;
1571 return *this;
1572}
1573
1574tools::Polygon& Polygon::operator=( tools::Polygon&& rPoly ) noexcept
1575{
1576 mpImplPolygon = std::move(rPoly.mpImplPolygon);
1577 return *this;
1578}
1579
1580bool Polygon::operator==( const tools::Polygon& rPoly ) const
1581{
1582 return (mpImplPolygon == rPoly.mpImplPolygon);
1583}
1584
1585bool Polygon::IsEqual( const tools::Polygon& rPoly ) const
1586{
1587 bool bIsEqual = true;
1588 sal_uInt16 i;
1589 if ( GetSize() != rPoly.GetSize() )
1590 bIsEqual = false;
1591 else
1592 {
1593 for ( i = 0; i < GetSize(); i++ )
1594 {
1595 if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) ||
1596 ( GetFlags( i ) != rPoly.GetFlags( i ) ) )
1597 {
1598 bIsEqual = false;
1599 break;
1600 }
1601 }
1602 }
1603 return bIsEqual;
1604}
1605
1606SvStream& ReadPolygon( SvStream& rIStream, tools::Polygon& rPoly )
1607{
1608 sal_uInt16 i;
1609 sal_uInt16 nPoints(0);
1610
1611 // read all points and create array
1612 rIStream.ReadUInt16( nPoints );
1613
1614 const size_t nMaxRecordsPossible = rIStream.remainingSize() / (2 * sizeof(sal_Int32));
1615 if (nPoints > nMaxRecordsPossible)
1616 {
1617 SAL_WARN("tools", "Polygon claims " << nPoints << " records, but only " << nMaxRecordsPossible << " possible")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "tools")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Polygon claims " << nPoints << " records, but only "
<< nMaxRecordsPossible << " possible") == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1617" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Polygon claims " << nPoints <<
" records, but only " << nMaxRecordsPossible << " possible"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Polygon claims " << nPoints << " records, but only "
<< nMaxRecordsPossible << " possible"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1617" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Polygon claims " << nPoints << " records, but only "
<< nMaxRecordsPossible << " possible") == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1617" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Polygon claims " << nPoints <<
" records, but only " << nMaxRecordsPossible << " possible"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Polygon claims " << nPoints << " records, but only "
<< nMaxRecordsPossible << " possible"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("tools"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1617" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1618 nPoints = nMaxRecordsPossible;
1619 }
1620
1621 rPoly.mpImplPolygon->ImplSetSize( nPoints, false );
1622
1623 // Determine whether we need to write through operators
1624#if (SAL_TYPES_SIZEOFLONG8) == 4
1625#ifdef OSL_BIGENDIAN
1626 if ( rIStream.GetEndian() == SvStreamEndian::BIG )
1627#else
1628 if ( rIStream.GetEndian() == SvStreamEndian::LITTLE )
1629#endif
1630 rIStream.ReadBytes(rPoly.mpImplPolygon->mxPointAry.get(), nPoints*sizeof(Point));
1631 else
1632#endif
1633 {
1634 for( i = 0; i < nPoints; i++ )
1635 {
1636 sal_Int32 nTmpX(0), nTmpY(0);
1637 rIStream.ReadInt32( nTmpX ).ReadInt32( nTmpY );
1638 rPoly.mpImplPolygon->mxPointAry[i].setX( nTmpX );
1639 rPoly.mpImplPolygon->mxPointAry[i].setY( nTmpY );
1640 }
1641 }
1642
1643 return rIStream;
1644}
1645
1646SvStream& WritePolygon( SvStream& rOStream, const tools::Polygon& rPoly )
1647{
1648 sal_uInt16 i;
1649 sal_uInt16 nPoints = rPoly.GetSize();
1650
1651 // Write number of points
1652 rOStream.WriteUInt16( nPoints );
1653
1654 // Determine whether we need to write through operators
1655#if (SAL_TYPES_SIZEOFLONG8) == 4
1656#ifdef OSL_BIGENDIAN
1657 if ( rOStream.GetEndian() == SvStreamEndian::BIG )
1658#else
1659 if ( rOStream.GetEndian() == SvStreamEndian::LITTLE )
1660#endif
1661 {
1662 if ( nPoints )
1663 rOStream.WriteBytes(rPoly.mpImplPolygon->mxPointAry.get(), nPoints*sizeof(Point));
1664 }
1665 else
1666#endif
1667 {
1668 for( i = 0; i < nPoints; i++ )
1669 {
1670 rOStream.WriteInt32( rPoly.mpImplPolygon->mxPointAry[i].X() )
1671 .WriteInt32( rPoly.mpImplPolygon->mxPointAry[i].Y() );
1672 }
1673 }
1674
1675 return rOStream;
1676}
1677
1678void Polygon::ImplRead( SvStream& rIStream )
1679{
1680 sal_uInt8 bHasPolyFlags(0);
1681
1682 ReadPolygon( rIStream, *this );
1683 rIStream.ReadUChar( bHasPolyFlags );
1684
1685 if ( bHasPolyFlags )
1686 {
1687 mpImplPolygon->mxFlagAry.reset(new PolyFlags[mpImplPolygon->mnPoints]);
1688 rIStream.ReadBytes(mpImplPolygon->mxFlagAry.get(), mpImplPolygon->mnPoints);
1689 }
1690}
1691
1692void Polygon::Read( SvStream& rIStream )
1693{
1694 VersionCompat aCompat( rIStream, StreamMode::READ );
1695
1696 ImplRead( rIStream );
1697}
1698
1699void Polygon::ImplWrite( SvStream& rOStream ) const
1700{
1701 bool bHasPolyFlags(mpImplPolygon->mxFlagAry);
1702 WritePolygon( rOStream, *this );
1703 rOStream.WriteBool(bHasPolyFlags);
1704
1705 if ( bHasPolyFlags )
1706 rOStream.WriteBytes(mpImplPolygon->mxFlagAry.get(), mpImplPolygon->mnPoints);
1707}
1708
1709void Polygon::Write( SvStream& rOStream ) const
1710{
1711 VersionCompat aCompat( rOStream, StreamMode::WRITE, 1 );
1712
1713 ImplWrite( rOStream );
1714}
1715
1716// #i74631#/#i115917# numerical correction method for B2DPolygon
1717static void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, PolyFlags nCFlag)
1718{
1719 const sal_uInt32 nPointCount(roPolygon.count());
1720 OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)")do { if (true && (!(nIndex < nPointCount))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1720" ": "), "%s", "impCorrectContinuity: index access out of range (!)"
); } } while (false)
;
1721
1722 if(nIndex >= nPointCount || (PolyFlags::Smooth != nCFlag && PolyFlags::Symmetric != nCFlag))
1723 return;
1724
1725 if(!roPolygon.isPrevControlPointUsed(nIndex) || !roPolygon.isNextControlPointUsed(nIndex))
1726 return;
1727
1728 // #i115917# Patch from osnola (modified, thanks for showing the problem)
1729
1730 // The correction is needed because an integer polygon with control points
1731 // is converted to double precision. When C1 or C2 is used the involved vectors
1732 // may not have the same directions/lengths since these come from integer coordinates
1733 // and may have been snapped to different nearest integer coordinates. The snap error
1734 // is in the range of +-1 in y and y, thus 0.0 <= error <= sqrt(2.0). Nonetheless,
1735 // it needs to be corrected to be able to detect the continuity in this points
1736 // correctly.
1737
1738 // We only have the integer data here (already in double precision form, but no mantissa
1739 // used), so the best correction is to use:
1740
1741 // for C1: The longest vector since it potentially has best preserved the original vector.
1742 // Even better the sum of the vectors, weighted by their length. This gives the
1743 // normal vector addition to get the vector itself, lengths need to be preserved.
1744 // for C2: The mediated vector(s) since both should be the same, but mirrored
1745
1746 // extract the point and vectors
1747 const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex));
1748 const basegfx::B2DVector aNext(roPolygon.getNextControlPoint(nIndex) - aPoint);
1749 const basegfx::B2DVector aPrev(aPoint - roPolygon.getPrevControlPoint(nIndex));
1750
1751 // calculate common direction vector, normalize
1752 const basegfx::B2DVector aDirection(aNext + aPrev);
1753 const double fDirectionLen = aDirection.getLength();
1754 if (fDirectionLen == 0.0)
1755 return;
1756
1757 if (PolyFlags::Smooth == nCFlag)
1758 {
1759 // C1: apply common direction vector, preserve individual lengths
1760 const double fInvDirectionLen(1.0 / fDirectionLen);
1761 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + (aDirection * (aNext.getLength() * fInvDirectionLen))));
1762 roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - (aDirection * (aPrev.getLength() * fInvDirectionLen))));
1763 }
1764 else // PolyFlags::Symmetric
1765 {
1766 // C2: get mediated length. Taking half of the unnormalized direction would be
1767 // an approximation, but not correct.
1768 const double fMedLength((aNext.getLength() + aPrev.getLength()) * (0.5 / fDirectionLen));
1769 const basegfx::B2DVector aScaledDirection(aDirection * fMedLength);
1770
1771 // Bring Direction to correct length and apply
1772 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aScaledDirection));
1773 roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - aScaledDirection));
1774 }
1775}
1776
1777// convert to basegfx::B2DPolygon and return
1778basegfx::B2DPolygon Polygon::getB2DPolygon() const
1779{
1780 basegfx::B2DPolygon aRetval;
1781 const sal_uInt16 nCount(mpImplPolygon->mnPoints);
1782
1783 if (nCount)
1784 {
1785 if (mpImplPolygon->mxFlagAry)
1786 {
1787 // handling for curves. Add start point
1788 const Point aStartPoint(mpImplPolygon->mxPointAry[0]);
1789 PolyFlags nPointFlag(mpImplPolygon->mxFlagAry[0]);
1790 aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y()));
1791 Point aControlA, aControlB;
1792
1793 for(sal_uInt16 a(1); a < nCount;)
1794 {
1795 bool bControlA(false);
1796 bool bControlB(false);
1797
1798 if(PolyFlags::Control == mpImplPolygon->mxFlagAry[a])
1799 {
1800 aControlA = mpImplPolygon->mxPointAry[a++];
1801 bControlA = true;
1802 }
1803
1804 if(a < nCount && PolyFlags::Control == mpImplPolygon->mxFlagAry[a])
1805 {
1806 aControlB = mpImplPolygon->mxPointAry[a++];
1807 bControlB = true;
1808 }
1809
1810 // assert invalid polygons
1811 OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)")do { if (true && (!(bControlA == bControlB))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/tools/source/generic/poly.cxx"
":" "1811" ": "), "%s", "Polygon::getB2DPolygon: Invalid source polygon (!)"
); } } while (false)
;
1812
1813 if(a < nCount)
1814 {
1815 const Point aEndPoint(mpImplPolygon->mxPointAry[a]);
1816
1817 if(bControlA)
1818 {
1819 // bezier edge, add
1820 aRetval.appendBezierSegment(
1821 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
1822 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
1823 basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y()));
1824
1825 impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag);
1826 }
1827 else
1828 {
1829 // no bezier edge, add end point
1830 aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y()));
1831 }
1832
1833 nPointFlag = mpImplPolygon->mxFlagAry[a++];
1834 }
1835 }
1836
1837 // if exist, remove double first/last points, set closed and correct control points
1838 basegfx::utils::checkClosed(aRetval);
1839
1840 if(aRetval.isClosed())
1841 {
1842 // closeWithGeometryChange did really close, so last point(s) were removed.
1843 // Correct the continuity in the changed point
1844 impCorrectContinuity(aRetval, 0, mpImplPolygon->mxFlagAry[0]);
1845 }
1846 }
1847 else
1848 {
1849 // extra handling for non-curves (most-used case) for speedup
1850 for(sal_uInt16 a(0); a < nCount; a++)
1851 {
1852 // get point and add
1853 const Point aPoint(mpImplPolygon->mxPointAry[a]);
1854 aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y()));
1855 }
1856
1857 // set closed flag
1858 basegfx::utils::checkClosed(aRetval);
1859 }
1860 }
1861
1862 return aRetval;
1863}
1864
1865Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) : mpImplPolygon(ImplPolygon(rPolygon))
1866{
1867}
1868
1869} // namespace tools
1870
1871/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/o3tl/cow_wrapper.hxx

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#ifndef INCLUDED_O3TL_COW_WRAPPER_HXX
21#define INCLUDED_O3TL_COW_WRAPPER_HXX
22
23#include <memory>
24#include <osl/interlck.h>
25
26#include <utility>
27#include <cstddef>
28
29namespace o3tl
30{
31 /** Thread-unsafe refcounting
32
33 This is the default locking policy for cow_wrapper. No
34 locking/guarding against concurrent access is performed
35 whatsoever.
36 */
37 struct UnsafeRefCountingPolicy
38 {
39 typedef std::size_t ref_count_t;
40 static void incrementCount( ref_count_t& rCount ) { ++rCount; }
41 static bool decrementCount( ref_count_t& rCount ) { return --rCount != 0; }
42 };
43
44 /** Thread-safe refcounting
45
46 Use this to have the cow_wrapper refcounting mechanisms employ
47 the thread-safe oslInterlockedCount .
48 */
49 struct ThreadSafeRefCountingPolicy
50 {
51 typedef oslInterlockedCount ref_count_t;
52 static void incrementCount( ref_count_t& rCount ) { osl_atomic_increment(&rCount)__sync_add_and_fetch((&rCount), 1); }
53 static bool decrementCount( ref_count_t& rCount )
54 {
55 if( rCount == 1 ) // caller is already the only/last reference
56 return false;
57 else
58 return osl_atomic_decrement(&rCount)__sync_sub_and_fetch((&rCount), 1) != 0;
59 }
60 };
61
62 /** Copy-on-write wrapper.
63
64 This template provides copy-on-write semantics for the wrapped
65 type: when copying, the operation is performed shallow,
66 i.e. different cow_wrapper objects share the same underlying
67 instance. Only when accessing the underlying object via
68 non-const methods, a unique copy is provided.
69
70 The type parameter <code>T</code> must satisfy the following
71 requirements: it must be default-constructible, copyable (it
72 need not be assignable), and be of non-reference type. Note
73 that, despite the fact that this template provides access to
74 the wrapped type via pointer-like methods
75 (<code>operator->()</code> and <code>operator*()</code>), it does
76 <em>not</em> work like e.g. the std smart pointer wrappers
77 (shared_ptr, unique_ptr, etc.). Internally, the cow_wrapper
78 holds a by-value instance of the wrapped object. This is to
79 avoid one additional heap allocation, and providing access via
80 <code>operator->()</code>/<code>operator*()</code> is because
81 <code>operator.()</code> cannot be overridden.
82
83 Regarding thread safety: this wrapper is <em>not</em>
84 thread-safe per se, because cow_wrapper has no way of
85 synchronizing the potentially many different cow_wrapper
86 instances, that reference a single shared value_type
87 instance. That said, when passing
88 <code>ThreadSafeRefCountingPolicy</code> as the
89 <code>MTPolicy</code> parameter, accessing a thread-safe
90 pointee through multiple cow_wrapper instances might be
91 thread-safe, if the individual pointee methods are
92 thread-safe, <em>including</em> pointee's copy
93 constructor. Any wrapped object that needs external
94 synchronisation (e.g. via an external mutex, which arbitrates
95 access to object methods, and can be held across multiple
96 object method calls) cannot easily be dealt with in a
97 thread-safe way, because, as noted, objects are shared behind
98 the client's back.
99
100 @attention if one wants to use the pimpl idiom together with
101 cow_wrapper (i.e. put an opaque type into the cow_wrapper),
102 then <em>all<em> methods in the surrounding class needs to be
103 non-inline (<em>including</em> destructor, copy constructor
104 and assignment operator).
105
106 @example
107 <pre>
108class cow_wrapper_client_impl;
109
110class cow_wrapper_client
111{
112public:
113 cow_wrapper_client();
114 cow_wrapper_client( const cow_wrapper_client& );
115 cow_wrapper_client( cow_wrapper_client&& );
116 ~cow_wrapper_client();
117
118 cow_wrapper_client& operator=( const cow_wrapper_client& );
119 cow_wrapper_client& operator=( cow_wrapper_client&& );
120
121 void modify( int nVal );
122 int queryUnmodified() const;
123
124private:
125 o3tl::cow_wrapper< cow_wrapper_client_impl > maImpl;
126};
127 </pre>
128 and the implementation file would look like this:
129 <pre>
130class cow_wrapper_client_impl
131{
132public:
133 void setValue( int nVal ) { mnValue = nVal; }
134 int getValue() const { return mnValue; }
135
136private:
137 int mnValue;
138}
139
140cow_wrapper_client::cow_wrapper_client() :
141 maImpl()
142{
143}
144cow_wrapper_client::cow_wrapper_client( const cow_wrapper_client& rSrc ) :
145 maImpl( rSrc.maImpl )
146{
147}
148cow_wrapper_client::cow_wrapper_client( cow_wrapper_client& rSrc ) :
149 maImpl( std::move( rSrc.maImpl ) )
150{
151}
152cow_wrapper_client::~cow_wrapper_client()
153{
154}
155cow_wrapper_client& cow_wrapper_client::operator=( const cow_wrapper_client& rSrc )
156{
157 maImpl = rSrc.maImpl;
158 return *this;
159}
160cow_wrapper_client& cow_wrapper_client::operator=( cow_wrapper_client&& rSrc )
161{
162 maImpl = std::move( rSrc.maImpl );
163 return *this;
164}
165void cow_wrapper_client::modify( int nVal )
166{
167 maImpl->setValue( nVal );
168}
169int cow_wrapper_client::queryUnmodified() const
170{
171 return maImpl->getValue();
172}
173 </pre>
174 */
175 template<typename T, class MTPolicy=UnsafeRefCountingPolicy> class cow_wrapper
176 {
177 /** shared value object - gets cloned before cow_wrapper hands
178 out a non-const reference to it
179 */
180 struct impl_t
181 {
182 impl_t(const impl_t&) = delete;
183 impl_t& operator=(const impl_t&) = delete;
184
185 impl_t() :
186 m_value(),
187 m_ref_count(1)
188 {
189 }
190
191 explicit impl_t( const T& v ) :
192 m_value(v),
193 m_ref_count(1)
194 {
195 }
196
197 explicit impl_t( T&& v ) :
198 m_value(std::move(v)),
199 m_ref_count(1)
200 {
201 }
202
203 T m_value;
204 typename MTPolicy::ref_count_t m_ref_count;
205 };
206
207 void release()
208 {
209 if( m_pimpl
18.1
Field 'm_pimpl' is non-null
18.1
Field 'm_pimpl' is non-null
&& !MTPolicy::decrementCount(m_pimpl->m_ref_count) )
19
Taking true branch
210 {
211 delete m_pimpl;
20
Memory is released
212 m_pimpl = nullptr;
213 }
214 }
215
216 public:
217 typedef T value_type;
218 typedef T* pointer;
219 typedef const T* const_pointer;
220 typedef MTPolicy mt_policy;
221
222 /** Default-construct wrapped type instance
223 */
224 cow_wrapper() :
225 m_pimpl( new impl_t() )
226 {
227 }
228
229 /** Copy-construct wrapped type instance from given object
230 */
231 explicit cow_wrapper( const value_type& r ) :
232 m_pimpl( new impl_t(r) )
233 {
234 }
235
236 /** Move-construct wrapped type instance from given object
237 */
238 explicit cow_wrapper( value_type&& r ) :
239 m_pimpl( new impl_t(std::move(r)) )
240 {
241 }
242
243 /** Shallow-copy given cow_wrapper
244 */
245 explicit cow_wrapper( const cow_wrapper& rSrc ) : // nothrow
246 m_pimpl( rSrc.m_pimpl )
247 {
248 MTPolicy::incrementCount( m_pimpl->m_ref_count );
249 }
250
251 /** Move-construct and steal rSrc shared resource
252 */
253 explicit cow_wrapper( cow_wrapper&& rSrc ) noexcept :
254 m_pimpl( rSrc.m_pimpl )
255 {
256 rSrc.m_pimpl = nullptr;
257 }
258
259 ~cow_wrapper() // nothrow, if ~T does not throw
260 {
261 release();
18
Calling 'cow_wrapper::release'
21
Returning; memory was released
262 }
263
264 /// now sharing rSrc cow_wrapper instance with us
265 cow_wrapper& operator=( const cow_wrapper& rSrc ) // nothrow
266 {
267 // this already guards against self-assignment
268 MTPolicy::incrementCount( rSrc.m_pimpl->m_ref_count );
269
270 release();
271 m_pimpl = rSrc.m_pimpl;
272
273 return *this;
274 }
275
276 /// stealing rSrc's resource
277 cow_wrapper& operator=(cow_wrapper&& rSrc) noexcept
278 {
279 // self-movement guts ourself, see also 17.6.4.9
280 release();
281 m_pimpl = rSrc.m_pimpl;
282
283 rSrc.m_pimpl = nullptr;
284
285 return *this;
286 }
287
288 /// unshare with any other cow_wrapper instance
289 value_type& make_unique()
290 {
291 if( m_pimpl->m_ref_count > 1 )
26
Use of memory after it is freed
292 {
293 impl_t* pimpl = new impl_t(m_pimpl->m_value);
294 release();
295 m_pimpl = pimpl;
296 }
297
298 return m_pimpl->m_value;
299 }
300
301 /// true, if not shared with any other cow_wrapper instance
302 bool is_unique() const // nothrow
303 {
304 return !m_pimpl || m_pimpl->m_ref_count == 1;
305 }
306
307 /// return number of shared instances (1 for unique object)
308 typename MTPolicy::ref_count_t use_count() const // nothrow
309 {
310 return m_pimpl ? m_pimpl->m_ref_count : 0;
311 }
312
313 void swap(cow_wrapper& r) // never throws
314 {
315 std::swap(m_pimpl, r.m_pimpl);
316 }
317
318 pointer operator->() { return &make_unique(); }
25
Calling 'cow_wrapper::make_unique'
319 value_type& operator*() { return make_unique(); }
320 const_pointer operator->() const { return &m_pimpl->m_value; }
321 const value_type& operator*() const { return m_pimpl->m_value; }
322
323 pointer get() { return &make_unique(); }
324 const_pointer get() const { return &m_pimpl->m_value; }
325
326 /// true, if both cow_wrapper internally share the same object
327 bool same_object( const cow_wrapper& rOther ) const
328 {
329 return rOther.m_pimpl == m_pimpl;
330 }
331
332 private:
333 impl_t* m_pimpl;
334 };
335
336
337 template<class T, class P> inline bool operator==( const cow_wrapper<T,P>& a,
338 const cow_wrapper<T,P>& b )
339 {
340 return a.same_object(b) || *a == *b;
341 }
342
343 template<class T, class P> inline bool operator!=( const cow_wrapper<T,P>& a,
344 const cow_wrapper<T,P>& b )
345 {
346 return !a.same_object(b) && *a != *b;
347 }
348
349 template<class A, class B, class P> inline bool operator<( const cow_wrapper<A,P>& a,
350 const cow_wrapper<B,P>& b )
351 {
352 return *a < *b;
353 }
354
355 template<class T, class P> inline void swap( cow_wrapper<T,P>& a,
356 cow_wrapper<T,P>& b )
357 {
358 a.swap(b);
359 }
360
361}
362
363#endif /* INCLUDED_O3TL_COW_WRAPPER_HXX */
364
365/* vim:set shiftwidth=4 softtabstop=4 expandtab: */