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 .
23 #include <osl/diagnose.h>
24 #include <tools/line.hxx>
25 #include <tools/helpers.hxx>
27 #include <vcl/hatch.hxx>
28 #include <vcl/metaact.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/outdev.hxx>
31 #include <vcl/virdev.hxx>
37 #define HATCH_MAXPOINTS 1024
41 static int HatchCmpFnc( const void* p1
, const void* p2
)
43 const tools::Long nX1
= static_cast<Point
const *>(p1
)->X();
44 const tools::Long nX2
= static_cast<Point
const *>(p2
)->X();
45 const tools::Long nY1
= static_cast<Point
const *>(p1
)->Y();
46 const tools::Long nY2
= static_cast<Point
const *>(p2
)->Y();
48 return ( nX1
> nX2
? 1 : nX1
== nX2
? nY1
> nY2
? 1: nY1
== nY2
? 0 : -1 : -1 );
53 void OutputDevice::DrawHatch( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
)
55 assert(!is_double_buffered_window());
57 Hatch
aHatch( rHatch
);
59 if ( mnDrawMode
& ( DrawModeFlags::BlackLine
| DrawModeFlags::WhiteLine
|
60 DrawModeFlags::GrayLine
|
61 DrawModeFlags::SettingsLine
) )
63 Color
aColor( rHatch
.GetColor() );
65 if ( mnDrawMode
& DrawModeFlags::BlackLine
)
67 else if ( mnDrawMode
& DrawModeFlags::WhiteLine
)
69 else if ( mnDrawMode
& DrawModeFlags::GrayLine
)
71 const sal_uInt8 cLum
= aColor
.GetLuminance();
72 aColor
= Color( cLum
, cLum
, cLum
);
74 else if( mnDrawMode
& DrawModeFlags::SettingsLine
)
76 aColor
= GetSettings().GetStyleSettings().GetFontColor();
79 aHatch
.SetColor( aColor
);
83 mpMetaFile
->AddAction( new MetaHatchAction( rPolyPoly
, aHatch
) );
85 if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
88 if( !mpGraphics
&& !AcquireGraphics() )
91 if( mbInitClipRegion
)
97 if( rPolyPoly
.Count() )
99 tools::PolyPolygon
aPolyPoly( LogicToPixel( rPolyPoly
) );
100 GDIMetaFile
* pOldMetaFile
= mpMetaFile
;
101 bool bOldMap
= mbMap
;
103 aPolyPoly
.Optimize( PolyOptimizeFlags::NO_SAME
);
104 aHatch
.SetDistance( ImplLogicWidthToDevicePixel( aHatch
.GetDistance() ) );
106 mpMetaFile
= nullptr;
107 EnableMapMode( false );
108 Push( PushFlags::LINECOLOR
);
109 SetLineColor( aHatch
.GetColor() );
111 DrawHatch( aPolyPoly
, aHatch
, false );
113 EnableMapMode( bOldMap
);
114 mpMetaFile
= pOldMetaFile
;
118 mpAlphaVDev
->DrawHatch( rPolyPoly
, rHatch
);
121 void OutputDevice::AddHatchActions( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
,
125 tools::PolyPolygon
aPolyPoly( rPolyPoly
);
126 aPolyPoly
.Optimize( PolyOptimizeFlags::NO_SAME
| PolyOptimizeFlags::CLOSE
);
128 if( aPolyPoly
.Count() )
130 GDIMetaFile
* pOldMtf
= mpMetaFile
;
133 mpMetaFile
->AddAction( new MetaPushAction( PushFlags::ALL
) );
134 mpMetaFile
->AddAction( new MetaLineColorAction( rHatch
.GetColor(), true ) );
135 DrawHatch( aPolyPoly
, rHatch
, true );
136 mpMetaFile
->AddAction( new MetaPopAction() );
137 mpMetaFile
= pOldMtf
;
141 void OutputDevice::DrawHatch( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
, bool bMtf
)
143 assert(!is_double_buffered_window());
145 if(!rPolyPoly
.Count())
148 // #i115630# DrawHatch does not work with beziers included in the polypolygon, take care of that
149 bool bIsCurve(false);
151 for(sal_uInt16
a(0); !bIsCurve
&& a
< rPolyPoly
.Count(); a
++)
153 if(rPolyPoly
[a
].HasFlags())
161 OSL_ENSURE(false, "DrawHatch does *not* support curves, falling back to AdaptiveSubdivide()...");
162 tools::PolyPolygon aPolyPoly
;
164 rPolyPoly
.AdaptiveSubdivide(aPolyPoly
);
165 DrawHatch(aPolyPoly
, rHatch
, bMtf
);
169 tools::Rectangle
aRect( rPolyPoly
.GetBoundRect() );
170 const tools::Long nLogPixelWidth
= ImplDevicePixelToLogicWidth( 1 );
171 const tools::Long nWidth
= ImplDevicePixelToLogicWidth( std::max( ImplLogicWidthToDevicePixel( rHatch
.GetDistance() ), tools::Long(3) ) );
172 std::unique_ptr
<Point
[]> pPtBuffer(new Point
[ HATCH_MAXPOINTS
]);
173 Point aPt1
, aPt2
, aEndPt1
;
177 aRect
.AdjustLeft( -nLogPixelWidth
); aRect
.AdjustTop( -nLogPixelWidth
); aRect
.AdjustRight(nLogPixelWidth
); aRect
.AdjustBottom(nLogPixelWidth
);
178 CalcHatchValues( aRect
, nWidth
, rHatch
.GetAngle(), aPt1
, aPt2
, aInc
, aEndPt1
);
181 DrawHatchLine( tools::Line( aPt1
, aPt2
), rPolyPoly
, pPtBuffer
.get(), bMtf
);
182 aPt1
.AdjustX(aInc
.Width() ); aPt1
.AdjustY(aInc
.Height() );
183 aPt2
.AdjustX(aInc
.Width() ); aPt2
.AdjustY(aInc
.Height() );
185 while( ( aPt1
.X() <= aEndPt1
.X() ) && ( aPt1
.Y() <= aEndPt1
.Y() ) );
187 if( ( rHatch
.GetStyle() == HatchStyle::Double
) || ( rHatch
.GetStyle() == HatchStyle::Triple
) )
190 CalcHatchValues( aRect
, nWidth
, rHatch
.GetAngle() + Degree10(900), aPt1
, aPt2
, aInc
, aEndPt1
);
193 DrawHatchLine( tools::Line( aPt1
, aPt2
), rPolyPoly
, pPtBuffer
.get(), bMtf
);
194 aPt1
.AdjustX(aInc
.Width() ); aPt1
.AdjustY(aInc
.Height() );
195 aPt2
.AdjustX(aInc
.Width() ); aPt2
.AdjustY(aInc
.Height() );
197 while( ( aPt1
.X() <= aEndPt1
.X() ) && ( aPt1
.Y() <= aEndPt1
.Y() ) );
199 if( rHatch
.GetStyle() == HatchStyle::Triple
)
202 CalcHatchValues( aRect
, nWidth
, rHatch
.GetAngle() + Degree10(450), aPt1
, aPt2
, aInc
, aEndPt1
);
205 DrawHatchLine( tools::Line( aPt1
, aPt2
), rPolyPoly
, pPtBuffer
.get(), bMtf
);
206 aPt1
.AdjustX(aInc
.Width() ); aPt1
.AdjustY(aInc
.Height() );
207 aPt2
.AdjustX(aInc
.Width() ); aPt2
.AdjustY(aInc
.Height() );
209 while( ( aPt1
.X() <= aEndPt1
.X() ) && ( aPt1
.Y() <= aEndPt1
.Y() ) );
215 void OutputDevice::CalcHatchValues( const tools::Rectangle
& rRect
, tools::Long nDist
, Degree10 nAngle10
,
216 Point
& rPt1
, Point
& rPt2
, Size
& rInc
, Point
& rEndPt1
)
219 Degree10 nAngle
= nAngle10
% Degree10(1800);
220 tools::Long nOffset
= 0;
222 if( nAngle
> Degree10(900) )
223 nAngle
-= Degree10(1800);
225 aRef
= ( !IsRefPoint() ? rRect
.TopLeft() : GetRefPoint() );
227 if( Degree10(0) == nAngle
)
229 rInc
= Size( 0, nDist
);
230 rPt1
= rRect
.TopLeft();
231 rPt2
= rRect
.TopRight();
232 rEndPt1
= rRect
.BottomLeft();
234 if( aRef
.Y() <= rRect
.Top() )
235 nOffset
= ( ( rRect
.Top() - aRef
.Y() ) % nDist
);
237 nOffset
= ( nDist
- ( ( aRef
.Y() - rRect
.Top() ) % nDist
) );
239 rPt1
.AdjustY( -nOffset
);
240 rPt2
.AdjustY( -nOffset
);
242 else if( Degree10(900) == nAngle
)
244 rInc
= Size( nDist
, 0 );
245 rPt1
= rRect
.TopLeft();
246 rPt2
= rRect
.BottomLeft();
247 rEndPt1
= rRect
.TopRight();
249 if( aRef
.X() <= rRect
.Left() )
250 nOffset
= ( rRect
.Left() - aRef
.X() ) % nDist
;
252 nOffset
= nDist
- ( ( aRef
.X() - rRect
.Left() ) % nDist
);
254 rPt1
.AdjustX( -nOffset
);
255 rPt2
.AdjustX( -nOffset
);
257 else if( nAngle
>= Degree10(-450) && nAngle
<= Degree10(450) )
259 const double fAngle
= F_PI1800
* std::abs( nAngle
.get() );
260 const double fTan
= tan( fAngle
);
261 const tools::Long nYOff
= FRound( ( rRect
.Right() - rRect
.Left() ) * fTan
);
264 nDist
= FRound( nDist
/ cos( fAngle
) );
265 rInc
= Size( 0, nDist
);
267 if( nAngle
> Degree10(0) )
269 rPt1
= rRect
.TopLeft();
270 rPt2
= Point( rRect
.Right(), rRect
.Top() - nYOff
);
271 rEndPt1
= Point( rRect
.Left(), rRect
.Bottom() + nYOff
);
272 nPY
= FRound( aRef
.Y() - ( ( rPt1
.X() - aRef
.X() ) * fTan
) );
276 rPt1
= rRect
.TopRight();
277 rPt2
= Point( rRect
.Left(), rRect
.Top() - nYOff
);
278 rEndPt1
= Point( rRect
.Right(), rRect
.Bottom() + nYOff
);
279 nPY
= FRound( aRef
.Y() + ( ( rPt1
.X() - aRef
.X() ) * fTan
) );
282 if( nPY
<= rPt1
.Y() )
283 nOffset
= ( rPt1
.Y() - nPY
) % nDist
;
285 nOffset
= nDist
- ( ( nPY
- rPt1
.Y() ) % nDist
);
287 rPt1
.AdjustY( -nOffset
);
288 rPt2
.AdjustY( -nOffset
);
292 const double fAngle
= F_PI1800
* std::abs( nAngle
.get() );
293 const double fTan
= tan( fAngle
);
294 const tools::Long nXOff
= FRound( ( rRect
.Bottom() - rRect
.Top() ) / fTan
);
297 nDist
= FRound( nDist
/ sin( fAngle
) );
298 rInc
= Size( nDist
, 0 );
300 if( nAngle
> Degree10(0) )
302 rPt1
= rRect
.TopLeft();
303 rPt2
= Point( rRect
.Left() - nXOff
, rRect
.Bottom() );
304 rEndPt1
= Point( rRect
.Right() + nXOff
, rRect
.Top() );
305 nPX
= FRound( aRef
.X() - ( ( rPt1
.Y() - aRef
.Y() ) / fTan
) );
309 rPt1
= rRect
.BottomLeft();
310 rPt2
= Point( rRect
.Left() - nXOff
, rRect
.Top() );
311 rEndPt1
= Point( rRect
.Right() + nXOff
, rRect
.Bottom() );
312 nPX
= FRound( aRef
.X() + ( ( rPt1
.Y() - aRef
.Y() ) / fTan
) );
315 if( nPX
<= rPt1
.X() )
316 nOffset
= ( rPt1
.X() - nPX
) % nDist
;
318 nOffset
= nDist
- ( ( nPX
- rPt1
.X() ) % nDist
);
320 rPt1
.AdjustX( -nOffset
);
321 rPt2
.AdjustX( -nOffset
);
325 void OutputDevice::DrawHatchLine( const tools::Line
& rLine
, const tools::PolyPolygon
& rPolyPoly
,
326 Point
* pPtBuffer
, bool bMtf
)
328 assert(!is_double_buffered_window());
331 tools::Long nAdd
, nPCounter
= 0;
333 for( tools::Long nPoly
= 0, nPolyCount
= rPolyPoly
.Count(); nPoly
< nPolyCount
; nPoly
++ )
335 const tools::Polygon
& rPoly
= rPolyPoly
[ static_cast<sal_uInt16
>(nPoly
) ];
337 if( rPoly
.GetSize() > 1 )
339 tools::Line
aCurSegment( rPoly
[ 0 ], Point() );
341 for( tools::Long i
= 1, nCount
= rPoly
.GetSize(); i
<= nCount
; i
++ )
343 aCurSegment
.SetEnd( rPoly
[ static_cast<sal_uInt16
>( i
% nCount
) ] );
346 if( rLine
.Intersection( aCurSegment
, fX
, fY
) )
348 if( ( fabs( fX
- aCurSegment
.GetStart().X() ) <= 0.0000001 ) &&
349 ( fabs( fY
- aCurSegment
.GetStart().Y() ) <= 0.0000001 ) )
351 const tools::Line
aPrevSegment( rPoly
[ static_cast<sal_uInt16
>( ( i
> 1 ) ? ( i
- 2 ) : ( nCount
- 1 ) ) ], aCurSegment
.GetStart() );
352 const double fPrevDistance
= rLine
.GetDistance( aPrevSegment
.GetStart() );
353 const double fCurDistance
= rLine
.GetDistance( aCurSegment
.GetEnd() );
355 if( ( fPrevDistance
<= 0.0 && fCurDistance
> 0.0 ) ||
356 ( fPrevDistance
> 0.0 && fCurDistance
< 0.0 ) )
361 else if( ( fabs( fX
- aCurSegment
.GetEnd().X() ) <= 0.0000001 ) &&
362 ( fabs( fY
- aCurSegment
.GetEnd().Y() ) <= 0.0000001 ) )
364 const tools::Line
aNextSegment( aCurSegment
.GetEnd(), rPoly
[ static_cast<sal_uInt16
>( ( i
+ 1 ) % nCount
) ] );
366 if( ( fabs( rLine
.GetDistance( aNextSegment
.GetEnd() ) ) <= 0.0000001 ) &&
367 ( rLine
.GetDistance( aCurSegment
.GetStart() ) > 0.0 ) )
376 pPtBuffer
[ nPCounter
++ ] = Point( FRound( fX
), FRound( fY
) );
379 aCurSegment
.SetStart( aCurSegment
.GetEnd() );
387 qsort( pPtBuffer
, nPCounter
, sizeof( Point
), HatchCmpFnc
);
394 for( tools::Long i
= 0; i
< nPCounter
; i
+= 2 )
395 mpMetaFile
->AddAction( new MetaLineAction( pPtBuffer
[ i
], pPtBuffer
[ i
+ 1 ] ) );
399 for( tools::Long i
= 0; i
< nPCounter
; i
+= 2 )
400 DrawHatchLine_DrawLine(pPtBuffer
[i
], pPtBuffer
[i
+1]);
404 void OutputDevice::DrawHatchLine_DrawLine(const Point
& rStartPoint
, const Point
& rEndPoint
)
406 Point aPt1
{ImplLogicToDevicePixel(rStartPoint
)}, aPt2
{ImplLogicToDevicePixel(rEndPoint
)};
407 mpGraphics
->DrawLine(aPt1
.X(), aPt1
.Y(), aPt2
.X(), aPt2
.Y(), this);
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */