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