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