LibreOffice Module vcl (master)  1
outdev/hatch.cxx
Go to the documentation of this file.
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 <cassert>
21 #include <cstdlib>
22 
23 #include <osl/diagnose.h>
24 #include <tools/line.hxx>
25 #include <tools/helpers.hxx>
26 
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>
32 
33 #include <salgdi.hxx>
34 
35 #include <memory>
36 
37 #define HATCH_MAXPOINTS 1024
38 
39 extern "C" {
40 
41 static int HatchCmpFnc( const void* p1, const void* p2 )
42 {
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();
47 
48  return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 );
49 }
50 
51 }
52 
53 void OutputDevice::DrawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch )
54 {
56 
57  Hatch aHatch( rHatch );
58 
62  {
63  Color aColor( rHatch.GetColor() );
64 
66  aColor = COL_BLACK;
68  aColor = COL_WHITE;
70  {
71  const sal_uInt8 cLum = aColor.GetLuminance();
72  aColor = Color( cLum, cLum, cLum );
73  }
75  {
77  }
78 
79  aHatch.SetColor( aColor );
80  }
81 
82  if( mpMetaFile )
83  mpMetaFile->AddAction( new MetaHatchAction( rPolyPoly, aHatch ) );
84 
86  return;
87 
88  if( !mpGraphics && !AcquireGraphics() )
89  return;
90 
91  if( mbInitClipRegion )
93 
94  if( mbOutputClipped )
95  return;
96 
97  if( rPolyPoly.Count() )
98  {
99  tools::PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
100  GDIMetaFile* pOldMetaFile = mpMetaFile;
101  bool bOldMap = mbMap;
102 
103  aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME );
105 
106  mpMetaFile = nullptr;
107  EnableMapMode( false );
109  SetLineColor( aHatch.GetColor() );
110  InitLineColor();
111  DrawHatch( aPolyPoly, aHatch, false );
112  Pop();
113  EnableMapMode( bOldMap );
114  mpMetaFile = pOldMetaFile;
115  }
116 
117  if( mpAlphaVDev )
118  mpAlphaVDev->DrawHatch( rPolyPoly, rHatch );
119 }
120 
121 void OutputDevice::AddHatchActions( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch,
122  GDIMetaFile& rMtf )
123 {
124 
125  tools::PolyPolygon aPolyPoly( rPolyPoly );
126  aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME | PolyOptimizeFlags::CLOSE );
127 
128  if( aPolyPoly.Count() )
129  {
130  GDIMetaFile* pOldMtf = mpMetaFile;
131 
132  mpMetaFile = &rMtf;
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;
138  }
139 }
140 
141 void OutputDevice::DrawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch, bool bMtf )
142 {
144 
145  if(!rPolyPoly.Count())
146  return;
147 
148  // #i115630# DrawHatch does not work with beziers included in the polypolygon, take care of that
149  bool bIsCurve(false);
150 
151  for(sal_uInt16 a(0); !bIsCurve && a < rPolyPoly.Count(); a++)
152  {
153  if(rPolyPoly[a].HasFlags())
154  {
155  bIsCurve = true;
156  }
157  }
158 
159  if(bIsCurve)
160  {
161  OSL_ENSURE(false, "DrawHatch does *not* support curves, falling back to AdaptiveSubdivide()...");
162  tools::PolyPolygon aPolyPoly;
163 
164  rPolyPoly.AdaptiveSubdivide(aPolyPoly);
165  DrawHatch(aPolyPoly, rHatch, bMtf);
166  }
167  else
168  {
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;
174  Size aInc;
175 
176  // Single hatch
177  aRect.AdjustLeft( -nLogPixelWidth ); aRect.AdjustTop( -nLogPixelWidth ); aRect.AdjustRight(nLogPixelWidth ); aRect.AdjustBottom(nLogPixelWidth );
178  CalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 );
179  do
180  {
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() );
184  }
185  while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
186 
187  if( ( rHatch.GetStyle() == HatchStyle::Double ) || ( rHatch.GetStyle() == HatchStyle::Triple ) )
188  {
189  // Double hatch
190  CalcHatchValues( aRect, nWidth, rHatch.GetAngle() + Degree10(900), aPt1, aPt2, aInc, aEndPt1 );
191  do
192  {
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() );
196  }
197  while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
198 
199  if( rHatch.GetStyle() == HatchStyle::Triple )
200  {
201  // Triple hatch
202  CalcHatchValues( aRect, nWidth, rHatch.GetAngle() + Degree10(450), aPt1, aPt2, aInc, aEndPt1 );
203  do
204  {
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() );
208  }
209  while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
210  }
211  }
212  }
213 }
214 
216  Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 )
217 {
218  Point aRef;
219  Degree10 nAngle = nAngle10 % Degree10(1800);
220  tools::Long nOffset = 0;
221 
222  if( nAngle > Degree10(900) )
223  nAngle -= Degree10(1800);
224 
225  aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() );
226 
227  if( Degree10(0) == nAngle )
228  {
229  rInc = Size( 0, nDist );
230  rPt1 = rRect.TopLeft();
231  rPt2 = rRect.TopRight();
232  rEndPt1 = rRect.BottomLeft();
233 
234  if( aRef.Y() <= rRect.Top() )
235  nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist );
236  else
237  nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) );
238 
239  rPt1.AdjustY( -nOffset );
240  rPt2.AdjustY( -nOffset );
241  }
242  else if( Degree10(900) == nAngle )
243  {
244  rInc = Size( nDist, 0 );
245  rPt1 = rRect.TopLeft();
246  rPt2 = rRect.BottomLeft();
247  rEndPt1 = rRect.TopRight();
248 
249  if( aRef.X() <= rRect.Left() )
250  nOffset = ( rRect.Left() - aRef.X() ) % nDist;
251  else
252  nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist );
253 
254  rPt1.AdjustX( -nOffset );
255  rPt2.AdjustX( -nOffset );
256  }
257  else if( nAngle >= Degree10(-450) && nAngle <= Degree10(450) )
258  {
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 );
262  tools::Long nPY;
263 
264  nDist = FRound( nDist / cos( fAngle ) );
265  rInc = Size( 0, nDist );
266 
267  if( nAngle > Degree10(0) )
268  {
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 ) );
273  }
274  else
275  {
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 ) );
280  }
281 
282  if( nPY <= rPt1.Y() )
283  nOffset = ( rPt1.Y() - nPY ) % nDist;
284  else
285  nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist );
286 
287  rPt1.AdjustY( -nOffset );
288  rPt2.AdjustY( -nOffset );
289  }
290  else
291  {
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 );
295  tools::Long nPX;
296 
297  nDist = FRound( nDist / sin( fAngle ) );
298  rInc = Size( nDist, 0 );
299 
300  if( nAngle > Degree10(0) )
301  {
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 ) );
306  }
307  else
308  {
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 ) );
313  }
314 
315  if( nPX <= rPt1.X() )
316  nOffset = ( rPt1.X() - nPX ) % nDist;
317  else
318  nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist );
319 
320  rPt1.AdjustX( -nOffset );
321  rPt2.AdjustX( -nOffset );
322  }
323 }
324 
325 void OutputDevice::DrawHatchLine( const tools::Line& rLine, const tools::PolyPolygon& rPolyPoly,
326  Point* pPtBuffer, bool bMtf )
327 {
329 
330  double fX, fY;
331  tools::Long nAdd, nPCounter = 0;
332 
333  for( tools::Long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ )
334  {
335  const tools::Polygon& rPoly = rPolyPoly[ static_cast<sal_uInt16>(nPoly) ];
336 
337  if( rPoly.GetSize() > 1 )
338  {
339  tools::Line aCurSegment( rPoly[ 0 ], Point() );
340 
341  for( tools::Long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ )
342  {
343  aCurSegment.SetEnd( rPoly[ static_cast<sal_uInt16>( i % nCount ) ] );
344  nAdd = 0;
345 
346  if( rLine.Intersection( aCurSegment, fX, fY ) )
347  {
348  if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) &&
349  ( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) )
350  {
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() );
354 
355  if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) ||
356  ( fPrevDistance > 0.0 && fCurDistance < 0.0 ) )
357  {
358  nAdd = 1;
359  }
360  }
361  else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) &&
362  ( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) )
363  {
364  const tools::Line aNextSegment( aCurSegment.GetEnd(), rPoly[ static_cast<sal_uInt16>( ( i + 1 ) % nCount ) ] );
365 
366  if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) &&
367  ( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) )
368  {
369  nAdd = 1;
370  }
371  }
372  else
373  nAdd = 1;
374 
375  if( nAdd )
376  pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) );
377  }
378 
379  aCurSegment.SetStart( aCurSegment.GetEnd() );
380  }
381  }
382  }
383 
384  if( nPCounter <= 1 )
385  return;
386 
387  qsort( pPtBuffer, nPCounter, sizeof( Point ), HatchCmpFnc );
388 
389  if( nPCounter & 1 )
390  nPCounter--;
391 
392  if( bMtf )
393  {
394  for( tools::Long i = 0; i < nPCounter; i += 2 )
395  mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) );
396  }
397  else
398  {
399  for( tools::Long i = 0; i < nPCounter; i += 2 )
400  DrawHatchLine_DrawLine(pPtBuffer[i], pPtBuffer[i+1]);
401  }
402 }
403 
404 void OutputDevice::DrawHatchLine_DrawLine(const Point& rStartPoint, const Point& rEndPoint)
405 {
406  Point aPt1{ImplLogicToDevicePixel(rStartPoint)}, aPt2{ImplLogicToDevicePixel(rEndPoint)};
407  mpGraphics->DrawLine(aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this);
408 }
409 
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt16 Count() const
Point TopLeft() const
bool Intersection(const tools::Line &rLine, double &rIntersectionX, double &rIntersectionY) const
void SetDistance(tools::Long nDistance)
Definition: gdi/hatch.cxx:69
Definition: hatch.hxx:46
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:636
const Point & GetRefPoint() const
Definition: outdev.hxx:433
Point BottomLeft() const
SAL_DLLPRIVATE void CalcHatchValues(const tools::Rectangle &rRect, tools::Long nDist, Degree10 nAngle10, Point &rPt1, Point &rPt2, Size &rInc, Point &rEndPt1)
DrawModeFlags mnDrawMode
Definition: outdev.hxx:353
long Long
const StyleSettings & GetStyleSettings() const
#define F_PI1800
bool mbOutputClipped
Definition: outdev.hxx:378
SAL_DLLPRIVATE bool is_double_buffered_window() const
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:538
bool mbMap
Definition: outdev.hxx:373
SAL_DLLPRIVATE void InitLineColor()
tools::Long Left() const
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
#define X
Definition: field.cxx:977
int nCount
tools::Long Bottom() const
const Point & GetEnd() const
HatchStyle GetStyle() const
Definition: hatch.hxx:59
static int HatchCmpFnc(const void *p1, const void *p2)
void AdaptiveSubdivide(tools::PolyPolygon &rResult) const
#define HATCH_MAXPOINTS
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:314
void Optimize(PolyOptimizeFlags nOptimizeFlags)
void SetLineColor()
UNDERLYING_TYPE get() const
int i
uno_Any a
tools::Long FRound(double fVal)
SAL_DLLPRIVATE void DrawHatchLine(const tools::Line &rLine, const tools::PolyPolygon &rPolyPoly, Point *pPtBuffer, bool bMtf)
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
const Color & GetColor() const
Definition: hatch.hxx:62
void SetEnd(const Point &rEndPt)
const Color & GetFontColor() const
SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel(tools::Long nWidth) const
Convert a logical width to a width in units of device pixels.
Definition: map.cxx:344
sal_uInt16 GetSize() const
const AllSettings & GetSettings() const
Definition: outdev.hxx:417
tools::Long Top() const
void DrawHatch(const tools::PolyPolygon &rPolyPoly, const Hatch &rHatch)
#define Y
virtual void DrawHatchLine_DrawLine(const Point &rStartPoint, const Point &rEndPoint)
Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:828
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:329
void SetStart(const Point &rStartPt)
bool IsRefPoint() const
Definition: outdev.hxx:434
unsigned char sal_uInt8
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:566
bool mbInitClipRegion
Definition: outdev.hxx:385
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
virtual void InitClipRegion()
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
void AddHatchActions(const tools::PolyPolygon &rPolyPoly, const Hatch &rHatch, GDIMetaFile &rMtf)
tools::Long GetDistance() const
Definition: hatch.hxx:65
void DrawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2, const OutputDevice *pOutDev)
SAL_DLLPRIVATE tools::Rectangle ImplLogicToDevicePixel(const tools::Rectangle &rLogicRect) const
Convert a logical rectangle to a rectangle in physical device pixel units.
Definition: map.cxx:406
tools::Rectangle GetBoundRect() const
double GetDistance(const double &rPtX, const double &rPtY) const
Degree10 GetAngle() const
Definition: hatch.hxx:68
const Point & GetStart() const
SAL_DLLPRIVATE tools::Long ImplDevicePixelToLogicWidth(tools::Long nWidth) const
Convert device pixels to a width in logical units.
Definition: map.cxx:368
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:600
void Push(PushFlags nFlags=PushFlags::ALL)
Definition: outdevstate.cxx:60
void SetColor(const Color &rColor)
Definition: gdi/hatch.cxx:64
Point TopRight() const
tools::Long Right() const
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:317