KiCad PCB EDA Suite
cairo_gal.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KICAD, a free EDA CAD application.
3  *
4  * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5  * Copyright (C) 2012-2021 Kicad Developers, see AUTHORS.txt for contributors.
6  * Copyright (C) 2017-2018 CERN
7  *
8  * @author Maciej Suminski <maciej.suminski@cern.ch>
9  *
10  * CairoGal - Graphics Abstraction Layer for Cairo
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, you may find one here:
24  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
25  * or you may search the http://www.gnu.org website for the version 2 license,
26  * or you may write to the Free Software Foundation, Inc.,
27  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29 
30 #include <wx/image.h>
31 #include <wx/log.h>
32 
33 #include <gal/cairo/cairo_gal.h>
35 #include <gal/definitions.h>
37 #include <math/util.h> // for KiROUND
38 #include <bitmap_base.h>
39 
40 #include <algorithm>
41 #include <cmath>
42 #include <limits>
43 
44 #include <pixman.h>
45 
46 using namespace KIGFX;
47 
48 
49 CAIRO_GAL_BASE::CAIRO_GAL_BASE( GAL_DISPLAY_OPTIONS& aDisplayOptions ) : GAL( aDisplayOptions )
50 {
51  // Initialise grouping
52  m_isGrouping = false;
53  m_isElementAdded = false;
54  m_groupCounter = 0;
55  m_currentGroup = nullptr;
56 
57  m_lineWidth = 1.0;
58  m_lineWidthInPixels = 1.0;
59  m_lineWidthIsOdd = true;
60 
61  // Initialise Cairo state
62  cairo_matrix_init_identity( &m_cairoWorldScreenMatrix );
63  m_currentContext = nullptr;
64  m_context = nullptr;
65  m_surface = nullptr;
66 
67  // Grid color settings are different in Cairo and OpenGL
68  SetGridColor( COLOR4D( 0.1, 0.1, 0.1, 0.8 ) );
70 
71  // Avoid uninitialized variables:
72  cairo_matrix_init_identity( &m_currentXform );
73  cairo_matrix_init_identity( &m_currentWorld2Screen );
74 }
75 
76 
78 {
79  ClearCache();
80 
81  if( m_surface )
82  cairo_surface_destroy( m_surface );
83 
84  if( m_context )
85  cairo_destroy( m_context );
86 
87  for( _cairo_surface* imageSurface : m_imageSurfaces )
88  cairo_surface_destroy( imageSurface );
89 }
90 
91 
93 {
94  resetContext();
95 }
96 
97 
99 {
100  // Force remaining objects to be drawn
101  Flush();
102 }
103 
104 
106 {
107  cairo_matrix_multiply( &m_currentWorld2Screen, &m_currentXform, &m_cairoWorldScreenMatrix );
108 }
109 
110 
111 const VECTOR2D CAIRO_GAL_BASE::xform( double x, double y )
112 {
113  VECTOR2D rv;
114 
117  return rv;
118 }
119 
120 
122 {
123  return xform( aP.x, aP.y );
124 }
125 
126 
127 const double CAIRO_GAL_BASE::angle_xform( const double aAngle )
128 {
129  // calculate rotation angle due to the rotation transform
130  // and if flipped on X axis.
131  double world_rotation = -std::atan2( m_currentWorld2Screen.xy, m_currentWorld2Screen.xx );
132 
133  // When flipped on X axis, the rotation angle is M_PI - initial angle:
134  if( IsFlippedX() )
135  world_rotation = M_PI - world_rotation;
136 
137  return std::fmod( aAngle + world_rotation, 2.0 * M_PI );
138 }
139 
140 
141 void CAIRO_GAL_BASE::arc_angles_xform_and_normalize( double& aStartAngle, double& aEndAngle )
142 {
143  double startAngle = aStartAngle;
144  double endAngle = aEndAngle;
145 
146  // When the view is flipped, the coordinates are flipped by the matrix transform
147  // However, arc angles need to be "flipped": the flipped angle is M_PI - initial angle.
148  if( IsFlippedX() )
149  {
150  startAngle = M_PI - startAngle;
151  endAngle = M_PI - endAngle;
152  }
153 
154  // Normalize arc angles
155  SWAP( startAngle, >, endAngle );
156 
157  // now rotate arc according to the rotation transform matrix
158  // Remark:
159  // We call angle_xform() to calculate angles according to the flip/rotation
160  // transform and normalize between -2M_PI and +2M_PI.
161  // Therefore, if aStartAngle = aEndAngle + 2*n*M_PI, the transform gives
162  // aEndAngle = aStartAngle
163  // So, if this is the case, force the aEndAngle value to draw a circle.
164  aStartAngle = angle_xform( startAngle );
165 
166  if( std::abs( aEndAngle - aStartAngle ) >= 2 * M_PI ) // arc is a full circle
167  aEndAngle = aStartAngle + 2 * M_PI;
168  else
169  aEndAngle = angle_xform( endAngle );
170 }
171 
172 
173 const double CAIRO_GAL_BASE::xform( double x )
174 {
175  double dx = m_currentWorld2Screen.xx * x;
176  double dy = m_currentWorld2Screen.yx * x;
177  return sqrt( dx * dx + dy * dy );
178 }
179 
180 
181 static double roundp( double x )
182 {
183  return floor( x + 0.5 ) + 0.5;
184 }
185 
186 
188 {
190  return VECTOR2D( ::roundp( v.x ), ::roundp( v.y ) );
191  else
192  return VECTOR2D( floor( v.x + 0.5 ), floor( v.y + 0.5 ) );
193 }
194 
195 
196 void CAIRO_GAL_BASE::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
197 {
198  syncLineWidth();
199 
200  VECTOR2D p0 = roundp( xform( aStartPoint ) );
201  VECTOR2D p1 = roundp( xform( aEndPoint ) );
202 
203  cairo_move_to( m_currentContext, p0.x, p0.y );
204  cairo_line_to( m_currentContext, p1.x, p1.y );
205  flushPath();
206  m_isElementAdded = true;
207 }
208 
209 
210 void CAIRO_GAL_BASE::syncLineWidth( bool aForceWidth, double aWidth )
211 {
212  double w = floor( xform( aForceWidth ? aWidth : m_lineWidth ) + 0.5 );
213 
214  if( w <= 1.0 )
215  {
216  w = 1.0;
217  cairo_set_line_join( m_currentContext, CAIRO_LINE_JOIN_MITER );
218  cairo_set_line_cap( m_currentContext, CAIRO_LINE_CAP_BUTT );
219  cairo_set_line_width( m_currentContext, 1.0 );
220  m_lineWidthIsOdd = true;
221  }
222  else
223  {
224  cairo_set_line_join( m_currentContext, CAIRO_LINE_JOIN_ROUND );
225  cairo_set_line_cap( m_currentContext, CAIRO_LINE_CAP_ROUND );
226  cairo_set_line_width( m_currentContext, w );
227  m_lineWidthIsOdd = ( (int) w % 2 ) == 1;
228  }
229 
231 }
232 
233 
234 void CAIRO_GAL_BASE::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
235  double aWidth )
236 {
237  if( m_isFillEnabled )
238  {
239  syncLineWidth( true, aWidth );
240 
241  VECTOR2D p0 = roundp( xform( aStartPoint ) );
242  VECTOR2D p1 = roundp( xform( aEndPoint ) );
243 
244  cairo_move_to( m_currentContext, p0.x, p0.y );
245  cairo_line_to( m_currentContext, p1.x, p1.y );
246  cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
247  m_fillColor.a );
248  cairo_stroke( m_currentContext );
249  }
250  else
251  {
252  aWidth /= 2.0;
253  SetLineWidth( 1.0 );
254  syncLineWidth();
255 
256  // Outline mode for tracks
257  VECTOR2D startEndVector = aEndPoint - aStartPoint;
258  double lineAngle = atan2( startEndVector.y, startEndVector.x );
259 
260  double sa = sin( lineAngle + M_PI / 2.0 );
261  double ca = cos( lineAngle + M_PI / 2.0 );
262 
263  auto pa0 = xform( aStartPoint + VECTOR2D( aWidth * ca, aWidth * sa ) );
264  auto pa1 = xform( aStartPoint - VECTOR2D( aWidth * ca, aWidth * sa ) );
265  auto pb0 = xform( aEndPoint + VECTOR2D( aWidth * ca, aWidth * sa ) );
266  auto pb1 = xform( aEndPoint - VECTOR2D( aWidth * ca, aWidth * sa ) );
267  auto pa = xform( aStartPoint );
268  auto pb = xform( aEndPoint );
269  auto rb = ( pa0 - pa ).EuclideanNorm();
270 
271  cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
272  m_strokeColor.a );
273 
274  cairo_move_to( m_currentContext, pa0.x, pa0.y );
275  cairo_line_to( m_currentContext, pb0.x, pb0.y );
276 
277  cairo_move_to( m_currentContext, pa1.x, pa1.y );
278  cairo_line_to( m_currentContext, pb1.x, pb1.y );
279 
280  cairo_arc( m_currentContext, pb.x, pb.y, rb, lineAngle - M_PI / 2.0,
281  lineAngle + M_PI / 2.0 );
282  cairo_arc( m_currentContext, pa.x, pa.y, rb, lineAngle + M_PI / 2.0,
283  lineAngle + 3.0 * M_PI / 2.0 );
284 
285  flushPath();
286  }
287 
288  m_isElementAdded = true;
289 }
290 
291 
292 void CAIRO_GAL_BASE::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
293 {
294  syncLineWidth();
295 
296  VECTOR2D c = roundp( xform( aCenterPoint ) );
297  double r = ::roundp( xform( aRadius ) );
298 
299  cairo_set_line_width( m_currentContext, std::min( 2.0 * r, m_lineWidthInPixels ) );
300  cairo_new_sub_path( m_currentContext );
301  cairo_arc( m_currentContext, c.x, c.y, r, 0.0, 2 * M_PI );
302  cairo_close_path( m_currentContext );
303  flushPath();
304  m_isElementAdded = true;
305 }
306 
307 
308 void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
309  double aEndAngle )
310 {
311  syncLineWidth();
312 
313  // calculate start and end arc angles according to the rotation transform matrix
314  // and normalize:
315  arc_angles_xform_and_normalize( aStartAngle, aEndAngle );
316 
317  double r = xform( aRadius );
318 
319  // N.B. This is backwards. We set this because we want to adjust the center
320  // point that changes both endpoints. In the worst case, this is twice as far.
321  // We cannot adjust radius or center based on the other because this causes the
322  // whole arc to change position/size
323  m_lineWidthIsOdd = !( static_cast<int>( aRadius ) % 2 );
324 
325  auto mid = roundp( xform( aCenterPoint ) );
326 
327  cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
328  cairo_new_sub_path( m_currentContext );
329 
330  if( m_isFillEnabled )
331  cairo_move_to( m_currentContext, mid.x, mid.y );
332 
333  cairo_arc( m_currentContext, mid.x, mid.y, r, aStartAngle, aEndAngle );
334 
335  if( m_isFillEnabled )
336  cairo_close_path( m_currentContext );
337 
338  flushPath();
339 
340  m_isElementAdded = true;
341 }
342 
343 
344 void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
345  double aStartAngle, double aEndAngle, double aWidth )
346 {
347  if( m_isFillEnabled )
348  {
349  m_lineWidth = aWidth;
350  m_isStrokeEnabled = true;
351  m_isFillEnabled = false;
352  DrawArc( aCenterPoint, aRadius, aStartAngle, aEndAngle );
353  m_isFillEnabled = true;
354  m_isStrokeEnabled = false;
355  return;
356  }
357 
358  syncLineWidth();
359 
360  // calculate start and end arc angles according to the rotation transform matrix
361  // and normalize:
362  double startAngleS = aStartAngle;
363  double endAngleS = aEndAngle;
364  arc_angles_xform_and_normalize( startAngleS, endAngleS );
365 
366  double r = xform( aRadius );
367 
368  // N.B. This is backwards. We set this because we want to adjust the center
369  // point that changes both endpoints. In the worst case, this is twice as far.
370  // We cannot adjust radius or center based on the other because this causes the
371  // whole arc to change position/size
372  m_lineWidthIsOdd = !( static_cast<int>( aRadius ) % 2 );
373 
374  VECTOR2D mid = roundp( xform( aCenterPoint ) );
375  double width = xform( aWidth / 2.0 );
376  VECTOR2D startPointS = VECTOR2D( r, 0.0 ).Rotate( startAngleS );
377  VECTOR2D endPointS = VECTOR2D( r, 0.0 ).Rotate( endAngleS );
378 
379  cairo_save( m_currentContext );
380 
381  cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
382  m_strokeColor.a );
383 
384  cairo_translate( m_currentContext, mid.x, mid.y );
385 
386  cairo_new_sub_path( m_currentContext );
387  cairo_arc( m_currentContext, 0, 0, r - width, startAngleS, endAngleS );
388 
389  cairo_new_sub_path( m_currentContext );
390  cairo_arc( m_currentContext, 0, 0, r + width, startAngleS, endAngleS );
391 
392  cairo_new_sub_path( m_currentContext );
393  cairo_arc_negative( m_currentContext, startPointS.x, startPointS.y, width, startAngleS,
394  startAngleS + M_PI );
395 
396  cairo_new_sub_path( m_currentContext );
397  cairo_arc( m_currentContext, endPointS.x, endPointS.y, width, endAngleS, endAngleS + M_PI );
398 
399  cairo_restore( m_currentContext );
400  flushPath();
401 
402  m_isElementAdded = true;
403 }
404 
405 
406 void CAIRO_GAL_BASE::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
407 {
408  // Calculate the diagonal points
409  syncLineWidth();
410 
411  const VECTOR2D p0 = roundp( xform( aStartPoint ) );
412  const VECTOR2D p1 = roundp( xform( VECTOR2D( aEndPoint.x, aStartPoint.y ) ) );
413  const VECTOR2D p2 = roundp( xform( aEndPoint ) );
414  const VECTOR2D p3 = roundp( xform( VECTOR2D( aStartPoint.x, aEndPoint.y ) ) );
415 
416  // The path is composed from 4 segments
417  cairo_move_to( m_currentContext, p0.x, p0.y );
418  cairo_line_to( m_currentContext, p1.x, p1.y );
419  cairo_line_to( m_currentContext, p2.x, p2.y );
420  cairo_line_to( m_currentContext, p3.x, p3.y );
421  cairo_close_path( m_currentContext );
422  flushPath();
423 
424  m_isElementAdded = true;
425 }
426 
427 
428 void CAIRO_GAL_BASE::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
429 {
430  for( int i = 0; i < aPolySet.OutlineCount(); ++i )
431  drawPoly( aPolySet.COutline( i ) );
432 }
433 
434 
436 {
437  drawPoly( aPolygon );
438 }
439 
440 
441 void CAIRO_GAL_BASE::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
442  const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
443  double aFilterValue )
444 {
445  // Note: aFilterValue is not used because the cubic Bezier curve is
446  // supported by Cairo.
447  syncLineWidth();
448 
449  const VECTOR2D sp = roundp( xform( aStartPoint ) );
450  const VECTOR2D cpa = roundp( xform( aControlPointA ) );
451  const VECTOR2D cpb = roundp( xform( aControlPointB ) );
452  const VECTOR2D ep = roundp( xform( aEndPoint ) );
453 
454  cairo_move_to( m_currentContext, sp.x, sp.y );
455  cairo_curve_to( m_currentContext, cpa.x, cpa.y, cpb.x, cpb.y, ep.x, ep.y );
456  cairo_line_to( m_currentContext, ep.x, ep.y );
457 
458  flushPath();
459  m_isElementAdded = true;
460 }
461 
462 
464 {
465  cairo_save( m_currentContext );
466 
467  // We have to calculate the pixel size in users units to draw the image.
468  // m_worldUnitLength is a factor used for converting IU to inches
469  double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
470 
471  // The position of the bitmap is the bitmap center.
472  // move the draw origin to the top left bitmap corner:
473  int w = aBitmap.GetSizePixels().x;
474  int h = aBitmap.GetSizePixels().y;
475 
476  cairo_set_matrix( m_currentContext, &m_currentWorld2Screen );
477  cairo_scale( m_currentContext, scale, scale );
478  cairo_translate( m_currentContext, -w / 2.0, -h / 2.0 );
479 
480  cairo_new_path( m_currentContext );
481  cairo_surface_t* image = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w, h );
482  cairo_surface_flush( image );
483 
484  unsigned char* pix_buffer = cairo_image_surface_get_data( image );
485 
486  // The pixel buffer of the initial bitmap:
487  const wxImage& bm_pix_buffer = *aBitmap.GetImageData();
488 
489  uint32_t mask_color = ( bm_pix_buffer.GetMaskRed() << 16 )
490  + ( bm_pix_buffer.GetMaskGreen() << 8 ) + ( bm_pix_buffer.GetMaskBlue() );
491 
492  // Copy the source bitmap to the cairo bitmap buffer.
493  // In cairo bitmap buffer, a ARGB32 bitmap is an ARGB pixel packed into a uint_32
494  // 24 low bits only are used for color, top 8 are transparency.
495  for( int row = 0; row < h; row++ )
496  {
497  for( int col = 0; col < w; col++ )
498  {
499  // Build the RGB24 pixel:
500  uint32_t pixel = bm_pix_buffer.GetRed( col, row ) << 16;
501  pixel += bm_pix_buffer.GetGreen( col, row ) << 8;
502  pixel += bm_pix_buffer.GetBlue( col, row );
503 
504  if( bm_pix_buffer.HasAlpha() )
505  pixel += bm_pix_buffer.GetAlpha( col, row ) << 24;
506  else if( bm_pix_buffer.HasMask() && pixel == mask_color )
507  pixel += ( wxALPHA_TRANSPARENT << 24 );
508  else
509  pixel += ( wxALPHA_OPAQUE << 24 );
510 
511  // Write the pixel to the cairo image buffer:
512  uint32_t* pix_ptr = (uint32_t*) pix_buffer;
513  *pix_ptr = pixel;
514  pix_buffer += 4;
515  }
516  }
517 
518  cairo_surface_mark_dirty( image );
519  cairo_set_source_surface( m_currentContext, image, 0, 0 );
520  cairo_paint( m_currentContext );
521 
522  // store the image handle so it can be destroyed later
523  m_imageSurfaces.push_back( image );
524 
525  m_isElementAdded = true;
526 
527  cairo_restore( m_currentContext );
528 }
529 
530 
531 void CAIRO_GAL_BASE::ResizeScreen( int aWidth, int aHeight )
532 {
533  m_screenSize = VECTOR2I( aWidth, aHeight );
534 }
535 
536 
538 {
539  storePath();
540 }
541 
542 
544 {
545  cairo_set_source_rgb( m_currentContext, m_clearColor.r, m_clearColor.g, m_clearColor.b );
546  cairo_rectangle( m_currentContext, 0.0, 0.0, m_screenSize.x, m_screenSize.y );
547  cairo_fill( m_currentContext );
548 }
549 
550 
551 void CAIRO_GAL_BASE::SetIsFill( bool aIsFillEnabled )
552 {
553  storePath();
554  m_isFillEnabled = aIsFillEnabled;
555 
556  if( m_isGrouping )
557  {
558  GROUP_ELEMENT groupElement;
559  groupElement.m_Command = CMD_SET_FILL;
560  groupElement.m_Argument.BoolArg = aIsFillEnabled;
561  m_currentGroup->push_back( groupElement );
562  }
563 }
564 
565 
566 void CAIRO_GAL_BASE::SetIsStroke( bool aIsStrokeEnabled )
567 {
568  storePath();
569  m_isStrokeEnabled = aIsStrokeEnabled;
570 
571  if( m_isGrouping )
572  {
573  GROUP_ELEMENT groupElement;
574  groupElement.m_Command = CMD_SET_STROKE;
575  groupElement.m_Argument.BoolArg = aIsStrokeEnabled;
576  m_currentGroup->push_back( groupElement );
577  }
578 }
579 
580 
582 {
583  storePath();
584  m_strokeColor = aColor;
585 
586  if( m_isGrouping )
587  {
588  GROUP_ELEMENT groupElement;
589  groupElement.m_Command = CMD_SET_STROKECOLOR;
590  groupElement.m_Argument.DblArg[0] = m_strokeColor.r;
591  groupElement.m_Argument.DblArg[1] = m_strokeColor.g;
592  groupElement.m_Argument.DblArg[2] = m_strokeColor.b;
593  groupElement.m_Argument.DblArg[3] = m_strokeColor.a;
594  m_currentGroup->push_back( groupElement );
595  }
596 }
597 
598 
600 {
601  storePath();
602  m_fillColor = aColor;
603 
604  if( m_isGrouping )
605  {
606  GROUP_ELEMENT groupElement;
607  groupElement.m_Command = CMD_SET_FILLCOLOR;
608  groupElement.m_Argument.DblArg[0] = m_fillColor.r;
609  groupElement.m_Argument.DblArg[1] = m_fillColor.g;
610  groupElement.m_Argument.DblArg[2] = m_fillColor.b;
611  groupElement.m_Argument.DblArg[3] = m_fillColor.a;
612  m_currentGroup->push_back( groupElement );
613  }
614 }
615 
616 
617 void CAIRO_GAL_BASE::SetLineWidth( float aLineWidth )
618 {
619  storePath();
620  GAL::SetLineWidth( aLineWidth );
621 
622  if( m_isGrouping )
623  {
624  GROUP_ELEMENT groupElement;
625  groupElement.m_Command = CMD_SET_LINE_WIDTH;
626  groupElement.m_Argument.DblArg[0] = aLineWidth;
627  m_currentGroup->push_back( groupElement );
628  }
629  else
630  {
631  m_lineWidth = aLineWidth;
632  }
633 }
634 
635 
636 void CAIRO_GAL_BASE::SetLayerDepth( double aLayerDepth )
637 {
638  super::SetLayerDepth( aLayerDepth );
639  storePath();
640 }
641 
642 
643 void CAIRO_GAL_BASE::Transform( const MATRIX3x3D& aTransformation )
644 {
645  cairo_matrix_t cairoTransformation, newXform;
646 
647  cairo_matrix_init( &cairoTransformation, aTransformation.m_data[0][0],
648  aTransformation.m_data[1][0], aTransformation.m_data[0][1],
649  aTransformation.m_data[1][1], aTransformation.m_data[0][2],
650  aTransformation.m_data[1][2] );
651 
652  cairo_matrix_multiply( &newXform, &m_currentXform, &cairoTransformation );
653  m_currentXform = newXform;
655 }
656 
657 
658 void CAIRO_GAL_BASE::Rotate( double aAngle )
659 {
660  storePath();
661 
662  if( m_isGrouping )
663  {
664  GROUP_ELEMENT groupElement;
665  groupElement.m_Command = CMD_ROTATE;
666  groupElement.m_Argument.DblArg[0] = aAngle;
667  m_currentGroup->push_back( groupElement );
668  }
669  else
670  {
671  cairo_matrix_rotate( &m_currentXform, aAngle );
673  }
674 }
675 
676 
677 void CAIRO_GAL_BASE::Translate( const VECTOR2D& aTranslation )
678 {
679  storePath();
680 
681  if( m_isGrouping )
682  {
683  GROUP_ELEMENT groupElement;
684  groupElement.m_Command = CMD_TRANSLATE;
685  groupElement.m_Argument.DblArg[0] = aTranslation.x;
686  groupElement.m_Argument.DblArg[1] = aTranslation.y;
687  m_currentGroup->push_back( groupElement );
688  }
689  else
690  {
691  cairo_matrix_translate( &m_currentXform, aTranslation.x, aTranslation.y );
693  }
694 }
695 
696 
697 void CAIRO_GAL_BASE::Scale( const VECTOR2D& aScale )
698 {
699  storePath();
700 
701  if( m_isGrouping )
702  {
703  GROUP_ELEMENT groupElement;
704  groupElement.m_Command = CMD_SCALE;
705  groupElement.m_Argument.DblArg[0] = aScale.x;
706  groupElement.m_Argument.DblArg[1] = aScale.y;
707  m_currentGroup->push_back( groupElement );
708  }
709  else
710  {
711  cairo_matrix_scale( &m_currentXform, aScale.x, aScale.y );
713  }
714 }
715 
716 
718 {
719  storePath();
720 
721  if( m_isGrouping )
722  {
723  GROUP_ELEMENT groupElement;
724  groupElement.m_Command = CMD_SAVE;
725  m_currentGroup->push_back( groupElement );
726  }
727  else
728  {
729  m_xformStack.push_back( m_currentXform );
731  }
732 }
733 
734 
736 {
737  storePath();
738 
739  if( m_isGrouping )
740  {
741  GROUP_ELEMENT groupElement;
742  groupElement.m_Command = CMD_RESTORE;
743  m_currentGroup->push_back( groupElement );
744  }
745  else
746  {
747  if( !m_xformStack.empty() )
748  {
749  m_currentXform = m_xformStack.back();
750  m_xformStack.pop_back();
752  }
753  }
754 }
755 
756 
758 {
759  // If the grouping is started: the actual path is stored in the group, when
760  // a attribute was changed or when grouping stops with the end group method.
761  storePath();
762 
763  GROUP group;
764  int groupNumber = getNewGroupNumber();
765  m_groups.insert( std::make_pair( groupNumber, group ) );
766  m_currentGroup = &m_groups[groupNumber];
767  m_isGrouping = true;
768 
769  return groupNumber;
770 }
771 
772 
774 {
775  storePath();
776  m_isGrouping = false;
777 }
778 
779 
780 void CAIRO_GAL_BASE::DrawGroup( int aGroupNumber )
781 {
782  // This method implements a small Virtual Machine - all stored commands
783  // are executed; nested calling is also possible
784 
785  storePath();
786 
787  for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
788  {
789  switch( it->m_Command )
790  {
791  case CMD_SET_FILL:
792  m_isFillEnabled = it->m_Argument.BoolArg;
793  break;
794 
795  case CMD_SET_STROKE:
796  m_isStrokeEnabled = it->m_Argument.BoolArg;
797  break;
798 
799  case CMD_SET_FILLCOLOR:
800  m_fillColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
801  it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
802  break;
803 
804  case CMD_SET_STROKECOLOR:
805  m_strokeColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
806  it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
807  break;
808 
809  case CMD_SET_LINE_WIDTH:
810  {
811  // Make lines appear at least 1 pixel wide, no matter of zoom
812  double x = 1.0, y = 1.0;
813  cairo_device_to_user_distance( m_currentContext, &x, &y );
814  double minWidth = std::min( fabs( x ), fabs( y ) );
815  cairo_set_line_width( m_currentContext,
816  std::max( it->m_Argument.DblArg[0], minWidth ) );
817  break;
818  }
819 
820 
821  case CMD_STROKE_PATH:
822  cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
824  cairo_append_path( m_currentContext, it->m_CairoPath );
825  cairo_stroke( m_currentContext );
826  break;
827 
828  case CMD_FILL_PATH:
829  cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
830  m_strokeColor.a );
831  cairo_append_path( m_currentContext, it->m_CairoPath );
832  cairo_fill( m_currentContext );
833  break;
834 
835  /*
836  case CMD_TRANSFORM:
837  cairo_matrix_t matrix;
838  cairo_matrix_init( &matrix, it->argument.DblArg[0], it->argument.DblArg[1],
839  it->argument.DblArg[2], it->argument.DblArg[3],
840  it->argument.DblArg[4], it->argument.DblArg[5] );
841  cairo_transform( m_currentContext, &matrix );
842  break;
843  */
844 
845  case CMD_ROTATE:
846  cairo_rotate( m_currentContext, it->m_Argument.DblArg[0] );
847  break;
848 
849  case CMD_TRANSLATE:
850  cairo_translate( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
851  break;
852 
853  case CMD_SCALE:
854  cairo_scale( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
855  break;
856 
857  case CMD_SAVE:
858  cairo_save( m_currentContext );
859  break;
860 
861  case CMD_RESTORE:
862  cairo_restore( m_currentContext );
863  break;
864 
865  case CMD_CALL_GROUP:
866  DrawGroup( it->m_Argument.IntArg );
867  break;
868  }
869  }
870 }
871 
872 
873 void CAIRO_GAL_BASE::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
874 {
875  storePath();
876 
877  for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
878  {
879  if( it->m_Command == CMD_SET_FILLCOLOR || it->m_Command == CMD_SET_STROKECOLOR )
880  {
881  it->m_Argument.DblArg[0] = aNewColor.r;
882  it->m_Argument.DblArg[1] = aNewColor.g;
883  it->m_Argument.DblArg[2] = aNewColor.b;
884  it->m_Argument.DblArg[3] = aNewColor.a;
885  }
886  }
887 }
888 
889 
890 void CAIRO_GAL_BASE::ChangeGroupDepth( int aGroupNumber, int aDepth )
891 {
892  // Cairo does not have any possibilities to change the depth coordinate of stored items,
893  // it depends only on the order of drawing
894 }
895 
896 
897 void CAIRO_GAL_BASE::DeleteGroup( int aGroupNumber )
898 {
899  storePath();
900 
901  // Delete the Cairo paths
902  std::deque<GROUP_ELEMENT>::iterator it, end;
903 
904  for( it = m_groups[aGroupNumber].begin(), end = m_groups[aGroupNumber].end(); it != end; ++it )
905  {
906  if( it->m_Command == CMD_FILL_PATH || it->m_Command == CMD_STROKE_PATH )
907  cairo_path_destroy( it->m_CairoPath );
908  }
909 
910  // Delete the group
911  m_groups.erase( aGroupNumber );
912 }
913 
914 
916 {
917  for( auto it = m_groups.begin(); it != m_groups.end(); )
918  DeleteGroup( ( it++ )->first );
919 }
920 
921 
923 {
924  cairo_set_operator( m_currentContext, aSetting ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_OVER );
925 }
926 
927 
928 void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition )
929 {
930  m_cursorPosition = aCursorPosition;
931 }
932 
933 
934 void CAIRO_GAL_BASE::EnableDepthTest( bool aEnabled )
935 {
936 }
937 
938 
940 {
941  for( _cairo_surface* imageSurface : m_imageSurfaces )
942  cairo_surface_destroy( imageSurface );
943 
944  m_imageSurfaces.clear();
945 
946  ClearScreen();
947 
948  // Compute the world <-> screen transformations
950 
951  cairo_matrix_init( &m_cairoWorldScreenMatrix, m_worldScreenMatrix.m_data[0][0],
954  m_worldScreenMatrix.m_data[1][2] );
955 
956  // we work in screen-space coordinates and do the transforms outside.
957  cairo_identity_matrix( m_context );
958 
959  cairo_matrix_init_identity( &m_currentXform );
960 
961  // Start drawing with a new path
962  cairo_new_path( m_context );
963  m_isElementAdded = true;
964 
966 
967  m_lineWidth = 0;
968 }
969 
970 
971 void CAIRO_GAL_BASE::drawAxes( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
972 {
973  syncLineWidth();
974 
975  VECTOR2D p0 = roundp( xform( aStartPoint ) );
976  VECTOR2D p1 = roundp( xform( aEndPoint ) );
977  VECTOR2D org = roundp( xform( VECTOR2D( 0.0, 0.0 ) ) ); // Axis origin = 0,0 coord
978 
979  cairo_set_source_rgba( m_currentContext, m_axesColor.r, m_axesColor.g, m_axesColor.b,
980  m_axesColor.a );
981  cairo_move_to( m_currentContext, p0.x, org.y );
982  cairo_line_to( m_currentContext, p1.x, org.y );
983  cairo_move_to( m_currentContext, org.x, p0.y );
984  cairo_line_to( m_currentContext, org.x, p1.y );
985  cairo_stroke( m_currentContext );
986 }
987 
988 
989 void CAIRO_GAL_BASE::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
990 {
991  syncLineWidth();
992  VECTOR2D p0 = roundp( xform( aStartPoint ) );
993  VECTOR2D p1 = roundp( xform( aEndPoint ) );
994 
995  cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
996  m_gridColor.a );
997  cairo_move_to( m_currentContext, p0.x, p0.y );
998  cairo_line_to( m_currentContext, p1.x, p1.y );
999  cairo_stroke( m_currentContext );
1000 }
1001 
1002 
1004 {
1005  syncLineWidth();
1006  VECTOR2D offset( 0, 0 );
1007  double size = 2.0 * m_lineWidthInPixels + 0.5;
1008 
1009  VECTOR2D p0 = roundp( xform( aPoint ) ) - VECTOR2D( size, 0 ) + offset;
1010  VECTOR2D p1 = roundp( xform( aPoint ) ) + VECTOR2D( size, 0 ) + offset;
1011  VECTOR2D p2 = roundp( xform( aPoint ) ) - VECTOR2D( 0, size ) + offset;
1012  VECTOR2D p3 = roundp( xform( aPoint ) ) + VECTOR2D( 0, size ) + offset;
1013 
1014  cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1015  m_gridColor.a );
1016  cairo_move_to( m_currentContext, p0.x, p0.y );
1017  cairo_line_to( m_currentContext, p1.x, p1.y );
1018  cairo_move_to( m_currentContext, p2.x, p2.y );
1019  cairo_line_to( m_currentContext, p3.x, p3.y );
1020  cairo_stroke( m_currentContext );
1021 }
1022 
1023 
1024 void CAIRO_GAL_BASE::drawGridPoint( const VECTOR2D& aPoint, double aWidth, double aHeight )
1025 {
1026  VECTOR2D p = roundp( xform( aPoint ) );
1027 
1028  double sw = std::max( 1.0, aWidth );
1029  double sh = std::max( 1.0, aHeight );
1030 
1031  cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1032  m_gridColor.a );
1033  cairo_rectangle( m_currentContext, p.x - std::floor( sw / 2 ) - 0.5,
1034  p.y - std::floor( sh / 2 ) - 0.5, sw, sh );
1035 
1036  cairo_fill( m_currentContext );
1037 }
1038 
1039 
1041 {
1042  if( m_isFillEnabled )
1043  {
1044  cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
1045  m_fillColor.a );
1046 
1047  if( m_isStrokeEnabled )
1048  cairo_fill_preserve( m_currentContext );
1049  else
1050  cairo_fill( m_currentContext );
1051  }
1052 
1053  if( m_isStrokeEnabled )
1054  {
1055  cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
1056  m_strokeColor.a );
1057  cairo_stroke( m_currentContext );
1058  }
1059 }
1060 
1061 
1063 {
1064  if( m_isElementAdded )
1065  {
1066  m_isElementAdded = false;
1067 
1068  if( !m_isGrouping )
1069  {
1070  if( m_isFillEnabled )
1071  {
1072  cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g,
1074  cairo_fill_preserve( m_currentContext );
1075  }
1076 
1077  if( m_isStrokeEnabled )
1078  {
1079  cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
1081  cairo_stroke_preserve( m_currentContext );
1082  }
1083  }
1084  else
1085  {
1086  // Copy the actual path, append it to the global path list
1087  // then check, if the path needs to be stroked/filled and
1088  // add this command to the group list;
1089  if( m_isStrokeEnabled )
1090  {
1091  GROUP_ELEMENT groupElement;
1092  groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1093  groupElement.m_Command = CMD_STROKE_PATH;
1094  m_currentGroup->push_back( groupElement );
1095  }
1096 
1097  if( m_isFillEnabled )
1098  {
1099  GROUP_ELEMENT groupElement;
1100  groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1101  groupElement.m_Command = CMD_FILL_PATH;
1102  m_currentGroup->push_back( groupElement );
1103  }
1104  }
1105 
1106  cairo_new_path( m_currentContext );
1107  }
1108 }
1109 
1110 
1111 void CAIRO_GAL_BASE::blitCursor( wxMemoryDC& clientDC )
1112 {
1113  if( !IsCursorEnabled() )
1114  return;
1115 
1117  const COLOR4D cColor = getCursorColor();
1118  const int cursorSize = m_fullscreenCursor ? 8000 : 80;
1119 
1120  wxColour color( cColor.r * cColor.a * 255, cColor.g * cColor.a * 255, cColor.b * cColor.a * 255,
1121  255 );
1122  clientDC.SetPen( wxPen( color ) );
1123  clientDC.DrawLine( p.x - cursorSize / 2, p.y, p.x + cursorSize / 2, p.y );
1124  clientDC.DrawLine( p.x, p.y - cursorSize / 2, p.x, p.y + cursorSize / 2 );
1125 }
1126 
1127 
1128 void CAIRO_GAL_BASE::drawPoly( const std::deque<VECTOR2D>& aPointList )
1129 {
1130  wxCHECK( aPointList.size() > 1, /* void */ );
1131 
1132  // Iterate over the point list and draw the segments
1133  std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
1134 
1135  syncLineWidth();
1136 
1137  const VECTOR2D p = roundp( xform( it->x, it->y ) );
1138 
1139  cairo_move_to( m_currentContext, p.x, p.y );
1140 
1141  for( ++it; it != aPointList.end(); ++it )
1142  {
1143  const VECTOR2D p2 = roundp( xform( it->x, it->y ) );
1144 
1145  cairo_line_to( m_currentContext, p2.x, p2.y );
1146  }
1147 
1148  flushPath();
1149  m_isElementAdded = true;
1150 }
1151 
1152 
1153 void CAIRO_GAL_BASE::drawPoly( const VECTOR2D aPointList[], int aListSize )
1154 {
1155  wxCHECK( aListSize > 1, /* void */ );
1156 
1157  // Iterate over the point list and draw the segments
1158  const VECTOR2D* ptr = aPointList;
1159 
1160  syncLineWidth();
1161 
1162  const VECTOR2D p = roundp( xform( ptr->x, ptr->y ) );
1163  cairo_move_to( m_currentContext, p.x, p.y );
1164 
1165  for( int i = 1; i < aListSize; ++i )
1166  {
1167  ++ptr;
1168  const VECTOR2D p2 = roundp( xform( ptr->x, ptr->y ) );
1169  cairo_line_to( m_currentContext, p2.x, p2.y );
1170  }
1171 
1172  flushPath();
1173  m_isElementAdded = true;
1174 }
1175 
1176 
1178 {
1179  wxCHECK( aLineChain.PointCount() > 1, /* void */ );
1180 
1181  syncLineWidth();
1182 
1183  auto numPoints = aLineChain.PointCount();
1184 
1185  if( aLineChain.IsClosed() )
1186  numPoints += 1;
1187 
1188  const VECTOR2I start = aLineChain.CPoint( 0 );
1189  const VECTOR2D p = roundp( xform( start.x, start.y ) );
1190  cairo_move_to( m_currentContext, p.x, p.y );
1191 
1192  for( int i = 1; i < numPoints; ++i )
1193  {
1194  const VECTOR2I& pw = aLineChain.CPoint( i );
1195  const VECTOR2D ps = roundp( xform( pw.x, pw.y ) );
1196  cairo_line_to( m_currentContext, ps.x, ps.y );
1197  }
1198 
1199  flushPath();
1200  m_isElementAdded = true;
1201 }
1202 
1203 
1205 {
1206  wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
1207  wxT( "There are no free slots to store a group" ) );
1208 
1209  while( m_groups.find( m_groupCounter ) != m_groups.end() )
1210  m_groupCounter++;
1211 
1212  return m_groupCounter++;
1213 }
1214 
1215 
1216 CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
1217  wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
1218  const wxString& aName ) :
1219  CAIRO_GAL_BASE( aDisplayOptions ),
1220  wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName )
1221 {
1222  // Initialise compositing state
1223  m_mainBuffer = 0;
1224  m_overlayBuffer = 0;
1225  m_validCompositor = false;
1227 
1228  m_bitmapBuffer = nullptr;
1229  m_wxOutput = nullptr;
1230 
1231  m_parentWindow = aParent;
1232  m_mouseListener = aMouseListener;
1233  m_paintListener = aPaintListener;
1234 
1235  // Connect the native cursor handler
1236  Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( CAIRO_GAL::onSetNativeCursor ), nullptr,
1237  this );
1238 
1239  // Connecting the event handlers
1240  Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) );
1241 
1242  // Mouse events are skipped to the parent
1243  Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1244  Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1245  Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1246  Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1247  Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1248  Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1249  Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1250  Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1251  Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1252  Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1253  Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1254 #if defined _WIN32 || defined _WIN64
1255  Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1256 #endif
1257 
1258  SetSize( aParent->GetClientSize() );
1259  m_screenSize = VECTOR2I( aParent->GetClientSize() );
1260 
1261  // Allocate memory for pixel storage
1262  allocateBitmaps();
1263 
1264  m_isInitialized = false;
1265 }
1266 
1267 
1269 {
1270  deleteBitmaps();
1271 }
1272 
1273 
1275 {
1276  initSurface();
1277 
1279 
1280  if( !m_validCompositor )
1281  setCompositor();
1282 
1283  m_compositor->SetMainContext( m_context );
1284  m_compositor->SetBuffer( m_mainBuffer );
1285 }
1286 
1287 
1289 {
1291 
1292  // Merge buffers on the screen
1293  m_compositor->DrawBuffer( m_mainBuffer );
1294  m_compositor->DrawBuffer( m_overlayBuffer );
1295 
1296  // Now translate the raw context data from the format stored
1297  // by cairo into a format understood by wxImage.
1298 
1299  pixman_image_t* dstImg = pixman_image_create_bits(
1300  wxPlatformInfo::Get().GetEndianness() == wxENDIAN_LITTLE ? PIXMAN_b8g8r8
1301  : PIXMAN_r8g8b8,
1302  m_screenSize.x, m_screenSize.y, (uint32_t*) m_wxOutput, m_wxBufferWidth * 3 );
1303  pixman_image_t* srcImg =
1304  pixman_image_create_bits( PIXMAN_a8r8g8b8, m_screenSize.x, m_screenSize.y,
1305  (uint32_t*) m_bitmapBuffer, m_wxBufferWidth * 4 );
1306 
1307  pixman_image_composite( PIXMAN_OP_SRC, srcImg, nullptr, dstImg, 0, 0, 0, 0, 0, 0,
1309 
1310  // Free allocated memory
1311  pixman_image_unref( srcImg );
1312  pixman_image_unref( dstImg );
1313 
1314  wxImage img( m_wxBufferWidth, m_screenSize.y, m_wxOutput, true );
1315  wxBitmap bmp( img );
1316  wxMemoryDC mdc( bmp );
1317  wxClientDC clientDC( this );
1318 
1319  // Now it is the time to blit the mouse cursor
1320  blitCursor( mdc );
1321  clientDC.Blit( 0, 0, m_screenSize.x, m_screenSize.y, &mdc, 0, 0, wxCOPY );
1322 
1323  deinitSurface();
1324 }
1325 
1326 
1327 void CAIRO_GAL::PostPaint( wxPaintEvent& aEvent )
1328 {
1329  // posts an event to m_paint_listener to ask for redraw the canvas.
1330  if( m_paintListener )
1331  wxPostEvent( m_paintListener, aEvent );
1332 }
1333 
1334 
1335 void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
1336 {
1337  CAIRO_GAL_BASE::ResizeScreen( aWidth, aHeight );
1338 
1339  // Recreate the bitmaps
1340  deleteBitmaps();
1341  allocateBitmaps();
1342 
1343  if( m_validCompositor )
1344  m_compositor->Resize( aWidth, aHeight );
1345 
1346  m_validCompositor = false;
1347 
1348  SetSize( wxSize( aWidth, aHeight ) );
1349 }
1350 
1351 
1352 bool CAIRO_GAL::Show( bool aShow )
1353 {
1354  bool s = wxWindow::Show( aShow );
1355 
1356  if( aShow )
1357  wxWindow::Raise();
1358 
1359  return s;
1360 }
1361 
1362 
1364 {
1365  initSurface();
1366  return CAIRO_GAL_BASE::BeginGroup();
1367 }
1368 
1369 
1371 {
1373  deinitSurface();
1374 }
1375 
1376 
1378 {
1379  // If the compositor is not set, that means that there is a recaching process going on
1380  // and we do not need the compositor now
1381  if( !m_validCompositor )
1382  return;
1383 
1384  // Cairo grouping prevents display of overlapping items on the same layer in the lighter color
1385  if( m_isInitialized )
1386  storePath();
1387 
1388  switch( aTarget )
1389  {
1390  default:
1391  case TARGET_CACHED:
1392  case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1393  case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1394  }
1395 
1396  m_currentTarget = aTarget;
1397 }
1398 
1399 
1401 {
1402  return m_currentTarget;
1403 }
1404 
1405 
1407 {
1408  // Save the current state
1409  unsigned int currentBuffer = m_compositor->GetBuffer();
1410 
1411  switch( aTarget )
1412  {
1413  // Cached and noncached items are rendered to the same buffer
1414  default:
1415  case TARGET_CACHED:
1416  case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1417  case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1418  }
1419 
1420  m_compositor->ClearBuffer( COLOR4D::BLACK );
1421 
1422  // Restore the previous state
1423  m_compositor->SetBuffer( currentBuffer );
1424 }
1425 
1426 
1428 {
1429  if( m_isInitialized )
1430  return;
1431 
1432  m_surface = cairo_image_surface_create_for_data( m_bitmapBuffer, GAL_FORMAT, m_wxBufferWidth,
1433  m_screenSize.y, m_stride );
1434 
1435  m_context = cairo_create( m_surface );
1436 
1437 #ifdef DEBUG
1438  cairo_status_t status = cairo_status( m_context );
1439  wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) );
1440 #endif /* DEBUG */
1441 
1443 
1444  m_isInitialized = true;
1445 }
1446 
1447 
1449 {
1450  if( !m_isInitialized )
1451  return;
1452 
1453  cairo_destroy( m_context );
1454  m_context = nullptr;
1455  cairo_surface_destroy( m_surface );
1456  m_surface = nullptr;
1457 
1458  m_isInitialized = false;
1459 }
1460 
1461 
1463 {
1465 
1466  while( ( ( m_wxBufferWidth * 3 ) % 4 ) != 0 )
1467  m_wxBufferWidth++;
1468 
1469  // Create buffer, use the system independent Cairo context backend
1470  m_stride = cairo_format_stride_for_width( GAL_FORMAT, m_wxBufferWidth );
1472 
1473  wxASSERT( m_bitmapBuffer == nullptr );
1474  m_bitmapBuffer = new unsigned char[m_bufferSize * 4];
1475 
1476  wxASSERT( m_wxOutput == nullptr );
1477  m_wxOutput = new unsigned char[m_wxBufferWidth * 3 * m_screenSize.y];
1478 }
1479 
1480 
1482 {
1483  delete[] m_bitmapBuffer;
1484  m_bitmapBuffer = nullptr;
1485 
1486  delete[] m_wxOutput;
1487  m_wxOutput = nullptr;
1488 }
1489 
1490 
1492 {
1493  // Recreate the compositor with the new Cairo context
1496  m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
1497 
1498  // Prepare buffers
1499  m_mainBuffer = m_compositor->CreateBuffer();
1500  m_overlayBuffer = m_compositor->CreateBuffer();
1501 
1502  m_validCompositor = true;
1503 }
1504 
1505 
1506 void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
1507 {
1508  PostPaint( aEvent );
1509 }
1510 
1511 
1512 void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1513 {
1514  // Post the mouse event to the event listener registered in constructor, if any
1515  if( m_mouseListener )
1516  wxPostEvent( m_mouseListener, aEvent );
1517 }
1518 
1519 
1521 {
1522  bool refresh = false;
1523 
1524  if( m_validCompositor &&
1525  aOptions.cairo_antialiasing_mode != m_compositor->GetAntialiasingMode() )
1526  {
1527  m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
1528  m_validCompositor = false;
1529  deinitSurface();
1530 
1531  refresh = true;
1532  }
1533 
1534  if( super::updatedGalDisplayOptions( aOptions ) )
1535  {
1536  Refresh();
1537  refresh = true;
1538  }
1539 
1540  return refresh;
1541 }
1542 
1543 
1545 {
1546  // Store the current cursor type and get the wxCursor for it
1547  if( !GAL::SetNativeCursorStyle( aCursor ) )
1548  return false;
1549 
1551 
1552  // Update the cursor in the wx control
1553  wxWindow::SetCursor( m_currentwxCursor );
1554 
1555  return true;
1556 }
1557 
1558 
1559 void CAIRO_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
1560 {
1561  aEvent.SetCursor( m_currentwxCursor );
1562 }
1563 
1564 
1566 {
1568 
1569  // Draw the grid
1570  // For the drawing the start points, end points and increments have
1571  // to be calculated in world coordinates
1572  VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1573  VECTOR2D worldEndPoint = m_screenWorldMatrix * VECTOR2D( m_screenSize );
1574 
1575  // Compute the line marker or point radius of the grid
1576  // Note: generic grids can't handle sub-pixel lines without
1577  // either losing fine/course distinction or having some dots
1578  // fail to render
1579  float marker = std::fmax( 1.0f, m_gridLineWidth ) / m_worldScale;
1580  float doubleMarker = 2.0f * marker;
1581 
1582  // Draw axes if desired
1583  if( m_axesEnabled )
1584  {
1585  SetLineWidth( marker );
1586  drawAxes( worldStartPoint, worldEndPoint );
1587  }
1588 
1589  if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1590  return;
1591 
1592  VECTOR2D gridScreenSize( m_gridSize );
1593 
1594  double gridThreshold = KiROUND( computeMinGridSpacing() / m_worldScale );
1595 
1597  gridThreshold *= 2.0;
1598 
1599  // If we cannot display the grid density, scale down by a tick size and
1600  // try again. Eventually, we get some representation of the grid
1601  while( std::min( gridScreenSize.x, gridScreenSize.y ) <= gridThreshold )
1602  {
1603  gridScreenSize = gridScreenSize * static_cast<double>( m_gridTick );
1604  }
1605 
1606  // Compute grid starting and ending indexes to draw grid points on the
1607  // visible screen area
1608  // Note: later any point coordinate will be offsetted by m_gridOrigin
1609  int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1610  int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1611  int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1612  int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1613 
1614  // Ensure start coordinate > end coordinate
1615 
1616  SWAP( gridStartX, >, gridEndX );
1617  SWAP( gridStartY, >, gridEndY );
1618 
1619  // Ensure the grid fills the screen
1620  --gridStartX;
1621  ++gridEndX;
1622  --gridStartY;
1623  ++gridEndY;
1624 
1625  // Draw the grid behind all other layers
1626  SetLayerDepth( m_depthRange.y * 0.75 );
1627 
1629  {
1630  // Now draw the grid, every coarse grid line gets the double width
1631 
1632  // Vertical lines
1633  for( int j = gridStartY; j <= gridEndY; j++ )
1634  {
1635  const double y = j * gridScreenSize.y + m_gridOrigin.y;
1636 
1637  if( m_axesEnabled && y == 0.0 )
1638  continue;
1639 
1640  SetLineWidth( ( j % m_gridTick ) ? marker : doubleMarker );
1641  drawGridLine( VECTOR2D( gridStartX * gridScreenSize.x + m_gridOrigin.x, y ),
1642  VECTOR2D( gridEndX * gridScreenSize.x + m_gridOrigin.x, y ) );
1643  }
1644 
1645  // Horizontal lines
1646  for( int i = gridStartX; i <= gridEndX; i++ )
1647  {
1648  const double x = i * gridScreenSize.x + m_gridOrigin.x;
1649 
1650  if( m_axesEnabled && x == 0.0 )
1651  continue;
1652 
1653  SetLineWidth( ( i % m_gridTick ) ? marker : doubleMarker );
1654  drawGridLine( VECTOR2D( x, gridStartY * gridScreenSize.y + m_gridOrigin.y ),
1655  VECTOR2D( x, gridEndY * gridScreenSize.y + m_gridOrigin.y ) );
1656  }
1657  }
1658  else // Dots or Crosses grid
1659  {
1660  m_lineWidthIsOdd = true;
1661  m_isStrokeEnabled = true;
1662  for( int j = gridStartY; j <= gridEndY; j++ )
1663  {
1664  bool tickY = ( j % m_gridTick == 0 );
1665 
1666  for( int i = gridStartX; i <= gridEndX; i++ )
1667  {
1668  bool tickX = ( i % m_gridTick == 0 );
1669  VECTOR2D pos{ i * gridScreenSize.x + m_gridOrigin.x,
1670  j * gridScreenSize.y + m_gridOrigin.y };
1671 
1673  {
1674  SetLineWidth( ( tickX && tickY ) ? doubleMarker : marker );
1675  drawGridCross( pos );
1676  }
1677  else if( m_gridStyle == GRID_STYLE::DOTS )
1678  {
1679  double doubleGridLineWidth = m_gridLineWidth * 2.0f;
1680  drawGridPoint( pos, ( tickX ) ? doubleGridLineWidth : m_gridLineWidth,
1681  ( tickY ) ? doubleGridLineWidth : m_gridLineWidth );
1682  }
1683  }
1684  }
1685  }
1686 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
void SetNegativeDrawMode(bool aSetting) override
Set negative draw mode in the renderer.
Definition: cairo_gal.cpp:922
wxWindow * m_parentWindow
Parent window.
Definition: cairo_gal.h:468
void initSurface()
Prepare Cairo surfaces for drawing.
Definition: cairo_gal.cpp:1427
unsigned char * m_wxOutput
wxImage compatible buffer
Definition: cairo_gal.h:472
void DrawBitmap(const BITMAP_BASE &aBitmap) override
Draw a bitmap image.
Definition: cairo_gal.cpp:463
std::map< int, GROUP > m_groups
List of graphic groups.
Definition: cairo_gal.h:329
Use lines for the grid.
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:531
bool m_isFillEnabled
Is filling of graphic objects enabled ?
CAIRO_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("CairoCanvas"))
Definition: cairo_gal.cpp:1216
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void EndGroup() override
End the group.
Definition: cairo_gal.cpp:773
void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Change the color used to draw the group.
Definition: cairo_gal.cpp:873
bool m_isInitialized
Are Cairo image & surface ready to use.
Definition: cairo_gal.h:478
double m_lineWidthInPixels
Definition: cairo_gal.h:333
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Handle updating display options.
Definition: cairo_gal.cpp:1520
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:191
bool m_axesEnabled
Should the axes be drawn.
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:1335
void deinitSurface()
Destroy Cairo surfaces when are not needed anymore.
Definition: cairo_gal.cpp:1448
void SetIsStroke(bool aIsStrokeEnabled) override
Enable/disable stroked outlines.
Definition: cairo_gal.cpp:566
double computeMinGridSpacing() const
compute minimum grid spacing from the grid settings
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: cairo_gal.h:464
MATRIX3x3D m_screenWorldMatrix
Screen transformation.
void EnableDepthTest(bool aEnabled=false) override
Definition: cairo_gal.cpp:934
void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Change the depth (Z-axis position) of the group.
Definition: cairo_gal.cpp:890
void onPaint(wxPaintEvent &aEvent)
Paint event handler.
Definition: cairo_gal.cpp:1506
std::deque< GROUP_ELEMENT > GROUP
A graphic group type definition.
Definition: cairo_gal.h:324
~CAIRO_GAL()
Return true if the GAL canvas is visible on the screen.
Definition: cairo_gal.cpp:1268
bool IsFlippedX() const
Return true if flip flag for the X axis is set.
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
Definition: cairo_gal.cpp:677
VECTOR2D ToScreen(const VECTOR2D &aPoint) const
Compute the point position in screen coordinates from given world coordinates.
CAIRO_GAL_BASE(GAL_DISPLAY_OPTIONS &aDisplayOptions)
Definition: cairo_gal.cpp:49
int color
Definition: DXF_plotter.cpp:60
void setCompositor()
Prepare the compositor.
Definition: cairo_gal.cpp:1491
void storePath()
Store the actual path.
Definition: cairo_gal.cpp:1062
VECTOR2D m_gridSize
The grid size.
wxImage * GetImageData()
Definition: bitmap_base.h:70
GROUP * m_currentGroup
Currently used group.
Definition: cairo_gal.h:331
Type definition for an graphics group element.
Definition: cairo_gal.h:313
static const COLOR4D BLACK
Definition: color4d.h:373
void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
Definition: cairo_gal.cpp:897
double g
Green component.
Definition: color4d.h:364
unsigned char * m_bitmapBuffer
Storage of the Cairo image.
Definition: cairo_gal.h:475
KICURSOR
Definition: cursors.h:33
union KIGFX::CAIRO_GAL_BASE::GROUP_ELEMENT::@26 m_Argument
void PostPaint(wxPaintEvent &aEvent)
Post an event to m_paint_listener.
Definition: cairo_gal.cpp:1327
void drawAxes(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Definition: cairo_gal.cpp:971
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
int PointCount() const
Return the number of points (vertices) in this line chain.
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
cairo_matrix_t m_cairoWorldScreenMatrix
Cairo world to screen transform matrix.
Definition: cairo_gal.h:336
Enable/disable filling.
Definition: cairo_gal.h:296
void SetLayerDepth(double aLayerDepth) override
Set the depth of the layer (position on the z-axis)
Definition: cairo_gal.cpp:636
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
unsigned int m_groupCounter
Counter used for generating group keys.
Definition: cairo_gal.h:330
int m_gridTick
Every tick line gets the double width.
unsigned int m_bufferSize
Size of buffers cairoOutput, bitmapBuffers.
Definition: cairo_gal.h:471
float m_gridLineWidth
Line width of the grid.
const double angle_xform(const double aAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix.
Definition: cairo_gal.cpp:127
MATRIX3x3D m_worldScreenMatrix
World transformation.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
double b
Blue component.
Definition: color4d.h:365
Class that handles multitarget rendering (ie.
Auxiliary rendering target (noncached)
Definition: definitions.h:49
T m_data[3][3]
Definition: matrix3x3.h:64
COLOR4D m_gridColor
Color of the grid.
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:51
void ClearCache() override
Delete all data created during caching of graphic items.
Definition: cairo_gal.cpp:915
double m_worldScale
The scale factor world->screen.
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: cairo_gal.cpp:292
CAIRO_ANTIALIASING_MODE cairo_antialiasing_mode
static double roundp(double x)
Definition: cairo_gal.cpp:181
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: cairo_gal.cpp:234
VECTOR2D m_cursorPosition
Current cursor position (world coordinates)
GAL_DISPLAY_OPTIONS & m_options
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
void drawGridPoint(const VECTOR2D &aPoint, double aWidth, double aHeight)
Definition: cairo_gal.cpp:1024
void ClearScreen() override
Clear the screen.
Definition: cairo_gal.cpp:543
bool m_isElementAdded
Was an graphic element added ?
Definition: cairo_gal.h:328
void SetGridColor(const COLOR4D &aGridColor)
Set the grid color.
GRAPHICS_COMMAND m_Command
Command to execute.
Definition: cairo_gal.h:315
double a
Alpha component.
Definition: color4d.h:366
bool m_isStrokeEnabled
Are the outlines stroked ?
COLOR4D m_strokeColor
The color of the outlines.
void ClearTarget(RENDER_TARGET aTarget) override
Clear the target for rendering.
Definition: cairo_gal.cpp:1406
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: cairo_gal.cpp:196
std::vector< cairo_matrix_t > m_xformStack
Definition: cairo_gal.h:346
VECTOR2< double > VECTOR2D
Definition: vector2d.h:622
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
bool IsClosed() const override
bool m_gridVisibility
Should the grid be shown.
Represent a set of closed polygons.
void endDrawing() override
Definition: cairo_gal.cpp:98
static const wxCursor GetCursor(KICURSOR aCursorType)
Definition: cursors.cpp:394
void SetAxesColor(const COLOR4D &aAxesColor)
Set the axes color.
void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, double aStartAngle, double aEndAngle, double aWidth) override
Draw an arc segment.
Definition: cairo_gal.cpp:344
void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
Definition: cairo_gal.h:101
wxCursor m_currentwxCursor
wxCursor showing the current native cursor
Definition: cairo_gal.h:480
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
cairo_t * m_currentContext
Currently used Cairo context for drawing.
Definition: cairo_gal.h:339
bool SetNativeCursorStyle(KICURSOR aCursor) override
Set the cursor in the native panel.
Definition: cairo_gal.cpp:1544
void updateWorldScreenMatrix()
Definition: cairo_gal.cpp:105
void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
Definition: cairo_gal.cpp:928
void onSetNativeCursor(wxSetCursorEvent &aEvent)
Give the correct cursor image when the native widget asks for it.
Definition: cairo_gal.cpp:1559
const double xform(double x)
Definition: cairo_gal.cpp:173
int m_stride
Stride value for Cairo.
Definition: cairo_gal.h:476
cairo_t * m_context
Cairo image.
Definition: cairo_gal.h:340
void SetTarget(RENDER_TARGET aTarget) override
Set the target for rendering.
Definition: cairo_gal.cpp:1377
void skipMouseEvent(wxMouseEvent &aEvent)
Mouse event handler, forwards the event to the child.
Definition: cairo_gal.cpp:1512
Use dots for the grid.
Use small cross instead of dots for the grid.
Save the transformation matrix.
Definition: cairo_gal.h:307
COLOR4D getCursorColor() const
Get the actual cursor color to draw.
void SetIsFill(bool aIsFillEnabled) override
Enable/disable fill.
Definition: cairo_gal.cpp:551
const VECTOR2D roundp(const VECTOR2D &v)
Definition: cairo_gal.cpp:187
virtual bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions)
Handle updating display options.
bool m_validCompositor
Compositor initialization flag.
Definition: cairo_gal.h:465
cairo_matrix_t m_currentWorld2Screen
Definition: cairo_gal.h:338
virtual bool SetNativeCursorStyle(KICURSOR aCursor)
Set the cursor in the native panel.
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, double aStartAngle, double aEndAngle) override
Draw an arc.
Definition: cairo_gal.cpp:308
void SetStrokeColor(const COLOR4D &aColor) override
Set the stroke color.
Definition: cairo_gal.cpp:581
void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
Definition: cairo_gal.cpp:406
void beginDrawing() override
Definition: cairo_gal.cpp:1274
void Restore() override
Restore the context.
Definition: cairo_gal.cpp:735
GRID_STYLE m_gridStyle
Grid display style.
bool Show(bool aShow) override
Show/hide the GAL canvas.
Definition: cairo_gal.cpp:1352
cairo_matrix_t m_currentXform
Definition: cairo_gal.h:337
COLOR4D m_fillColor
The fill color.
int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:757
Definition: color4d.h:56
VECTOR2< T > Rotate(double aAngle) const
Rotate the vector by a given angle.
Definition: vector2d.h:371
VECTOR2D m_depthRange
Range of the depth.
bool BoolArg
A bool argument.
Definition: cairo_gal.h:318
COLOR4D m_axesColor
Color of the axes.
const int scale
Main rendering target (cached)
Definition: definitions.h:48
void arc_angles_xform_and_normalize(double &aStartAngle, double &aEndAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix for the start angle a...
Definition: cairo_gal.cpp:141
wxEvtHandler * m_mouseListener
Mouse listener.
Definition: cairo_gal.h:469
RENDER_TARGET GetTarget() const override
Get the currently used target for rendering.
Definition: cairo_gal.cpp:1400
Represent a polyline (an zero-thickness chain of connected line segments).
void drawPoly(const std::deque< VECTOR2D > &aPointList)
Drawing polygons & polylines is the same in Cairo, so here is the common code.
Definition: cairo_gal.cpp:1128
void DrawGrid() override
Definition: cairo_gal.cpp:1565
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Translate the context.
Definition: cairo_gal.h:305
wxSize GetSizePixels() const
Definition: bitmap_base.h:124
void Save() override
Save the context.
Definition: cairo_gal.cpp:717
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
void beginDrawing() override
Definition: cairo_gal.cpp:92
#define SWAP(varA, condition, varB)
Swap the variables if a condition is met.
Definition: definitions.h:31
void endDrawing() override
Definition: cairo_gal.cpp:1288
void drawGridLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a grid line (usually a simplified line function).
Definition: cairo_gal.cpp:989
unsigned int m_mainBuffer
Handle to the main buffer.
Definition: cairo_gal.h:462
bool m_fullscreenCursor
Shape of the cursor (fullscreen or small cross)
cairo_path_t * m_CairoPath
Pointer to a Cairo path.
Definition: cairo_gal.h:321
void SetLineWidth(float aLineWidth) override
Set the line width.
Definition: cairo_gal.cpp:617
std::shared_ptr< CAIRO_COMPOSITOR > m_compositor
Object for layers compositing.
Definition: cairo_gal.h:461
void Scale(const VECTOR2D &aScale) override
Scale the context.
Definition: cairo_gal.cpp:697
void deleteBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1481
void DrawGroup(int aGroupNumber) override
Draw the stored group.
Definition: cairo_gal.cpp:780
VECTOR2D m_gridOrigin
The grid origin.
void syncLineWidth(bool aForceWidth=false, double aWidth=0.0)
Definition: cairo_gal.cpp:210
double m_worldUnitLength
The unit length of the world coordinates [inch].
double r
Red component.
Definition: color4d.h:363
int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:1363
double DblArg[MAX_CAIRO_ARGUMENTS]
Arguments for Cairo commands.
Definition: cairo_gal.h:317
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:46
void allocateBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1462
unsigned int m_overlayBuffer
Handle to the overlay buffer.
Definition: cairo_gal.h:463
void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
Definition: cairo_gal.cpp:643
bool IsCursorEnabled() const
Return information about cursor visibility.
VECTOR2I m_screenSize
Screen size in screen coordinates.
void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint, double aFilterValue=0.0) override
Draw a cubic bezier spline.
Definition: cairo_gal.cpp:441
KICURSOR m_currentNativeCursor
Current cursor.
float m_lineWidth
The line width.
void EndGroup() override
End the group.
Definition: cairo_gal.cpp:1370
cairo_surface_t * m_surface
Cairo surface.
Definition: cairo_gal.h:341
virtual void SetTarget(RENDER_TARGET aTarget)
Set the target for rendering.
void Flush() override
Force all remaining objects to be drawn.
Definition: cairo_gal.cpp:537
std::vector< cairo_surface_t * > m_imageSurfaces
List of surfaces that were created by painting images, to be cleaned up later.
Definition: cairo_gal.h:344
int GetPPI() const
Definition: bitmap_base.h:135
Enable/disable stroking.
Definition: cairo_gal.h:297
unsigned int getNewGroupNumber()
Return a valid key that can be used as a new group number.
Definition: cairo_gal.cpp:1204
bool m_isGrouping
Is grouping enabled ?
Definition: cairo_gal.h:327
void blitCursor(wxMemoryDC &clientDC)
Blit cursor into the current screen.
Definition: cairo_gal.cpp:1111
Restore the transformation matrix.
Definition: cairo_gal.h:308
void SetFillColor(const COLOR4D &aColor) override
Set the fill color.
Definition: cairo_gal.cpp:599
void drawGridCross(const VECTOR2D &aPoint)
Definition: cairo_gal.cpp:1003
Abstract interface for drawing on a 2D-surface.
void Rotate(double aAngle) override
Rotate the context.
Definition: cairo_gal.cpp:658
static constexpr cairo_format_t GAL_FORMAT
Format used to store pixels.
Definition: cairo_gal.h:348
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
wxEvtHandler * m_paintListener
Paint listener.
Definition: cairo_gal.h:470