1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
22 #include <osl/diagnose.h>
23 #include <tools/line.hxx>
24 #include <tools/helpers.hxx>
26 #include <vcl/hatch.hxx>
27 #include <vcl/metaact.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/outdev.hxx>
30 #include <vcl/virdev.hxx>
36 #define HATCH_MAXPOINTS 1024
40 static int HatchCmpFnc( const void* p1
, const void* p2
)
42 const long nX1
= static_cast<Point
const *>(p1
)->X();
43 const long nX2
= static_cast<Point
const *>(p2
)->X();
44 const long nY1
= static_cast<Point
const *>(p1
)->Y();
45 const long nY2
= static_cast<Point
const *>(p2
)->Y();
47 return ( nX1
> nX2
? 1 : nX1
== nX2
? nY1
> nY2
? 1: nY1
== nY2
? 0 : -1 : -1 );
52 void OutputDevice::DrawHatch( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
)
54 assert(!is_double_buffered_window());
56 Hatch
aHatch( rHatch
);
58 if ( mnDrawMode
& ( DrawModeFlags::BlackLine
| DrawModeFlags::WhiteLine
|
59 DrawModeFlags::GrayLine
|
60 DrawModeFlags::SettingsLine
) )
62 Color
aColor( rHatch
.GetColor() );
64 if ( mnDrawMode
& DrawModeFlags::BlackLine
)
66 else if ( mnDrawMode
& DrawModeFlags::WhiteLine
)
68 else if ( mnDrawMode
& DrawModeFlags::GrayLine
)
70 const sal_uInt8 cLum
= aColor
.GetLuminance();
71 aColor
= Color( cLum
, cLum
, cLum
);
73 else if( mnDrawMode
& DrawModeFlags::SettingsLine
)
75 aColor
= GetSettings().GetStyleSettings().GetFontColor();
78 aHatch
.SetColor( aColor
);
82 mpMetaFile
->AddAction( new MetaHatchAction( rPolyPoly
, aHatch
) );
84 if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
87 if( !mpGraphics
&& !AcquireGraphics() )
90 if( mbInitClipRegion
)
96 if( rPolyPoly
.Count() )
98 tools::PolyPolygon
aPolyPoly( LogicToPixel( rPolyPoly
) );
99 GDIMetaFile
* pOldMetaFile
= mpMetaFile
;
100 bool bOldMap
= mbMap
;
102 aPolyPoly
.Optimize( PolyOptimizeFlags::NO_SAME
);
103 aHatch
.SetDistance( ImplLogicWidthToDevicePixel( aHatch
.GetDistance() ) );
105 mpMetaFile
= nullptr;
106 EnableMapMode( false );
107 Push( PushFlags::LINECOLOR
);
108 SetLineColor( aHatch
.GetColor() );
110 DrawHatch( aPolyPoly
, aHatch
, false );
112 EnableMapMode( bOldMap
);
113 mpMetaFile
= pOldMetaFile
;
117 mpAlphaVDev
->DrawHatch( rPolyPoly
, rHatch
);
120 void OutputDevice::AddHatchActions( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
,
124 tools::PolyPolygon
aPolyPoly( rPolyPoly
);
125 aPolyPoly
.Optimize( PolyOptimizeFlags::NO_SAME
| PolyOptimizeFlags::CLOSE
);
127 if( aPolyPoly
.Count() )
129 GDIMetaFile
* pOldMtf
= mpMetaFile
;
132 mpMetaFile
->AddAction( new MetaPushAction( PushFlags::ALL
) );
133 mpMetaFile
->AddAction( new MetaLineColorAction( rHatch
.GetColor(), true ) );
134 DrawHatch( aPolyPoly
, rHatch
, true );
135 mpMetaFile
->AddAction( new MetaPopAction() );
136 mpMetaFile
= pOldMtf
;
140 void OutputDevice::DrawHatch( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
, bool bMtf
)
142 assert(!is_double_buffered_window());
144 if(rPolyPoly
.Count())
146 // #i115630# DrawHatch does not work with beziers included in the polypolygon, take care of that
147 bool bIsCurve(false);
149 for(sal_uInt16
a(0); !bIsCurve
&& a
< rPolyPoly
.Count(); a
++)
151 if(rPolyPoly
[a
].HasFlags())
159 OSL_ENSURE(false, "DrawHatch does *not* support curves, falling back to AdaptiveSubdivide()...");
160 tools::PolyPolygon aPolyPoly
;
162 rPolyPoly
.AdaptiveSubdivide(aPolyPoly
);
163 DrawHatch(aPolyPoly
, rHatch
, bMtf
);
167 tools::Rectangle
aRect( rPolyPoly
.GetBoundRect() );
168 const long nLogPixelWidth
= ImplDevicePixelToLogicWidth( 1 );
169 const long nWidth
= ImplDevicePixelToLogicWidth( std::max( ImplLogicWidthToDevicePixel( rHatch
.GetDistance() ), 3L ) );
170 std::unique_ptr
<Point
[]> pPtBuffer(new Point
[ HATCH_MAXPOINTS
]);
171 Point aPt1
, aPt2
, aEndPt1
;
175 aRect
.AdjustLeft( -nLogPixelWidth
); aRect
.AdjustTop( -nLogPixelWidth
); aRect
.AdjustRight(nLogPixelWidth
); aRect
.AdjustBottom(nLogPixelWidth
);
176 CalcHatchValues( aRect
, nWidth
, rHatch
.GetAngle(), aPt1
, aPt2
, aInc
, aEndPt1
);
179 DrawHatchLine( tools::Line( aPt1
, aPt2
), rPolyPoly
, pPtBuffer
.get(), bMtf
);
180 aPt1
.AdjustX(aInc
.Width() ); aPt1
.AdjustY(aInc
.Height() );
181 aPt2
.AdjustX(aInc
.Width() ); aPt2
.AdjustY(aInc
.Height() );
183 while( ( aPt1
.X() <= aEndPt1
.X() ) && ( aPt1
.Y() <= aEndPt1
.Y() ) );
185 if( ( rHatch
.GetStyle() == HatchStyle::Double
) || ( rHatch
.GetStyle() == HatchStyle::Triple
) )
188 CalcHatchValues( aRect
, nWidth
, rHatch
.GetAngle() + 900, aPt1
, aPt2
, aInc
, aEndPt1
);
191 DrawHatchLine( tools::Line( aPt1
, aPt2
), rPolyPoly
, pPtBuffer
.get(), bMtf
);
192 aPt1
.AdjustX(aInc
.Width() ); aPt1
.AdjustY(aInc
.Height() );
193 aPt2
.AdjustX(aInc
.Width() ); aPt2
.AdjustY(aInc
.Height() );
195 while( ( aPt1
.X() <= aEndPt1
.X() ) && ( aPt1
.Y() <= aEndPt1
.Y() ) );
197 if( rHatch
.GetStyle() == HatchStyle::Triple
)
200 CalcHatchValues( aRect
, nWidth
, rHatch
.GetAngle() + 450, aPt1
, aPt2
, aInc
, aEndPt1
);
203 DrawHatchLine( tools::Line( aPt1
, aPt2
), rPolyPoly
, pPtBuffer
.get(), bMtf
);
204 aPt1
.AdjustX(aInc
.Width() ); aPt1
.AdjustY(aInc
.Height() );
205 aPt2
.AdjustX(aInc
.Width() ); aPt2
.AdjustY(aInc
.Height() );
207 while( ( aPt1
.X() <= aEndPt1
.X() ) && ( aPt1
.Y() <= aEndPt1
.Y() ) );
214 void OutputDevice::CalcHatchValues( const tools::Rectangle
& rRect
, long nDist
, sal_uInt16 nAngle10
,
215 Point
& rPt1
, Point
& rPt2
, Size
& rInc
, Point
& rEndPt1
)
218 long nAngle
= nAngle10
% 1800;
224 aRef
= ( !IsRefPoint() ? rRect
.TopLeft() : GetRefPoint() );
228 rInc
= Size( 0, nDist
);
229 rPt1
= rRect
.TopLeft();
230 rPt2
= rRect
.TopRight();
231 rEndPt1
= rRect
.BottomLeft();
233 if( aRef
.Y() <= rRect
.Top() )
234 nOffset
= ( ( rRect
.Top() - aRef
.Y() ) % nDist
);
236 nOffset
= ( nDist
- ( ( aRef
.Y() - rRect
.Top() ) % nDist
) );
238 rPt1
.AdjustY( -nOffset
);
239 rPt2
.AdjustY( -nOffset
);
241 else if( 900 == nAngle
)
243 rInc
= Size( nDist
, 0 );
244 rPt1
= rRect
.TopLeft();
245 rPt2
= rRect
.BottomLeft();
246 rEndPt1
= rRect
.TopRight();
248 if( aRef
.X() <= rRect
.Left() )
249 nOffset
= ( rRect
.Left() - aRef
.X() ) % nDist
;
251 nOffset
= nDist
- ( ( aRef
.X() - rRect
.Left() ) % nDist
);
253 rPt1
.AdjustX( -nOffset
);
254 rPt2
.AdjustX( -nOffset
);
256 else if( nAngle
>= -450 && nAngle
<= 450 )
258 const double fAngle
= F_PI1800
* labs( nAngle
);
259 const double fTan
= tan( fAngle
);
260 const long nYOff
= FRound( ( rRect
.Right() - rRect
.Left() ) * fTan
);
263 rInc
= Size( 0, nDist
= FRound( nDist
/ cos( fAngle
) ) );
267 rPt1
= rRect
.TopLeft();
268 rPt2
= Point( rRect
.Right(), rRect
.Top() - nYOff
);
269 rEndPt1
= Point( rRect
.Left(), rRect
.Bottom() + nYOff
);
270 nPY
= FRound( aRef
.Y() - ( ( rPt1
.X() - aRef
.X() ) * fTan
) );
274 rPt1
= rRect
.TopRight();
275 rPt2
= Point( rRect
.Left(), rRect
.Top() - nYOff
);
276 rEndPt1
= Point( rRect
.Right(), rRect
.Bottom() + nYOff
);
277 nPY
= FRound( aRef
.Y() + ( ( rPt1
.X() - aRef
.X() ) * fTan
) );
280 if( nPY
<= rPt1
.Y() )
281 nOffset
= ( rPt1
.Y() - nPY
) % nDist
;
283 nOffset
= nDist
- ( ( nPY
- rPt1
.Y() ) % nDist
);
285 rPt1
.AdjustY( -nOffset
);
286 rPt2
.AdjustY( -nOffset
);
290 const double fAngle
= F_PI1800
* labs( nAngle
);
291 const double fTan
= tan( fAngle
);
292 const long nXOff
= FRound( ( rRect
.Bottom() - rRect
.Top() ) / fTan
);
295 rInc
= Size( nDist
= FRound( nDist
/ sin( fAngle
) ), 0 );
299 rPt1
= rRect
.TopLeft();
300 rPt2
= Point( rRect
.Left() - nXOff
, rRect
.Bottom() );
301 rEndPt1
= Point( rRect
.Right() + nXOff
, rRect
.Top() );
302 nPX
= FRound( aRef
.X() - ( ( rPt1
.Y() - aRef
.Y() ) / fTan
) );
306 rPt1
= rRect
.BottomLeft();
307 rPt2
= Point( rRect
.Left() - nXOff
, rRect
.Top() );
308 rEndPt1
= Point( rRect
.Right() + nXOff
, rRect
.Bottom() );
309 nPX
= FRound( aRef
.X() + ( ( rPt1
.Y() - aRef
.Y() ) / fTan
) );
312 if( nPX
<= rPt1
.X() )
313 nOffset
= ( rPt1
.X() - nPX
) % nDist
;
315 nOffset
= nDist
- ( ( nPX
- rPt1
.X() ) % nDist
);
317 rPt1
.AdjustX( -nOffset
);
318 rPt2
.AdjustX( -nOffset
);
322 void OutputDevice::DrawHatchLine( const tools::Line
& rLine
, const tools::PolyPolygon
& rPolyPoly
,
323 Point
* pPtBuffer
, bool bMtf
)
325 assert(!is_double_buffered_window());
328 long nAdd
, nPCounter
= 0;
330 for( long nPoly
= 0, nPolyCount
= rPolyPoly
.Count(); nPoly
< nPolyCount
; nPoly
++ )
332 const tools::Polygon
& rPoly
= rPolyPoly
[ static_cast<sal_uInt16
>(nPoly
) ];
334 if( rPoly
.GetSize() > 1 )
336 tools::Line
aCurSegment( rPoly
[ 0 ], Point() );
338 for( long i
= 1, nCount
= rPoly
.GetSize(); i
<= nCount
; i
++ )
340 aCurSegment
.SetEnd( rPoly
[ static_cast<sal_uInt16
>( i
% nCount
) ] );
343 if( rLine
.Intersection( aCurSegment
, fX
, fY
) )
345 if( ( fabs( fX
- aCurSegment
.GetStart().X() ) <= 0.0000001 ) &&
346 ( fabs( fY
- aCurSegment
.GetStart().Y() ) <= 0.0000001 ) )
348 const tools::Line
aPrevSegment( rPoly
[ static_cast<sal_uInt16
>( ( i
> 1 ) ? ( i
- 2 ) : ( nCount
- 1 ) ) ], aCurSegment
.GetStart() );
349 const double fPrevDistance
= rLine
.GetDistance( aPrevSegment
.GetStart() );
350 const double fCurDistance
= rLine
.GetDistance( aCurSegment
.GetEnd() );
352 if( ( fPrevDistance
<= 0.0 && fCurDistance
> 0.0 ) ||
353 ( fPrevDistance
> 0.0 && fCurDistance
< 0.0 ) )
358 else if( ( fabs( fX
- aCurSegment
.GetEnd().X() ) <= 0.0000001 ) &&
359 ( fabs( fY
- aCurSegment
.GetEnd().Y() ) <= 0.0000001 ) )
361 const tools::Line
aNextSegment( aCurSegment
.GetEnd(), rPoly
[ static_cast<sal_uInt16
>( ( i
+ 1 ) % nCount
) ] );
363 if( ( fabs( rLine
.GetDistance( aNextSegment
.GetEnd() ) ) <= 0.0000001 ) &&
364 ( rLine
.GetDistance( aCurSegment
.GetStart() ) > 0.0 ) )
373 pPtBuffer
[ nPCounter
++ ] = Point( FRound( fX
), FRound( fY
) );
376 aCurSegment
.SetStart( aCurSegment
.GetEnd() );
383 qsort( pPtBuffer
, nPCounter
, sizeof( Point
), HatchCmpFnc
);
390 for( long i
= 0; i
< nPCounter
; i
+= 2 )
391 mpMetaFile
->AddAction( new MetaLineAction( pPtBuffer
[ i
], pPtBuffer
[ i
+ 1 ] ) );
395 for( long i
= 0; i
< nPCounter
; i
+= 2 )
396 DrawHatchLine_DrawLine(pPtBuffer
[i
], pPtBuffer
[i
+1]);
401 void OutputDevice::DrawHatchLine_DrawLine(const Point
& rStartPoint
, const Point
& rEndPoint
)
403 Point aPt1
{ImplLogicToDevicePixel(rStartPoint
)}, aPt2
{ImplLogicToDevicePixel(rEndPoint
)};
404 mpGraphics
->DrawLine(aPt1
.X(), aPt1
.Y(), aPt2
.X(), aPt2
.Y(), this);
407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */