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