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