/src/libreoffice/vcl/source/outdev/hatch.cxx
Line | Count | Source |
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/diagnose.h> |
21 | | #include <tools/line.hxx> |
22 | | #include <tools/helpers.hxx> |
23 | | #include <comphelper/configuration.hxx> |
24 | | |
25 | | #include <vcl/hatch.hxx> |
26 | | #include <vcl/metaact.hxx> |
27 | | #include <vcl/settings.hxx> |
28 | | #include <vcl/virdev.hxx> |
29 | | |
30 | | #include <drawmode.hxx> |
31 | | #include <salgdi.hxx> |
32 | | |
33 | | #include <cassert> |
34 | | #include <cstdlib> |
35 | | #include <memory> |
36 | | |
37 | 3.56M | #define HATCH_MAXPOINTS 1024 |
38 | | |
39 | | extern "C" { |
40 | | |
41 | | static int HatchCmpFnc( const void* p1, const void* p2 ) |
42 | 21.7M | { |
43 | 21.7M | const tools::Long nX1 = static_cast<Point const *>(p1)->X(); |
44 | 21.7M | const tools::Long nX2 = static_cast<Point const *>(p2)->X(); |
45 | 21.7M | const tools::Long nY1 = static_cast<Point const *>(p1)->Y(); |
46 | 21.7M | const tools::Long nY2 = static_cast<Point const *>(p2)->Y(); |
47 | | |
48 | 21.7M | return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 ); |
49 | 21.7M | } |
50 | | |
51 | | } |
52 | | |
53 | | void OutputDevice::DrawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch ) |
54 | 9.45k | { |
55 | 9.45k | assert(!is_double_buffered_window()); |
56 | | |
57 | 9.45k | Hatch aHatch( rHatch ); |
58 | 9.45k | aHatch.SetColor(vcl::drawmode::GetHatchColor(rHatch.GetColor(), GetDrawMode(), GetSettings().GetStyleSettings())); |
59 | | |
60 | 9.45k | if( mpMetaFile ) |
61 | 0 | mpMetaFile->AddAction( new MetaHatchAction( rPolyPoly, aHatch ) ); |
62 | | |
63 | 9.45k | if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() ) |
64 | 0 | return; |
65 | | |
66 | 9.45k | if( !mpGraphics && !AcquireGraphics() ) |
67 | 0 | return; |
68 | 9.45k | assert(mpGraphics); |
69 | | |
70 | 9.45k | if( mbInitClipRegion ) |
71 | 1.87k | InitClipRegion(); |
72 | | |
73 | 9.45k | if( mbOutputClipped ) |
74 | 1.24k | return; |
75 | | |
76 | 8.20k | if( rPolyPoly.Count() ) |
77 | 1.37k | { |
78 | 1.37k | tools::PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) ); |
79 | 1.37k | GDIMetaFile* pOldMetaFile = mpMetaFile; |
80 | 1.37k | bool bOldMap = mbMap; |
81 | | |
82 | 1.37k | aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME ); |
83 | 1.37k | aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) ); |
84 | | |
85 | 1.37k | mpMetaFile = nullptr; |
86 | 1.37k | EnableMapMode( false ); |
87 | 1.37k | Push( vcl::PushFlags::LINECOLOR ); |
88 | 1.37k | SetLineColor( aHatch.GetColor() ); |
89 | 1.37k | InitLineColor(); |
90 | 1.37k | DrawHatch( aPolyPoly, aHatch, false ); |
91 | 1.37k | Pop(); |
92 | 1.37k | EnableMapMode( bOldMap ); |
93 | 1.37k | mpMetaFile = pOldMetaFile; |
94 | 1.37k | } |
95 | 8.20k | } |
96 | | |
97 | | void OutputDevice::AddHatchActions( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch, |
98 | | GDIMetaFile& rMtf ) |
99 | 0 | { |
100 | |
|
101 | 0 | tools::PolyPolygon aPolyPoly( rPolyPoly ); |
102 | 0 | aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME | PolyOptimizeFlags::CLOSE ); |
103 | |
|
104 | 0 | if( aPolyPoly.Count() ) |
105 | 0 | { |
106 | 0 | GDIMetaFile* pOldMtf = mpMetaFile; |
107 | |
|
108 | 0 | mpMetaFile = &rMtf; |
109 | 0 | mpMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::ALL ) ); |
110 | 0 | mpMetaFile->AddAction( new MetaLineColorAction( rHatch.GetColor(), true ) ); |
111 | 0 | DrawHatch( aPolyPoly, rHatch, true ); |
112 | 0 | mpMetaFile->AddAction( new MetaPopAction() ); |
113 | 0 | mpMetaFile = pOldMtf; |
114 | 0 | } |
115 | 0 | } |
116 | | |
117 | | static bool HasSaneNSteps(const Point& rPt1, const Point& rEndPt1, const Size& rInc) |
118 | 1.70k | { |
119 | 1.70k | tools::Long nVertSteps = -1; |
120 | 1.70k | if (rInc.Height()) |
121 | 1.36k | { |
122 | 1.36k | bool bFail = o3tl::checked_sub(rEndPt1.Y(), rPt1.Y(), nVertSteps); |
123 | 1.36k | if (bFail) |
124 | 0 | nVertSteps = std::numeric_limits<tools::Long>::max(); |
125 | 1.36k | else |
126 | 1.36k | nVertSteps = nVertSteps / rInc.Height(); |
127 | 1.36k | } |
128 | 1.70k | tools::Long nHorzSteps = -1; |
129 | 1.70k | if (rInc.Width()) |
130 | 347 | { |
131 | 347 | bool bFail = o3tl::checked_sub(rEndPt1.X(), rPt1.X(), nHorzSteps); |
132 | 347 | if (bFail) |
133 | 0 | nHorzSteps = std::numeric_limits<tools::Long>::max(); |
134 | 347 | else |
135 | 347 | nHorzSteps = nHorzSteps / rInc.Width(); |
136 | 347 | } |
137 | 1.70k | auto nSteps = std::max(nVertSteps, nHorzSteps); |
138 | 1.70k | if (nSteps > 1024) |
139 | 211 | { |
140 | 211 | SAL_WARN("vcl.gdi", "skipping slow hatch with " << nSteps << " steps"); |
141 | 211 | return false; |
142 | 211 | } |
143 | 1.49k | return true; |
144 | 1.70k | } |
145 | | |
146 | | void OutputDevice::DrawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch, bool bMtf ) |
147 | 1.37k | { |
148 | 1.37k | assert(!is_double_buffered_window()); |
149 | | |
150 | 1.37k | if(!rPolyPoly.Count()) |
151 | 0 | return; |
152 | | |
153 | | // #i115630# DrawHatch does not work with beziers included in the polypolygon, take care of that |
154 | 1.37k | bool bIsCurve(false); |
155 | | |
156 | 2.99k | for(sal_uInt16 a(0); !bIsCurve && a < rPolyPoly.Count(); a++) |
157 | 1.61k | { |
158 | 1.61k | if(rPolyPoly[a].HasFlags()) |
159 | 0 | { |
160 | 0 | bIsCurve = true; |
161 | 0 | } |
162 | 1.61k | } |
163 | | |
164 | 1.37k | if(bIsCurve) |
165 | 0 | { |
166 | 0 | OSL_ENSURE(false, "DrawHatch does *not* support curves, falling back to AdaptiveSubdivide()..."); |
167 | 0 | tools::PolyPolygon aPolyPoly; |
168 | |
|
169 | 0 | rPolyPoly.AdaptiveSubdivide(aPolyPoly); |
170 | 0 | DrawHatch(aPolyPoly, rHatch, bMtf); |
171 | |
|
172 | 0 | return; |
173 | 0 | } |
174 | | |
175 | 1.37k | tools::Rectangle aRect( rPolyPoly.GetBoundRect() ); |
176 | 1.37k | const tools::Long nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 ); |
177 | 1.37k | const tools::Long nWidth = ImplDevicePixelToLogicWidth( std::max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), tools::Long(3) ) ); |
178 | 1.37k | std::unique_ptr<Point[]> pPtBuffer(new Point[ HATCH_MAXPOINTS ]); |
179 | 1.37k | Point aPt1, aPt2, aEndPt1; |
180 | 1.37k | Size aInc; |
181 | | |
182 | | // Single hatch |
183 | 1.37k | aRect.AdjustLeft( -nLogPixelWidth ); aRect.AdjustTop( -nLogPixelWidth ); aRect.AdjustRight(nLogPixelWidth ); aRect.AdjustBottom(nLogPixelWidth ); |
184 | 1.37k | CalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 ); |
185 | 1.37k | if (comphelper::IsFuzzing() && !HasSaneNSteps(aPt1, aEndPt1, aInc)) |
186 | 82 | return; |
187 | | |
188 | 1.29k | if (aInc.Width() <= 0 && aInc.Height() <= 0) |
189 | 1.29k | SAL_WARN("vcl.gdi", "invalid increment"); |
190 | 1.29k | else |
191 | 1.29k | { |
192 | 1.29k | do |
193 | 79.6k | { |
194 | 79.6k | DrawHatchLine( tools::Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf ); |
195 | 79.6k | aPt1.AdjustX(aInc.Width() ); aPt1.AdjustY(aInc.Height() ); |
196 | 79.6k | aPt2.AdjustX(aInc.Width() ); aPt2.AdjustY(aInc.Height() ); |
197 | 79.6k | } |
198 | 79.6k | while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) ); |
199 | 1.29k | } |
200 | | |
201 | 1.29k | if (rHatch.GetStyle() != HatchStyle::Double && rHatch.GetStyle() != HatchStyle::Triple) |
202 | 1.02k | return; |
203 | | |
204 | | // Double hatch |
205 | 273 | CalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900_deg10, aPt1, aPt2, aInc, aEndPt1 ); |
206 | 273 | if (comphelper::IsFuzzing() && !HasSaneNSteps(aPt1, aEndPt1, aInc)) |
207 | 120 | return; |
208 | | |
209 | 153 | do |
210 | 9.74k | { |
211 | 9.74k | DrawHatchLine( tools::Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf ); |
212 | 9.74k | aPt1.AdjustX(aInc.Width() ); aPt1.AdjustY(aInc.Height() ); |
213 | 9.74k | aPt2.AdjustX(aInc.Width() ); aPt2.AdjustY(aInc.Height() ); |
214 | 9.74k | } |
215 | 9.74k | while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) ); |
216 | | |
217 | 153 | if( rHatch.GetStyle() == HatchStyle::Triple ) |
218 | 59 | { |
219 | | // Triple hatch |
220 | 59 | CalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450_deg10, aPt1, aPt2, aInc, aEndPt1 ); |
221 | 59 | if (comphelper::IsFuzzing() && !HasSaneNSteps(aPt1, aEndPt1, aInc)) |
222 | 9 | return; |
223 | | |
224 | 50 | do |
225 | 1.01k | { |
226 | 1.01k | DrawHatchLine( tools::Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer.get(), bMtf ); |
227 | 1.01k | aPt1.AdjustX(aInc.Width() ); aPt1.AdjustY(aInc.Height() ); |
228 | 1.01k | aPt2.AdjustX(aInc.Width() ); aPt2.AdjustY(aInc.Height() ); |
229 | 1.01k | } |
230 | 1.01k | while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) ); |
231 | 50 | } |
232 | 153 | } |
233 | | |
234 | | void OutputDevice::CalcHatchValues( const tools::Rectangle& rRect, tools::Long nDist, Degree10 nAngle10, |
235 | | Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 ) |
236 | 1.70k | { |
237 | 1.70k | Point aRef; |
238 | 1.70k | Degree10 nAngle = nAngle10 % 1800_deg10; |
239 | 1.70k | tools::Long nOffset = 0; |
240 | | |
241 | 1.70k | if( nAngle > 900_deg10 ) |
242 | 124 | nAngle -= 1800_deg10; |
243 | | |
244 | 1.70k | aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() ); |
245 | | |
246 | 1.70k | if( 0_deg10 == nAngle ) |
247 | 1.08k | { |
248 | 1.08k | rInc = Size( 0, nDist ); |
249 | 1.08k | rPt1 = rRect.TopLeft(); |
250 | 1.08k | rPt2 = rRect.TopRight(); |
251 | 1.08k | rEndPt1 = rRect.BottomLeft(); |
252 | | |
253 | 1.08k | if( aRef.Y() <= rRect.Top() ) |
254 | 358 | nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist ); |
255 | 730 | else |
256 | 730 | nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) ); |
257 | | |
258 | 1.08k | rPt1.AdjustY( -nOffset ); |
259 | 1.08k | rPt2.AdjustY( -nOffset ); |
260 | 1.08k | } |
261 | 620 | else if( 900_deg10 == nAngle ) |
262 | 206 | { |
263 | 206 | rInc = Size( nDist, 0 ); |
264 | 206 | rPt1 = rRect.TopLeft(); |
265 | 206 | rPt2 = rRect.BottomLeft(); |
266 | 206 | rEndPt1 = rRect.TopRight(); |
267 | | |
268 | 206 | if( aRef.X() <= rRect.Left() ) |
269 | 18 | nOffset = ( rRect.Left() - aRef.X() ) % nDist; |
270 | 188 | else |
271 | 188 | nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist ); |
272 | | |
273 | 206 | rPt1.AdjustX( -nOffset ); |
274 | 206 | rPt2.AdjustX( -nOffset ); |
275 | 206 | } |
276 | 414 | else if( nAngle >= Degree10(-450) && nAngle <= 450_deg10 ) |
277 | 273 | { |
278 | 273 | const double fAngle = std::abs( toRadians(nAngle) ); |
279 | 273 | const double fTan = tan( fAngle ); |
280 | 273 | const tools::Long nYOff = basegfx::fround<tools::Long>( ( rRect.Right() - rRect.Left() ) * fTan ); |
281 | 273 | tools::Long nPY; |
282 | | |
283 | 273 | nDist = basegfx::fround<tools::Long>(nDist / cos(fAngle)); |
284 | 273 | rInc = Size( 0, nDist ); |
285 | | |
286 | 273 | if( nAngle > 0_deg10 ) |
287 | 100 | { |
288 | 100 | rPt1 = rRect.TopLeft(); |
289 | 100 | rPt2 = Point( rRect.Right(), rRect.Top() - nYOff ); |
290 | 100 | rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff ); |
291 | 100 | nPY = basegfx::fround<tools::Long>(aRef.Y() - ((rPt1.X() - aRef.X()) * fTan)); |
292 | 100 | } |
293 | 173 | else |
294 | 173 | { |
295 | 173 | rPt1 = rRect.TopRight(); |
296 | 173 | rPt2 = Point( rRect.Left(), rRect.Top() - nYOff ); |
297 | 173 | rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff ); |
298 | 173 | nPY = basegfx::fround<tools::Long>(aRef.Y() + ((rPt1.X() - aRef.X()) * fTan)); |
299 | 173 | } |
300 | | |
301 | 273 | if( nPY <= rPt1.Y() ) |
302 | 100 | nOffset = ( rPt1.Y() - nPY ) % nDist; |
303 | 173 | else |
304 | 173 | nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist ); |
305 | | |
306 | 273 | rPt1.AdjustY( -nOffset ); |
307 | 273 | rPt2.AdjustY( -nOffset ); |
308 | 273 | } |
309 | 141 | else |
310 | 141 | { |
311 | 141 | const double fAngle = std::abs( toRadians(nAngle) ); |
312 | 141 | const double fTan = tan( fAngle ); |
313 | 141 | const tools::Long nXOff = basegfx::fround<tools::Long>( (static_cast<double>(rRect.Bottom()) - rRect.Top()) / fTan ); |
314 | 141 | tools::Long nPX; |
315 | | |
316 | 141 | nDist = basegfx::fround<tools::Long>(nDist / sin(fAngle)); |
317 | 141 | rInc = Size( nDist, 0 ); |
318 | | |
319 | 141 | if( nAngle > 0_deg10 ) |
320 | 73 | { |
321 | 73 | rPt1 = rRect.TopLeft(); |
322 | 73 | rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() ); |
323 | 73 | rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() ); |
324 | 73 | nPX = basegfx::fround<tools::Long>( aRef.X() - ( (static_cast<double>(rPt1.Y()) - aRef.Y()) / fTan ) ); |
325 | 73 | } |
326 | 68 | else |
327 | 68 | { |
328 | 68 | rPt1 = rRect.BottomLeft(); |
329 | 68 | rPt2 = Point( rRect.Left() - nXOff, rRect.Top() ); |
330 | 68 | rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() ); |
331 | 68 | nPX = basegfx::fround<tools::Long>( aRef.X() + ( (static_cast<double>(rPt1.Y()) - aRef.Y()) / fTan ) ); |
332 | 68 | } |
333 | | |
334 | 141 | if( nPX <= rPt1.X() ) |
335 | 117 | nOffset = ( rPt1.X() - nPX ) % nDist; |
336 | 24 | else |
337 | 24 | nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist ); |
338 | | |
339 | 141 | rPt1.AdjustX( -nOffset ); |
340 | 141 | rPt2.AdjustX( -nOffset ); |
341 | 141 | } |
342 | 1.70k | } |
343 | | |
344 | | void OutputDevice::DrawHatchLine( const tools::Line& rLine, const tools::PolyPolygon& rPolyPoly, |
345 | | Point* pPtBuffer, bool bMtf ) |
346 | 90.4k | { |
347 | 90.4k | assert(!is_double_buffered_window()); |
348 | | |
349 | 90.4k | double fX, fY; |
350 | 90.4k | tools::Long nAdd, nPCounter = 0; |
351 | | |
352 | 198k | for( tools::Long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ ) |
353 | 108k | { |
354 | 108k | const tools::Polygon& rPoly = rPolyPoly[ static_cast<sal_uInt16>(nPoly) ]; |
355 | | |
356 | 108k | if( rPoly.GetSize() > 1 ) |
357 | 106k | { |
358 | 106k | tools::Line aCurSegment( rPoly[ 0 ], Point() ); |
359 | | |
360 | 17.8M | for( tools::Long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ ) |
361 | 17.7M | { |
362 | 17.7M | aCurSegment.SetEnd( rPoly[ static_cast<sal_uInt16>( i % nCount ) ] ); |
363 | 17.7M | nAdd = 0; |
364 | | |
365 | 17.7M | if( rLine.Intersection( aCurSegment, fX, fY ) ) |
366 | 3.57M | { |
367 | 3.57M | if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) && |
368 | 433k | ( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) ) |
369 | 5.72k | { |
370 | 5.72k | const tools::Line aPrevSegment( rPoly[ static_cast<sal_uInt16>( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() ); |
371 | 5.72k | const double fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() ); |
372 | 5.72k | const double fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() ); |
373 | | |
374 | 5.72k | if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) || |
375 | 4.91k | ( fPrevDistance > 0.0 && fCurDistance < 0.0 ) ) |
376 | 1.55k | { |
377 | 1.55k | nAdd = 1; |
378 | 1.55k | } |
379 | 5.72k | } |
380 | 3.56M | else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) && |
381 | 432k | ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) ) |
382 | 5.72k | { |
383 | 5.72k | const tools::Line aNextSegment( aCurSegment.GetEnd(), rPoly[ static_cast<sal_uInt16>( ( i + 1 ) % nCount ) ] ); |
384 | | |
385 | 5.72k | if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) && |
386 | 537 | ( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) ) |
387 | 136 | { |
388 | 136 | nAdd = 1; |
389 | 136 | } |
390 | 5.72k | } |
391 | 3.56M | else |
392 | 3.56M | nAdd = 1; |
393 | | |
394 | 3.57M | if( nAdd ) |
395 | 3.56M | { |
396 | 3.56M | if (nPCounter == HATCH_MAXPOINTS) |
397 | 359 | { |
398 | 359 | SAL_WARN("vcl.gdi", "too many hatch points"); |
399 | 359 | return; |
400 | 359 | } |
401 | 3.56M | pPtBuffer[nPCounter++] = Point(basegfx::fround<tools::Long>(fX), |
402 | 3.56M | basegfx::fround<tools::Long>(fY)); |
403 | 3.56M | } |
404 | 3.57M | } |
405 | | |
406 | 17.7M | aCurSegment.SetStart( aCurSegment.GetEnd() ); |
407 | 17.7M | } |
408 | 106k | } |
409 | 108k | } |
410 | | |
411 | 90.0k | if( nPCounter <= 1 ) |
412 | 2.91k | return; |
413 | | |
414 | 87.1k | qsort( pPtBuffer, nPCounter, sizeof( Point ), HatchCmpFnc ); |
415 | | |
416 | 87.1k | if( nPCounter & 1 ) |
417 | 0 | nPCounter--; |
418 | | |
419 | 87.1k | if( bMtf ) |
420 | 0 | { |
421 | 0 | for( tools::Long i = 0; i < nPCounter; i += 2 ) |
422 | 0 | mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) ); |
423 | 0 | } |
424 | 87.1k | else |
425 | 87.1k | { |
426 | 1.68M | for( tools::Long i = 0; i < nPCounter; i += 2 ) |
427 | 1.59M | DrawHatchLine_DrawLine(pPtBuffer[i], pPtBuffer[i+1]); |
428 | 87.1k | } |
429 | 87.1k | } |
430 | | |
431 | | void OutputDevice::DrawHatchLine_DrawLine(const Point& rStartPoint, const Point& rEndPoint) |
432 | 1.59M | { |
433 | 1.59M | Point aPt1{ImplLogicToDevicePixel(rStartPoint)}, aPt2{ImplLogicToDevicePixel(rEndPoint)}; |
434 | 1.59M | mpGraphics->DrawLine(aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), *this); |
435 | 1.59M | } |
436 | | |
437 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |