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