Line data Source code
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 <drawinglayer/geometry/viewinformation2d.hxx>
21 : #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
22 : #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
23 : #include <basegfx/polygon/b2dpolygon.hxx>
24 : #include <basegfx/polygon/b2dpolygonclipper.hxx>
25 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
26 : #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
27 : #include <svtools/borderhelper.hxx>
28 : #include <numeric>
29 : #include <algorithm>
30 :
31 : //////////////////////////////////////////////////////////////////////////////
32 :
33 : namespace drawinglayer
34 : {
35 : // fdo#49438: heuristic pseudo hack
36 6142 : static bool lcl_UseHairline(double const fW,
37 : basegfx::B2DPoint const& rStart, basegfx::B2DPoint const& rEnd,
38 : geometry::ViewInformation2D const& rViewInformation)
39 : {
40 6142 : basegfx::B2DTuple scale;
41 6142 : basegfx::B2DTuple translation;
42 : double fRotation;
43 : double fShear;
44 6142 : rViewInformation.getObjectToViewTransformation().decompose(
45 6142 : scale, translation, fRotation, fShear);
46 : double const fScale(
47 12284 : (rEnd.getX() - rStart.getX() > rEnd.getY() - rStart.getY())
48 12284 : ? scale.getY() : scale.getX());
49 6142 : return (fW * fScale < 0.51);
50 : }
51 :
52 5534 : static double lcl_GetCorrectedWidth(double const fW,
53 : basegfx::B2DPoint const& rStart, basegfx::B2DPoint const& rEnd,
54 : geometry::ViewInformation2D const& rViewInformation)
55 : {
56 5534 : return (lcl_UseHairline(fW, rStart, rEnd, rViewInformation)) ? 0.0 : fW;
57 : }
58 :
59 : namespace primitive2d
60 : {
61 1620 : double BorderLinePrimitive2D::getWidth(
62 : geometry::ViewInformation2D const& rViewInformation) const
63 : {
64 1620 : return lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(),
65 1620 : rViewInformation)
66 1620 : + lcl_GetCorrectedWidth(mfDistance, getStart(), getEnd(),
67 1620 : rViewInformation)
68 1620 : + lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(),
69 3240 : rViewInformation);
70 : }
71 :
72 540 : basegfx::B2DPolyPolygon BorderLinePrimitive2D::getClipPolygon(
73 : geometry::ViewInformation2D const& rViewInformation) const
74 : {
75 540 : basegfx::B2DPolygon clipPolygon;
76 :
77 : // Get the vectors
78 540 : basegfx::B2DVector aVector( getEnd() - getStart() );
79 540 : aVector.normalize();
80 540 : const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
81 :
82 : // Get the points
83 540 : const double fWidth(getWidth(rViewInformation));
84 : const basegfx::B2DVector aLeftOff(
85 540 : aPerpendicular * (-0.5 * std::max(fWidth, 1.0)));
86 : const basegfx::B2DVector aRightOff(
87 540 : aPerpendicular * (0.5 * std::max(fWidth, 1.0)));
88 :
89 540 : const basegfx::B2DVector aSLVector( aLeftOff - ( getExtendLeftStart() * aVector ) );
90 540 : clipPolygon.append( basegfx::B2DPoint( getStart() + aSLVector * 2.0 ) );
91 :
92 540 : clipPolygon.append( getStart( ) );
93 :
94 540 : const basegfx::B2DVector aSRVector( aRightOff - ( getExtendRightStart() * aVector ) );
95 540 : clipPolygon.append( basegfx::B2DPoint( getStart() + aSRVector * 2.0 ) );
96 :
97 540 : const basegfx::B2DVector aERVector( aRightOff + ( getExtendRightEnd() * aVector ) );
98 540 : clipPolygon.append( basegfx::B2DPoint( getEnd() + aERVector * 2.0 ) );
99 :
100 540 : clipPolygon.append( getEnd( ) );
101 :
102 540 : const basegfx::B2DVector aELVector( aLeftOff + ( getExtendLeftEnd() * aVector ) );
103 540 : clipPolygon.append( basegfx::B2DPoint( getEnd() + aELVector * 2.0 ) );
104 :
105 540 : clipPolygon.setClosed( true );
106 :
107 540 : return basegfx::B2DPolyPolygon( clipPolygon );
108 : }
109 :
110 542 : Primitive2DSequence BorderLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
111 : {
112 542 : Primitive2DSequence xRetval;
113 :
114 542 : if(!getStart().equal(getEnd()) && ( isInsideUsed() || isOutsideUsed() ) )
115 : {
116 : // get data and vectors
117 540 : const double fWidth(getWidth(rViewInformation));
118 540 : basegfx::B2DVector aVector(getEnd() - getStart());
119 540 : aVector.normalize();
120 540 : const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
121 :
122 : const basegfx::B2DPolyPolygon& aClipRegion =
123 540 : getClipPolygon(rViewInformation);
124 :
125 540 : if(isOutsideUsed() && isInsideUsed())
126 : {
127 68 : const double fExt = getWidth(rViewInformation); // Extend a lot: it'll be clipped after
128 :
129 : // both used, double line definition. Create left and right offset
130 68 : xRetval.realloc(2);
131 68 : sal_uInt32 nInsert(0);
132 :
133 68 : basegfx::B2DPolygon aGap;
134 :
135 : {
136 : // create geometry for left
137 68 : const basegfx::B2DVector aLeftOff(aPerpendicular * (0.5 * (lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(), rViewInformation) - fWidth + 1)));
138 68 : const basegfx::B2DPoint aTmpStart(getStart() + aLeftOff - ( fExt * aVector));
139 68 : const basegfx::B2DPoint aTmpEnd(getEnd() + aLeftOff + ( fExt * aVector));
140 68 : basegfx::B2DPolygon aLeft;
141 :
142 136 : if (lcl_UseHairline(mfLeftWidth, getStart(), getEnd(),
143 68 : rViewInformation))
144 : {
145 : // create hairline primitive
146 34 : aLeft.append(aTmpStart);
147 34 : aLeft.append(aTmpEnd);
148 :
149 : basegfx::B2DPolyPolygon const aClipped =
150 : basegfx::tools::clipPolygonOnPolyPolygon(
151 34 : aLeft, aClipRegion, true, true);
152 :
153 34 : xRetval[nInsert++] =
154 : new PolyPolygonHairlinePrimitive2D(
155 : aClipped,
156 68 : getRGBColorLeft());
157 :
158 34 : aGap.append( getStart() - getExtendLeftStart() * aVector );
159 34 : aGap.append( getEnd() + getExtendLeftEnd() * aVector );
160 : }
161 : else
162 : {
163 : // create filled polygon primitive. Already tried to create thick lines
164 : // with the correct LineWidth, but this leads to problems when no AA
165 : // is available and fat line special case reductions between 0.5 < x < 2.5 line widths
166 : // are executed due to the FilledPolygon-do-not-paint-their-bottom-and-right-lines.
167 34 : const basegfx::B2DVector aLineWidthOffset((lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(), rViewInformation) * 0.5) * aPerpendicular);
168 :
169 34 : aLeft.append(aTmpStart + aLineWidthOffset);
170 34 : aLeft.append(aTmpEnd + aLineWidthOffset);
171 34 : aLeft.append(aTmpEnd - aLineWidthOffset);
172 34 : aLeft.append(aTmpStart - aLineWidthOffset);
173 34 : aLeft.setClosed(true);
174 :
175 : basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
176 34 : aLeft, aClipRegion, true, false );
177 :
178 34 : aGap.append( aTmpStart + aLineWidthOffset );
179 34 : aGap.append( aTmpEnd + aLineWidthOffset );
180 :
181 34 : xRetval[nInsert++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
182 68 : aClipped, getRGBColorLeft()));
183 68 : }
184 : }
185 :
186 : {
187 : // create geometry for right
188 68 : const basegfx::B2DVector aRightOff(aPerpendicular * (0.5 * (fWidth - lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(), rViewInformation) + 1)));
189 68 : const basegfx::B2DPoint aTmpStart(getStart() + aRightOff - ( fExt * aVector));
190 68 : const basegfx::B2DPoint aTmpEnd(getEnd() + aRightOff + ( fExt * aVector));
191 68 : basegfx::B2DPolygon aRight;
192 :
193 136 : if (lcl_UseHairline(mfRightWidth, getStart(), getEnd(),
194 68 : rViewInformation))
195 : {
196 : // create hairline primitive
197 36 : aRight.append(aTmpStart);
198 36 : aRight.append(aTmpEnd);
199 :
200 : basegfx::B2DPolyPolygon const aClipped =
201 : basegfx::tools::clipPolygonOnPolyPolygon(
202 36 : aRight, aClipRegion, true, true);
203 :
204 36 : xRetval[nInsert++] =
205 : new PolyPolygonHairlinePrimitive2D(
206 : aClipped,
207 72 : getRGBColorRight());
208 :
209 36 : aGap.append( getStart() - getExtendRightStart() * aVector );
210 36 : aGap.append( getEnd() + getExtendRightEnd() * aVector );
211 : }
212 : else
213 : {
214 : // create filled polygon primitive
215 32 : const basegfx::B2DVector aLineWidthOffset((lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(), rViewInformation) * 0.5) * aPerpendicular);
216 :
217 32 : aRight.append(aTmpStart + aLineWidthOffset);
218 32 : aRight.append(aTmpEnd + aLineWidthOffset);
219 32 : aRight.append(aTmpEnd - aLineWidthOffset);
220 32 : aRight.append(aTmpStart - aLineWidthOffset);
221 32 : aRight.setClosed(true);
222 :
223 : basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
224 32 : aRight, aClipRegion, true, false );
225 :
226 32 : xRetval[nInsert++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
227 64 : aClipped, getRGBColorRight()));
228 :
229 32 : aGap.append( aTmpEnd - aLineWidthOffset );
230 32 : aGap.append( aTmpStart - aLineWidthOffset );
231 68 : }
232 : }
233 :
234 68 : if (hasGapColor() && aGap.count() == 4)
235 : {
236 0 : xRetval.realloc( xRetval.getLength() + 1 );
237 : // create geometry for filled gap
238 0 : aGap.setClosed( true );
239 :
240 : basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
241 0 : aGap, aClipRegion, true, false );
242 :
243 0 : xRetval[nInsert++] = Primitive2DReference( new PolyPolygonColorPrimitive2D(
244 0 : aClipped, getRGBColorGap() ) );
245 68 : }
246 : }
247 : else
248 : {
249 : // single line, create geometry
250 472 : basegfx::B2DPolygon aPolygon;
251 472 : const double fExt = getWidth(rViewInformation); // Extend a lot: it'll be clipped after
252 472 : const basegfx::B2DPoint aTmpStart(getStart() - (fExt * aVector));
253 472 : const basegfx::B2DPoint aTmpEnd(getEnd() + (fExt * aVector));
254 472 : xRetval.realloc(1);
255 :
256 : // Get which is the line to show
257 472 : bool bIsSolidline = isSolidLine();
258 472 : double nWidth = getLeftWidth();
259 472 : basegfx::BColor aColor = getRGBColorLeft();
260 472 : if ( basegfx::fTools::equal( 0.0, mfLeftWidth ) )
261 : {
262 6 : nWidth = getRightWidth();
263 6 : aColor = getRGBColorRight();
264 : }
265 : bool const bIsHairline = lcl_UseHairline(
266 472 : nWidth, getStart(), getEnd(), rViewInformation);
267 : nWidth = lcl_GetCorrectedWidth(nWidth,
268 472 : getStart(), getEnd(), rViewInformation);
269 :
270 472 : if(bIsHairline && bIsSolidline)
271 : {
272 : // create hairline primitive
273 376 : aPolygon.append( getStart() );
274 376 : aPolygon.append( getEnd() );
275 :
276 376 : xRetval[0] = Primitive2DReference(new PolygonHairlinePrimitive2D(
277 : aPolygon,
278 752 : aColor));
279 : }
280 : else
281 : {
282 : // create filled polygon primitive
283 96 : const basegfx::B2DVector aLineWidthOffset(((nWidth + 1) * 0.5) * aPerpendicular);
284 96 : basegfx::B2DVector aScale( rViewInformation.getInverseObjectToViewTransformation() * aVector );
285 :
286 96 : aPolygon.append( aTmpStart );
287 96 : aPolygon.append( aTmpEnd );
288 :
289 : basegfx::B2DPolyPolygon aDashed = svtools::ApplyLineDashing(
290 96 : aPolygon, getStyle(), MAP_PIXEL, aScale.getLength() );
291 192 : for (sal_uInt32 i = 0; i < aDashed.count(); i++ )
292 : {
293 96 : basegfx::B2DPolygon aDash = aDashed.getB2DPolygon( i );
294 96 : basegfx::B2DPoint aDashStart = aDash.getB2DPoint( 0 );
295 96 : basegfx::B2DPoint aDashEnd = aDash.getB2DPoint( aDash.count() - 1 );
296 :
297 96 : basegfx::B2DPolygon aDashPolygon;
298 96 : aDashPolygon.append( aDashStart + aLineWidthOffset );
299 96 : aDashPolygon.append( aDashEnd + aLineWidthOffset );
300 96 : aDashPolygon.append( aDashEnd - aLineWidthOffset );
301 96 : aDashPolygon.append( aDashStart - aLineWidthOffset );
302 96 : aDashPolygon.setClosed( true );
303 :
304 : basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
305 96 : aDashPolygon, aClipRegion, true, false );
306 :
307 96 : if ( aClipped.count() )
308 96 : aDashed.setB2DPolygon( i, aClipped.getB2DPolygon( 0 ) );
309 96 : }
310 :
311 96 : xRetval[0] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
312 192 : basegfx::B2DPolyPolygon( aDashed ), aColor));
313 472 : }
314 540 : }
315 : }
316 :
317 542 : return xRetval;
318 : }
319 :
320 542 : BorderLinePrimitive2D::BorderLinePrimitive2D(
321 : const basegfx::B2DPoint& rStart,
322 : const basegfx::B2DPoint& rEnd,
323 : double fLeftWidth,
324 : double fDistance,
325 : double fRightWidth,
326 : double fExtendLeftStart,
327 : double fExtendLeftEnd,
328 : double fExtendRightStart,
329 : double fExtendRightEnd,
330 : const basegfx::BColor& rRGBColorRight,
331 : const basegfx::BColor& rRGBColorLeft,
332 : const basegfx::BColor& rRGBColorGap,
333 : bool bHasGapColor,
334 : const short nStyle)
335 : : BufferedDecompositionPrimitive2D(),
336 : maStart(rStart),
337 : maEnd(rEnd),
338 : mfLeftWidth(fLeftWidth),
339 : mfDistance(fDistance),
340 : mfRightWidth(fRightWidth),
341 : mfExtendLeftStart(fExtendLeftStart),
342 : mfExtendLeftEnd(fExtendLeftEnd),
343 : mfExtendRightStart(fExtendRightStart),
344 : mfExtendRightEnd(fExtendRightEnd),
345 : maRGBColorRight(rRGBColorRight),
346 : maRGBColorLeft(rRGBColorLeft),
347 : maRGBColorGap(rRGBColorGap),
348 : mbHasGapColor(bHasGapColor),
349 542 : mnStyle(nStyle)
350 : {
351 542 : }
352 :
353 0 : bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
354 : {
355 0 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
356 : {
357 0 : const BorderLinePrimitive2D& rCompare = (BorderLinePrimitive2D&)rPrimitive;
358 :
359 0 : return (getStart() == rCompare.getStart()
360 0 : && getEnd() == rCompare.getEnd()
361 0 : && getLeftWidth() == rCompare.getLeftWidth()
362 0 : && getDistance() == rCompare.getDistance()
363 0 : && getRightWidth() == rCompare.getRightWidth()
364 0 : && getExtendLeftStart() == rCompare.getExtendLeftStart()
365 0 : && getExtendLeftEnd() == rCompare.getExtendLeftEnd()
366 0 : && getExtendRightStart() == rCompare.getExtendRightStart()
367 0 : && getExtendRightEnd() == rCompare.getExtendRightEnd()
368 0 : && getRGBColorRight() == rCompare.getRGBColorRight()
369 0 : && getRGBColorLeft() == rCompare.getRGBColorLeft()
370 0 : && getRGBColorGap() == rCompare.getRGBColorGap()
371 0 : && hasGapColor() == rCompare.hasGapColor()
372 0 : && getStyle() == rCompare.getStyle());
373 : }
374 :
375 0 : return false;
376 : }
377 :
378 : // provide unique ID
379 542 : ImplPrimitrive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D)
380 :
381 : } // end of namespace primitive2d
382 : } // end of namespace drawinglayer
383 :
384 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|