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  double aFilterValue )
429 {
430  // Note: aFilterValue is not used because the cubic Bezier curve is
431  // supported by Cairo.
432  syncLineWidth();
433 
434  const auto sp = roundp( xform( aStartPoint ) );
435  const auto cpa = roundp( xform( aControlPointA ) );
436  const auto cpb = roundp( xform( aControlPointB ) );
437  const auto ep = roundp( xform( aEndPoint ) );
438 
439  cairo_move_to( currentContext, sp.x, sp.y );
440  cairo_curve_to( currentContext, cpa.x, cpa.y, cpb.x, cpb.y, ep.x, ep.y );
441  cairo_line_to( currentContext, ep.x, ep.y );
442 
443  flushPath();
444  isElementAdded = true;
445 }
446 
447 
449 {
450  cairo_save( currentContext );
451 
452  // We have to calculate the pixel size in users units to draw the image.
453  // worldUnitLength is a factor used for converting IU to inches
454  double scale = 1.0 / ( aBitmap.GetPPI() * worldUnitLength );
455 
456  // The position of the bitmap is the bitmap center.
457  // move the draw origin to the top left bitmap corner:
458  int w = aBitmap.GetSizePixels().x;
459  int h = aBitmap.GetSizePixels().y;
460 
461  cairo_set_matrix( currentContext, &currentWorld2Screen );
462  cairo_scale( currentContext, scale, scale );
463  cairo_translate( currentContext, -w / 2.0, -h / 2.0 );
464 
465  cairo_new_path( currentContext );
466  cairo_surface_t* image = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w, h );
467  cairo_surface_flush( image );
468 
469  unsigned char* pix_buffer = cairo_image_surface_get_data( image );
470  // The pixel buffer of the initial bitmap:
471  const wxImage& bm_pix_buffer = *aBitmap.GetImageData();
472 
473  uint32_t mask_color = ( bm_pix_buffer.GetMaskRed() << 16 ) +
474  ( bm_pix_buffer.GetMaskGreen() << 8 ) +
475  ( bm_pix_buffer.GetMaskBlue() );
476 
477  // Copy the source bitmap to the cairo bitmap buffer.
478  // In cairo bitmap buffer, a ARGB32 bitmap is an ARGB pixel packed into a uint_32
479  // 24 low bits only are used for color, top 8 are transparency.
480  for( int row = 0; row < h; row++ )
481  {
482  for( int col = 0; col < w; col++ )
483  {
484  // Build the RGB24 pixel:
485  uint32_t pixel = bm_pix_buffer.GetRed( col, row ) << 16;
486  pixel += bm_pix_buffer.GetGreen( col, row ) << 8;
487  pixel += bm_pix_buffer.GetBlue( col, row );
488 
489  if( bm_pix_buffer.HasAlpha() )
490  pixel += bm_pix_buffer.GetAlpha( col, row ) << 24;
491  else if( bm_pix_buffer.HasMask() && pixel == mask_color )
492  pixel += ( wxALPHA_TRANSPARENT << 24 );
493  else
494  pixel += ( wxALPHA_OPAQUE << 24 );
495 
496  // Write the pixel to the cairo image buffer:
497  uint32_t* pix_ptr = (uint32_t*) pix_buffer;
498  *pix_ptr = pixel;
499  pix_buffer += 4;
500  }
501  }
502 
503  cairo_surface_mark_dirty( image );
504  cairo_set_source_surface( currentContext, image, 0, 0 );
505  cairo_paint( currentContext );
506  cairo_surface_destroy( image );
507 
508  isElementAdded = true;
509 
510  cairo_restore( currentContext );
511 }
512 
513 
514 void CAIRO_GAL_BASE::ResizeScreen( int aWidth, int aHeight )
515 {
516  screenSize = VECTOR2I( aWidth, aHeight );
517 }
518 
519 
521 {
522  storePath();
523 }
524 
525 
527 {
528  cairo_set_source_rgb( currentContext, m_clearColor.r, m_clearColor.g, m_clearColor.b );
529  cairo_rectangle( currentContext, 0.0, 0.0, screenSize.x, screenSize.y );
530  cairo_fill( currentContext );
531 }
532 
533 
534 void CAIRO_GAL_BASE::SetIsFill( bool aIsFillEnabled )
535 {
536  storePath();
537  isFillEnabled = aIsFillEnabled;
538 
539  if( isGrouping )
540  {
541  GROUP_ELEMENT groupElement;
542  groupElement.command = CMD_SET_FILL;
543  groupElement.argument.boolArg = aIsFillEnabled;
544  currentGroup->push_back( groupElement );
545  }
546 }
547 
548 
549 void CAIRO_GAL_BASE::SetIsStroke( bool aIsStrokeEnabled )
550 {
551  storePath();
552  isStrokeEnabled = aIsStrokeEnabled;
553 
554  if( isGrouping )
555  {
556  GROUP_ELEMENT groupElement;
557  groupElement.command = CMD_SET_STROKE;
558  groupElement.argument.boolArg = aIsStrokeEnabled;
559  currentGroup->push_back( groupElement );
560  }
561 }
562 
563 
565 {
566  storePath();
567  strokeColor = aColor;
568 
569  if( isGrouping )
570  {
571  GROUP_ELEMENT groupElement;
572  groupElement.command = CMD_SET_STROKECOLOR;
573  groupElement.argument.dblArg[0] = strokeColor.r;
574  groupElement.argument.dblArg[1] = strokeColor.g;
575  groupElement.argument.dblArg[2] = strokeColor.b;
576  groupElement.argument.dblArg[3] = strokeColor.a;
577  currentGroup->push_back( groupElement );
578  }
579 }
580 
581 
583 {
584  storePath();
585  fillColor = aColor;
586 
587  if( isGrouping )
588  {
589  GROUP_ELEMENT groupElement;
590  groupElement.command = CMD_SET_FILLCOLOR;
591  groupElement.argument.dblArg[0] = fillColor.r;
592  groupElement.argument.dblArg[1] = fillColor.g;
593  groupElement.argument.dblArg[2] = fillColor.b;
594  groupElement.argument.dblArg[3] = fillColor.a;
595  currentGroup->push_back( groupElement );
596  }
597 }
598 
599 
600 void CAIRO_GAL_BASE::SetLineWidth( float aLineWidth )
601 {
602  storePath();
603  GAL::SetLineWidth( aLineWidth );
604 
605  if( isGrouping )
606  {
607  GROUP_ELEMENT groupElement;
608  groupElement.command = CMD_SET_LINE_WIDTH;
609  groupElement.argument.dblArg[0] = aLineWidth;
610  currentGroup->push_back( groupElement );
611  }
612  else
613  {
614  lineWidth = aLineWidth;
615  }
616 }
617 
618 
619 void CAIRO_GAL_BASE::SetLayerDepth( double aLayerDepth )
620 {
621  super::SetLayerDepth( aLayerDepth );
622  storePath();
623 }
624 
625 
626 void CAIRO_GAL_BASE::Transform( const MATRIX3x3D& aTransformation )
627 {
628  cairo_matrix_t cairoTransformation, newXform;
629 
630  cairo_matrix_init( &cairoTransformation,
631  aTransformation.m_data[0][0],
632  aTransformation.m_data[1][0],
633  aTransformation.m_data[0][1],
634  aTransformation.m_data[1][1],
635  aTransformation.m_data[0][2],
636  aTransformation.m_data[1][2] );
637 
638  cairo_matrix_multiply( &newXform, &currentXform, &cairoTransformation );
639  currentXform = newXform;
641 }
642 
643 
644 void CAIRO_GAL_BASE::Rotate( double aAngle )
645 {
646  storePath();
647 
648  if( isGrouping )
649  {
650  GROUP_ELEMENT groupElement;
651  groupElement.command = CMD_ROTATE;
652  groupElement.argument.dblArg[0] = aAngle;
653  currentGroup->push_back( groupElement );
654  }
655  else
656  {
657  cairo_matrix_rotate( &currentXform, aAngle );
659  }
660 }
661 
662 
663 void CAIRO_GAL_BASE::Translate( const VECTOR2D& aTranslation )
664 {
665  storePath();
666 
667  if( isGrouping )
668  {
669  GROUP_ELEMENT groupElement;
670  groupElement.command = CMD_TRANSLATE;
671  groupElement.argument.dblArg[0] = aTranslation.x;
672  groupElement.argument.dblArg[1] = aTranslation.y;
673  currentGroup->push_back( groupElement );
674  }
675  else
676  {
677  cairo_matrix_translate ( &currentXform, aTranslation.x, aTranslation.y );
679  }
680 }
681 
682 
683 void CAIRO_GAL_BASE::Scale( const VECTOR2D& aScale )
684 {
685  storePath();
686 
687  if( isGrouping )
688  {
689  GROUP_ELEMENT groupElement;
690  groupElement.command = CMD_SCALE;
691  groupElement.argument.dblArg[0] = aScale.x;
692  groupElement.argument.dblArg[1] = aScale.y;
693  currentGroup->push_back( groupElement );
694  }
695  else
696  {
697  cairo_matrix_scale( &currentXform, aScale.x, aScale.y );
699  }
700 }
701 
702 
704 {
705  storePath();
706 
707  if( isGrouping )
708  {
709  GROUP_ELEMENT groupElement;
710  groupElement.command = CMD_SAVE;
711  currentGroup->push_back( groupElement );
712  }
713  else
714  {
715  xformStack.push_back( currentXform );
717  }
718 }
719 
720 
722 {
723  storePath();
724 
725  if( isGrouping )
726  {
727  GROUP_ELEMENT groupElement;
728  groupElement.command = CMD_RESTORE;
729  currentGroup->push_back( groupElement );
730  }
731  else
732  {
733  if( !xformStack.empty() )
734  {
735  currentXform = xformStack.back();
736  xformStack.pop_back();
738  }
739  }
740 }
741 
742 
744 {
745  // If the grouping is started: the actual path is stored in the group, when
746  // a attribute was changed or when grouping stops with the end group method.
747  storePath();
748 
749  GROUP group;
750  int groupNumber = getNewGroupNumber();
751  groups.insert( std::make_pair( groupNumber, group ) );
752  currentGroup = &groups[groupNumber];
753  isGrouping = true;
754 
755  return groupNumber;
756 }
757 
758 
760 {
761  storePath();
762  isGrouping = false;
763 }
764 
765 
766 void CAIRO_GAL_BASE::DrawGroup( int aGroupNumber )
767 {
768  // This method implements a small Virtual Machine - all stored commands
769  // are executed; nested calling is also possible
770 
771  storePath();
772 
773  for( GROUP::iterator it = groups[aGroupNumber].begin();
774  it != groups[aGroupNumber].end(); ++it )
775  {
776  switch( it->command )
777  {
778  case CMD_SET_FILL:
779  isFillEnabled = it->argument.boolArg;
780  break;
781 
782  case CMD_SET_STROKE:
783  isStrokeEnabled = it->argument.boolArg;
784  break;
785 
786  case CMD_SET_FILLCOLOR:
787  fillColor = COLOR4D( it->argument.dblArg[0], it->argument.dblArg[1], it->argument.dblArg[2],
788  it->argument.dblArg[3] );
789  break;
790 
791  case CMD_SET_STROKECOLOR:
792  strokeColor = COLOR4D( it->argument.dblArg[0], it->argument.dblArg[1], it->argument.dblArg[2],
793  it->argument.dblArg[3] );
794  break;
795 
796  case CMD_SET_LINE_WIDTH:
797  {
798  // Make lines appear at least 1 pixel wide, no matter of zoom
799  double x = 1.0, y = 1.0;
800  cairo_device_to_user_distance( currentContext, &x, &y );
801  double minWidth = std::min( fabs( x ), fabs( y ) );
802  cairo_set_line_width( currentContext, std::max( it->argument.dblArg[0], minWidth ) );
803  }
804  break;
805 
806 
807  case CMD_STROKE_PATH:
808  cairo_set_source_rgba( currentContext, strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a );
809  cairo_append_path( currentContext, it->cairoPath );
810  cairo_stroke( currentContext );
811  break;
812 
813  case CMD_FILL_PATH:
814  cairo_set_source_rgba( currentContext, fillColor.r, fillColor.g, fillColor.b, strokeColor.a );
815  cairo_append_path( currentContext, it->cairoPath );
816  cairo_fill( currentContext );
817  break;
818 
819  /*
820  case CMD_TRANSFORM:
821  cairo_matrix_t matrix;
822  cairo_matrix_init( &matrix, it->argument.dblArg[0], it->argument.dblArg[1], it->argument.dblArg[2],
823  it->argument.dblArg[3], it->argument.dblArg[4], it->argument.dblArg[5] );
824  cairo_transform( currentContext, &matrix );
825  break;
826  */
827 
828  case CMD_ROTATE:
829  cairo_rotate( currentContext, it->argument.dblArg[0] );
830  break;
831 
832  case CMD_TRANSLATE:
833  cairo_translate( currentContext, it->argument.dblArg[0], it->argument.dblArg[1] );
834  break;
835 
836  case CMD_SCALE:
837  cairo_scale( currentContext, it->argument.dblArg[0], it->argument.dblArg[1] );
838  break;
839 
840  case CMD_SAVE:
841  cairo_save( currentContext );
842  break;
843 
844  case CMD_RESTORE:
845  cairo_restore( currentContext );
846  break;
847 
848  case CMD_CALL_GROUP:
849  DrawGroup( it->argument.intArg );
850  break;
851  }
852  }
853 }
854 
855 
856 void CAIRO_GAL_BASE::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
857 {
858  storePath();
859 
860  for( GROUP::iterator it = groups[aGroupNumber].begin();
861  it != groups[aGroupNumber].end(); ++it )
862  {
863  if( it->command == CMD_SET_FILLCOLOR || it->command == CMD_SET_STROKECOLOR )
864  {
865  it->argument.dblArg[0] = aNewColor.r;
866  it->argument.dblArg[1] = aNewColor.g;
867  it->argument.dblArg[2] = aNewColor.b;
868  it->argument.dblArg[3] = aNewColor.a;
869  }
870  }
871 }
872 
873 
874 void CAIRO_GAL_BASE::ChangeGroupDepth( int aGroupNumber, int aDepth )
875 {
876  // Cairo does not have any possibilities to change the depth coordinate of stored items,
877  // it depends only on the order of drawing
878 }
879 
880 
881 void CAIRO_GAL_BASE::DeleteGroup( int aGroupNumber )
882 {
883  storePath();
884 
885  // Delete the Cairo paths
886  std::deque<GROUP_ELEMENT>::iterator it, end;
887 
888  for( it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end(); it != end; ++it )
889  {
890  if( it->command == CMD_FILL_PATH || it->command == CMD_STROKE_PATH )
891  {
892  cairo_path_destroy( it->cairoPath );
893  }
894  }
895 
896  // Delete the group
897  groups.erase( aGroupNumber );
898 }
899 
900 
902 {
903  for( auto it = groups.begin(); it != groups.end(); )
904  DeleteGroup( ( it++ )->first );
905 }
906 
907 
909 {
910  cairo_set_operator( currentContext, aSetting ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_OVER );
911 }
912 
913 
914 void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition )
915 {
916  cursorPosition = aCursorPosition;
917 }
918 
919 
920 void CAIRO_GAL_BASE::EnableDepthTest( bool aEnabled )
921 {
922 }
923 
924 
926 {
927  ClearScreen();
928 
929  // Compute the world <-> screen transformations
931 
932  cairo_matrix_init( &cairoWorldScreenMatrix, worldScreenMatrix.m_data[0][0],
935  worldScreenMatrix.m_data[1][2] );
936 
937  // we work in screen-space coordinates and do the transforms outside.
938  cairo_identity_matrix( context );
939 
940  cairo_matrix_init_identity( &currentXform );
941 
942  // Start drawing with a new path
943  cairo_new_path( context );
944  isElementAdded = true;
945 
947 
948  lineWidth = 0;
949 }
950 
951 
952 void CAIRO_GAL_BASE::drawAxes( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
953 {
954  syncLineWidth();
955 
956  auto p0 = roundp( xform( aStartPoint ) );
957  auto p1 = roundp( xform( aEndPoint ) );
958  auto org = roundp( xform( VECTOR2D( 0.0, 0.0 ) ) ); // Axis origin = 0,0 coord
959 
960  cairo_set_source_rgba( currentContext, axesColor.r, axesColor.g, axesColor.b, axesColor.a );
961  cairo_move_to( currentContext, p0.x, org.y);
962  cairo_line_to( currentContext, p1.x, org.y );
963  cairo_move_to( currentContext, org.x, p0.y );
964  cairo_line_to( currentContext, org.x, p1.y );
965  cairo_stroke( currentContext );
966 }
967 
968 
969 void CAIRO_GAL_BASE::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
970 {
971  syncLineWidth();
972  auto p0 = roundp( xform( aStartPoint ) );
973  auto p1 = roundp( xform( aEndPoint ) );
974 
975  cairo_set_source_rgba( currentContext, gridColor.r, gridColor.g, gridColor.b, gridColor.a );
976  cairo_move_to( currentContext, p0.x, p0.y );
977  cairo_line_to( currentContext, p1.x, p1.y );
978  cairo_stroke( currentContext );
979 }
980 
981 
983 {
984  syncLineWidth();
985  VECTOR2D offset( 0, 0 );
986  auto size = 2.0 * lineWidthInPixels;
987 
988  auto p0 = roundp( xform( aPoint ) ) - VECTOR2D( size, 0 ) + offset;
989  auto p1 = roundp( xform( aPoint ) ) + VECTOR2D( size, 0 ) + offset;
990  auto p2 = roundp( xform( aPoint ) ) - VECTOR2D( 0, size ) + offset;
991  auto p3 = roundp( xform( aPoint ) ) + VECTOR2D( 0, size ) + offset;
992 
993  cairo_set_source_rgba( currentContext, gridColor.r, gridColor.g, gridColor.b, gridColor.a );
994  cairo_move_to( currentContext, p0.x, p0.y );
995  cairo_line_to( currentContext, p1.x, p1.y );
996  cairo_move_to( currentContext, p2.x, p2.y );
997  cairo_line_to( currentContext, p3.x, p3.y );
998  cairo_stroke( currentContext );
999 }
1000 
1001 
1002 void CAIRO_GAL_BASE::drawGridPoint( const VECTOR2D& aPoint, double aSize )
1003 {
1004  auto p = roundp( xform( aPoint ) );
1005  auto s = xform( aSize / 2.0 );
1006 
1007  cairo_set_source_rgba( currentContext, gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1008  cairo_move_to( currentContext, p.x, p.y );
1009  cairo_arc( currentContext, p.x, p.y, s, 0.0, 2.0 * M_PI );
1010  cairo_close_path( currentContext );
1011 
1012  cairo_fill( currentContext );
1013 }
1014 
1016 {
1017  if( isFillEnabled )
1018  {
1019  cairo_set_source_rgba( currentContext,
1021 
1022  if( isStrokeEnabled )
1023  cairo_fill_preserve( currentContext );
1024  else
1025  cairo_fill( currentContext );
1026  }
1027 
1028  if( isStrokeEnabled )
1029  {
1030  cairo_set_source_rgba( currentContext,
1032  cairo_stroke( currentContext );
1033  }
1034 }
1035 
1036 
1038 {
1039  if( isElementAdded )
1040  {
1041  isElementAdded = false;
1042 
1043  if( !isGrouping )
1044  {
1045  if( isFillEnabled )
1046  {
1047  cairo_set_source_rgba( currentContext, fillColor.r, fillColor.g, fillColor.b, fillColor.a );
1048  cairo_fill_preserve( currentContext );
1049  }
1050 
1051  if( isStrokeEnabled )
1052  {
1053  cairo_set_source_rgba( currentContext, strokeColor.r, strokeColor.g,
1055  cairo_stroke_preserve( currentContext );
1056  }
1057  }
1058  else
1059  {
1060  // Copy the actual path, append it to the global path list
1061  // then check, if the path needs to be stroked/filled and
1062  // add this command to the group list;
1063  if( isStrokeEnabled )
1064  {
1065  GROUP_ELEMENT groupElement;
1066  groupElement.cairoPath = cairo_copy_path( currentContext );
1067  groupElement.command = CMD_STROKE_PATH;
1068  currentGroup->push_back( groupElement );
1069  }
1070 
1071  if( isFillEnabled )
1072  {
1073  GROUP_ELEMENT groupElement;
1074  groupElement.cairoPath = cairo_copy_path( currentContext );
1075  groupElement.command = CMD_FILL_PATH;
1076  currentGroup->push_back( groupElement );
1077  }
1078  }
1079 
1080  cairo_new_path( currentContext );
1081  }
1082 }
1083 
1084 
1085 void CAIRO_GAL_BASE::blitCursor( wxMemoryDC& clientDC )
1086 {
1087  if( !IsCursorEnabled() )
1088  return;
1089 
1090  auto p = ToScreen( cursorPosition );
1091 
1092  const auto cColor = getCursorColor();
1093  const int cursorSize = fullscreenCursor ? 8000 : 80;
1094 
1095  wxColour color( cColor.r * cColor.a * 255, cColor.g * cColor.a * 255,
1096  cColor.b * cColor.a * 255, 255 );
1097  clientDC.SetPen( wxPen( color ) );
1098  clientDC.DrawLine( p.x - cursorSize / 2, p.y, p.x + cursorSize / 2, p.y );
1099  clientDC.DrawLine( p.x, p.y - cursorSize / 2, p.x, p.y + cursorSize / 2 );
1100 }
1101 
1102 
1103 void CAIRO_GAL_BASE::drawPoly( const std::deque<VECTOR2D>& aPointList )
1104 {
1105  // Iterate over the point list and draw the segments
1106  std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
1107 
1108  syncLineWidth();
1109 
1110  const auto p = roundp( xform( it->x, it->y ) );
1111 
1112  cairo_move_to( currentContext, p.x, p.y );
1113 
1114  for( ++it; it != aPointList.end(); ++it )
1115  {
1116  const auto p2 = roundp( xform( it->x, it->y ) );
1117 
1118  cairo_line_to( currentContext, p2.x, p2.y );
1119  }
1120 
1121  flushPath();
1122  isElementAdded = true;
1123 }
1124 
1125 
1126 void CAIRO_GAL_BASE::drawPoly( const VECTOR2D aPointList[], int aListSize )
1127 {
1128  // Iterate over the point list and draw the segments
1129  const VECTOR2D* ptr = aPointList;
1130 
1131  syncLineWidth();
1132 
1133  const auto p = roundp( xform( ptr->x, ptr->y ) );
1134  cairo_move_to( currentContext, p.x, p.y );
1135 
1136  for( int i = 0; i < aListSize; ++i )
1137  {
1138  ++ptr;
1139  const auto p2 = roundp( xform( ptr->x, ptr->y ) );
1140  cairo_line_to( currentContext, p2.x, p2.y );
1141  }
1142 
1143  flushPath();
1144  isElementAdded = true;
1145 }
1146 
1147 
1149 {
1150  if( aLineChain.PointCount() < 2 )
1151  return;
1152 
1153  syncLineWidth();
1154 
1155  auto numPoints = aLineChain.PointCount();
1156 
1157  if( aLineChain.IsClosed() )
1158  numPoints += 1;
1159 
1160  const VECTOR2I start = aLineChain.CPoint( 0 );
1161  const auto p = roundp( xform( start.x, start.y ) );
1162  cairo_move_to( currentContext, p.x, p.y );
1163 
1164  for( int i = 1; i < numPoints; ++i )
1165  {
1166  const VECTOR2I& pw = aLineChain.CPoint( i );
1167  const auto ps = roundp( xform( pw.x, pw.y ) );
1168  cairo_line_to( currentContext, ps.x, ps.y );
1169  }
1170 
1171  flushPath();
1172  isElementAdded = true;
1173 }
1174 
1175 
1177 {
1178  wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
1179  wxT( "There are no free slots to store a group" ) );
1180 
1181  while( groups.find( groupCounter ) != groups.end() )
1182  groupCounter++;
1183 
1184  return groupCounter++;
1185 }
1186 
1187 
1189  wxWindow* aParent, wxEvtHandler* aMouseListener,
1190  wxEvtHandler* aPaintListener, const wxString& aName ) :
1191  CAIRO_GAL_BASE( aDisplayOptions ),
1192  wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName )
1193 {
1194  // Initialise compositing state
1195  mainBuffer = 0;
1196  overlayBuffer = 0;
1197  validCompositor = false;
1199 
1200  parentWindow = aParent;
1201  mouseListener = aMouseListener;
1202  paintListener = aPaintListener;
1203 
1204  // Connecting the event handlers
1205  Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) );
1206 
1207  // Mouse events are skipped to the parent
1208  Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1209  Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1210  Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1211  Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1212  Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1213  Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1214  Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1215  Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1216  Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1217  Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1218  Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1219 #if defined _WIN32 || defined _WIN64
1220  Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1221 #endif
1222 
1223  SetSize( aParent->GetClientSize() );
1224  screenSize = VECTOR2I( aParent->GetClientSize() );
1225 
1226  // Allocate memory for pixel storage
1227  allocateBitmaps();
1228 
1229  isInitialized = false;
1230 }
1231 
1232 
1234 {
1235  deleteBitmaps();
1236 }
1237 
1238 
1240 {
1241  initSurface();
1242 
1244 
1245  if( !validCompositor )
1246  setCompositor();
1247 
1248  compositor->SetMainContext( context );
1249  compositor->SetBuffer( mainBuffer );
1250 }
1251 
1252 
1254 {
1256 
1257  // Merge buffers on the screen
1258  compositor->DrawBuffer( mainBuffer );
1259  compositor->DrawBuffer( overlayBuffer );
1260 
1261  // Now translate the raw context data from the format stored
1262  // by cairo into a format understood by wxImage.
1263 
1264  pixman_image_t* dstImg = pixman_image_create_bits( PIXMAN_r8g8b8,
1265  screenSize.x, screenSize.y, (uint32_t*) wxOutput, wxBufferWidth * 3 );
1266  pixman_image_t* srcImg = pixman_image_create_bits( PIXMAN_a8b8g8r8,
1267  screenSize.x, screenSize.y, (uint32_t*) bitmapBuffer, wxBufferWidth * 4 );
1268 
1269  pixman_image_composite( PIXMAN_OP_SRC, srcImg, NULL, dstImg,
1270  0, 0, 0, 0, 0, 0, screenSize.x, screenSize.y );
1271 
1272  // Free allocated memory
1273  pixman_image_unref( srcImg );
1274  pixman_image_unref( dstImg );
1275 
1276  wxImage img( wxBufferWidth, screenSize.y, wxOutput, true );
1277  wxBitmap bmp( img );
1278  wxMemoryDC mdc( bmp );
1279  wxClientDC clientDC( this );
1280 
1281  // Now it is the time to blit the mouse cursor
1282  blitCursor( mdc );
1283  clientDC.Blit( 0, 0, screenSize.x, screenSize.y, &mdc, 0, 0, wxCOPY );
1284 
1285  deinitSurface();
1286 }
1287 
1288 
1289 void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
1290 {
1291  CAIRO_GAL_BASE::ResizeScreen( aWidth, aHeight );
1292 
1293  // Recreate the bitmaps
1294  deleteBitmaps();
1295  allocateBitmaps();
1296 
1297  if( validCompositor )
1298  compositor->Resize( aWidth, aHeight );
1299 
1300  validCompositor = false;
1301 
1302  SetSize( wxSize( aWidth, aHeight ) );
1303 }
1304 
1305 
1306 bool CAIRO_GAL::Show( bool aShow )
1307 {
1308  bool s = wxWindow::Show( aShow );
1309 
1310  if( aShow )
1311  wxWindow::Raise();
1312 
1313  return s;
1314 }
1315 
1316 
1318 {
1319  initSurface();
1320  return CAIRO_GAL_BASE::BeginGroup();
1321 }
1322 
1323 
1325 {
1327  deinitSurface();
1328 }
1329 
1330 
1332 {
1333  // If the compositor is not set, that means that there is a recaching process going on
1334  // and we do not need the compositor now
1335  if( !validCompositor )
1336  return;
1337 
1338  // Cairo grouping prevents display of overlapping items on the same layer in the lighter color
1339  if( isInitialized )
1340  storePath();
1341 
1342  switch( aTarget )
1343  {
1344  default:
1345  case TARGET_CACHED:
1346  case TARGET_NONCACHED:
1347  compositor->SetBuffer( mainBuffer );
1348  break;
1349 
1350  case TARGET_OVERLAY:
1351  compositor->SetBuffer( overlayBuffer );
1352  break;
1353  }
1354 
1355  currentTarget = aTarget;
1356 }
1357 
1358 
1360 {
1361  return currentTarget;
1362 }
1363 
1364 
1366 {
1367  // Save the current state
1368  unsigned int currentBuffer = compositor->GetBuffer();
1369 
1370  switch( aTarget )
1371  {
1372  // Cached and noncached items are rendered to the same buffer
1373  default:
1374  case TARGET_CACHED:
1375  case TARGET_NONCACHED:
1376  compositor->SetBuffer( mainBuffer );
1377  break;
1378 
1379  case TARGET_OVERLAY:
1380  compositor->SetBuffer( overlayBuffer );
1381  break;
1382  }
1383 
1384  compositor->ClearBuffer( COLOR4D::BLACK );
1385 
1386  // Restore the previous state
1387  compositor->SetBuffer( currentBuffer );
1388 }
1389 
1390 
1392 {
1393  if( isInitialized )
1394  return;
1395 
1396  surface = cairo_image_surface_create_for_data( bitmapBuffer, GAL_FORMAT,
1398 
1399  context = cairo_create( surface );
1400 
1401 #ifdef DEBUG
1402  cairo_status_t status = cairo_status( context );
1403  wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) );
1404 #endif /* DEBUG */
1406 
1407  isInitialized = true;
1408 }
1409 
1410 
1412 {
1413  if( !isInitialized )
1414  return;
1415 
1416  cairo_destroy( context );
1417  context = nullptr;
1418  cairo_surface_destroy( surface );
1419  surface = nullptr;
1420 
1421  isInitialized = false;
1422 }
1423 
1424 
1426 {
1428  while( ( ( wxBufferWidth * 3 ) % 4 ) != 0 ) wxBufferWidth++;
1429 
1430  // Create buffer, use the system independent Cairo context backend
1431  stride = cairo_format_stride_for_width( GAL_FORMAT, wxBufferWidth );
1433 
1434  bitmapBuffer = new unsigned char[bufferSize * 4];
1435  wxOutput = new unsigned char[wxBufferWidth * 3 * screenSize.y];
1436 }
1437 
1438 
1440 {
1441  delete[] bitmapBuffer;
1442  delete[] wxOutput;
1443 }
1444 
1445 
1447 {
1448  // Recreate the compositor with the new Cairo context
1449  compositor.reset( new CAIRO_COMPOSITOR( &currentContext ) );
1450  compositor->Resize( screenSize.x, screenSize.y );
1451  compositor->SetAntialiasingMode( options.cairo_antialiasing_mode );
1452 
1453  // Prepare buffers
1454  mainBuffer = compositor->CreateBuffer();
1455  overlayBuffer = compositor->CreateBuffer();
1456 
1457  validCompositor = true;
1458 }
1459 
1460 
1461 void CAIRO_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
1462 {
1463  PostPaint();
1464 }
1465 
1466 
1467 void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1468 {
1469  // Post the mouse event to the event listener registered in constructor, if any
1470  if( mouseListener )
1471  wxPostEvent( mouseListener, aEvent );
1472 }
1473 
1474 
1476 {
1477  bool refresh = false;
1478 
1479  if( validCompositor && aOptions.cairo_antialiasing_mode != compositor->GetAntialiasingMode() )
1480  {
1481 
1482  compositor->SetAntialiasingMode( options.cairo_antialiasing_mode );
1483  validCompositor = false;
1484  deinitSurface();
1485 
1486  refresh = true;
1487  }
1488 
1489  if( super::updatedGalDisplayOptions( aOptions ) )
1490  {
1491  Refresh();
1492  refresh = true;
1493  }
1494 
1495  return refresh;
1496 }
1497 
1498 
1500 {
1502 
1503  // Draw the grid
1504  // For the drawing the start points, end points and increments have
1505  // to be calculated in world coordinates
1506  VECTOR2D worldStartPoint = screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1507  VECTOR2D worldEndPoint = screenWorldMatrix * VECTOR2D( screenSize );
1508 
1509  // Compute the line marker or point radius of the grid
1510  // Note: generic grids can't handle sub-pixel lines without
1511  // either losing fine/course distinction or having some dots
1512  // fail to render
1513  float marker = std::fmax( 1.0f, gridLineWidth ) / worldScale;
1514  float doubleMarker = 2.0f * marker;
1515 
1516  // Draw axes if desired
1517  if( axesEnabled )
1518  {
1519  SetLineWidth( marker );
1520  drawAxes( worldStartPoint, worldEndPoint );
1521  }
1522 
1523  if( !gridVisibility || gridSize.x == 0 || gridSize.y == 0 )
1524  return;
1525 
1526  VECTOR2D gridScreenSize( gridSize );
1527 
1528  double gridThreshold = KiROUND( computeMinGridSpacing() / worldScale );
1529 
1531  gridThreshold *= 2.0;
1532 
1533  // If we cannot display the grid density, scale down by a tick size and
1534  // try again. Eventually, we get some representation of the grid
1535  while( std::min( gridScreenSize.x, gridScreenSize.y ) <= gridThreshold )
1536  {
1537  gridScreenSize = gridScreenSize * static_cast<double>( gridTick );
1538  }
1539 
1540  // Compute grid starting and ending indexes to draw grid points on the
1541  // visible screen area
1542  // Note: later any point coordinate will be offsetted by gridOrigin
1543  int gridStartX = KiROUND( ( worldStartPoint.x - gridOrigin.x ) / gridScreenSize.x );
1544  int gridEndX = KiROUND( ( worldEndPoint.x - gridOrigin.x ) / gridScreenSize.x );
1545  int gridStartY = KiROUND( ( worldStartPoint.y - gridOrigin.y ) / gridScreenSize.y );
1546  int gridEndY = KiROUND( ( worldEndPoint.y - gridOrigin.y ) / gridScreenSize.y );
1547 
1548  // Ensure start coordinate > end coordinate
1549 
1550  SWAP( gridStartX, >, gridEndX );
1551  SWAP( gridStartY, >, gridEndY );
1552 
1553  // Ensure the grid fills the screen
1554  --gridStartX; ++gridEndX;
1555  --gridStartY; ++gridEndY;
1556 
1557  // Draw the grid behind all other layers
1558  SetLayerDepth( depthRange.y * 0.75 );
1559 
1560  if( gridStyle == GRID_STYLE::LINES )
1561  {
1562  // Now draw the grid, every coarse grid line gets the double width
1563 
1564  // Vertical lines
1565  for( int j = gridStartY; j <= gridEndY; j++ )
1566  {
1567  const double y = j * gridScreenSize.y + gridOrigin.y;
1568 
1569  if( axesEnabled && y == 0.0 )
1570  continue;
1571 
1572  SetLineWidth( ( j % gridTick ) ? marker : doubleMarker );
1573  drawGridLine( VECTOR2D( gridStartX * gridScreenSize.x + gridOrigin.x, y ),
1574  VECTOR2D( gridEndX * gridScreenSize.x + gridOrigin.x, y ) );
1575  }
1576 
1577  // Horizontal lines
1578  for( int i = gridStartX; i <= gridEndX; i++ )
1579  {
1580  const double x = i * gridScreenSize.x + gridOrigin.x;
1581 
1582  if( axesEnabled && x == 0.0 )
1583  continue;
1584 
1585  SetLineWidth( ( i % gridTick ) ? marker : doubleMarker );
1586  drawGridLine( VECTOR2D( x, gridStartY * gridScreenSize.y + gridOrigin.y ),
1587  VECTOR2D( x, gridEndY * gridScreenSize.y + gridOrigin.y ) );
1588 
1589  }
1590  }
1591  else // Dots or Crosses grid
1592  {
1593  for( int j = gridStartY; j <= gridEndY; j++ )
1594  {
1595  bool tickY = ( j % gridTick == 0 );
1596 
1597  for( int i = gridStartX; i <= gridEndX; i++ )
1598  {
1599  bool tickX = ( i % gridTick == 0 );
1600  SetLineWidth( ( ( tickX && tickY ) ? doubleMarker : marker ) );
1601  auto pos = VECTOR2D( i * gridScreenSize.x + gridOrigin.x,
1602  j * gridScreenSize.y + gridOrigin.y );
1603 
1605  drawGridCross( pos );
1606  else if( gridStyle == GRID_STYLE::DOTS )
1607  drawGridPoint( pos, GetLineWidth() );
1608  }
1609  }
1610  }
1611 }
1612 
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:908
Definition: colors.h:57
int gridTick
Every tick line gets the double width.
void initSurface()
Prepare Cairo surfaces for drawing.
Definition: cairo_gal.cpp:1391
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:448
Use lines for the grid.
virtual void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:514
bool axesEnabled
Should the axes be drawn.
unsigned int groupCounter
Counter used for generating keys for groups.
Definition: cairo_gal.h:292
int OutlineCount() const
Returns the number of outlines in the set
virtual void EndGroup() override
End the group.
Definition: cairo_gal.cpp:759
virtual void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Changes the color used to draw the group.
Definition: cairo_gal.cpp:856
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:1475
Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer.
Definition: class_module.h:58
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:1188
virtual void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:1289
void deinitSurface()
Destroy Cairo surfaces when are not needed anymore.
Definition: cairo_gal.cpp:1411
virtual void SetIsStroke(bool aIsStrokeEnabled) override
Enable/disable stroked outlines.
Definition: cairo_gal.cpp:549
double computeMinGridSpacing() const
compute minimum grid spacing from the grid settings
virtual void EnableDepthTest(bool aEnabled=false) override
Definition: cairo_gal.cpp:920
virtual void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Changes the depth (Z-axis position) of the group.
Definition: cairo_gal.cpp:874
cairo_surface_t * surface
Cairo surface.
Definition: cairo_gal.h:305
void onPaint(wxPaintEvent &aEvent)
Paint event handler.
Definition: cairo_gal.cpp:1461
std::deque< GROUP_ELEMENT > GROUP
A graphic group type definition.
Definition: cairo_gal.h:290
virtual ~CAIRO_GAL()
Definition: cairo_gal.cpp:1233
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:663
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:280
void setCompositor()
Prepare the compositor.
Definition: cairo_gal.cpp:1446
void storePath()
Store the actual path.
Definition: cairo_gal.cpp:1037
wxImage * GetImageData()
Definition: bitmap_base.h:83
Type definition for an graphics group element.
Definition: cairo_gal.h:276
void PostPaint()
Function PostPaint posts an event to m_paint_listener.
Definition: cairo_gal.h:388
virtual void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
Definition: cairo_gal.cpp:881
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:426
MATRIX3x3D screenWorldMatrix
Screen transformation.
unsigned int overlayBuffer
Handle to the overlay buffer.
Definition: cairo_gal.h:411
void drawAxes(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Definition: cairo_gal.cpp:952
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:259
#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:619
std::vector< cairo_matrix_t > xformStack
Definition: cairo_gal.h:307
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
bool validCompositor
Compositor initialization flag.
Definition: cairo_gal.h:413
const double angle_xform(const double aAngle)
Transform according to the rotation from currentWorld2Screen transform matrix:
Definition: cairo_gal.cpp: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:901
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:412
static const COLOR4D BLACK
Definition: color4d.h:311
unsigned char * wxOutput
wxImage comaptible buffer
Definition: cairo_gal.h:420
virtual void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: cairo_gal.cpp:225
const VECTOR2I & CPoint(int aIndex) const
Function Point()
virtual void ClearScreen() override
Clear the screen.
Definition: cairo_gal.cpp:526
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:1365
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:293
Class SHAPE_POLY_SET.
std::shared_ptr< CAIRO_COMPOSITOR > compositor
Object for layers compositing.
Definition: cairo_gal.h:409
COLOR4D axesColor
Color of the axes.
virtual void endDrawing() override
Definition: cairo_gal.cpp:90
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:331
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:96
VECTOR2D gridOrigin
The grid origin.
virtual void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
Definition: cairo_gal.cpp:914
void drawGridPoint(const VECTOR2D &aPoint, double aSize)
Definition: cairo_gal.cpp:1002
MATRIX3x3D worldScreenMatrix
World transformation.
wxEvtHandler * paintListener
Paint listener.
Definition: cairo_gal.h:418
const double xform(double x)
Definition: cairo_gal.cpp:164
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:1331
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:1467
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:534
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:424
virtual void SetStrokeColor(const COLOR4D &aColor) override
Set the stroke color.
Definition: cairo_gal.cpp:564
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:391
virtual void beginDrawing() override
Definition: cairo_gal.cpp:1239
virtual void Restore() override
Restore the context.
Definition: cairo_gal.cpp:721
virtual bool Show(bool aShow) override
Shows/hides the GAL canvas.
Definition: cairo_gal.cpp:1306
virtual int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:743
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:1359
Class 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:1103
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:1499
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
VECTOR2D gridSize
The grid size.
size_t i
Definition: json11.cpp:649
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:703
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:1253
void drawGridLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a grid line (usually a simplified line function).
Definition: cairo_gal.cpp:969
virtual void SetLineWidth(float aLineWidth) override
Set the line width.
Definition: cairo_gal.cpp:600
virtual void Scale(const VECTOR2D &aScale) override
Scale the context.
Definition: cairo_gal.cpp:683
void deleteBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1439
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:766
unsigned int bufferSize
Size of buffers cairoOutput, bitmapBuffers.
Definition: cairo_gal.h:419
void syncLineWidth(bool aForceWidth=false, double aWidth=0.0)
Definition: cairo_gal.cpp: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:1317
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:46
bool isElementAdded
Was an graphic element added ?
Definition: cairo_gal.h:289
virtual void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
Definition: cairo_gal.h:97
wxEvtHandler * mouseListener
Mouse listener.
Definition: cairo_gal.h:417
unsigned char * bitmapBuffer
Storage of the cairo image.
Definition: cairo_gal.h:423
void allocateBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1425
virtual void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
Definition: cairo_gal.cpp:626
unsigned int mainBuffer
Handle to the main buffer.
Definition: cairo_gal.h:410
bool IsCursorEnabled() const
Returns information about cursor visibility.
virtual void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint, double aFilterValue=0.0) override
Draw a cubic bezier spline.
Definition: cairo_gal.cpp:426
wxWindow * parentWindow
Parent window.
Definition: cairo_gal.h:416
virtual void EndGroup() override
End the group.
Definition: cairo_gal.cpp:1324
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:520
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:1176
cairo_matrix_t currentWorld2Screen
Definition: cairo_gal.h:302
virtual void blitCursor(wxMemoryDC &clientDC)
Blits cursor into the current screen.
Definition: cairo_gal.cpp:1085
Restore the transformation matrix.
Definition: cairo_gal.h:271
virtual void SetFillColor(const COLOR4D &aColor) override
Set the fill color.
Definition: cairo_gal.cpp:582
void drawGridCross(const VECTOR2D &aPoint)
Definition: cairo_gal.cpp:982
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:644
static constexpr cairo_format_t GAL_FORMAT
Format used to store pixels.
Definition: cairo_gal.h:335
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39