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