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