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