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 <[email protected]>
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 <trigo.h>
39#include <bitmap_base.h>
40
41#include <algorithm>
42#include <cmath>
43#include <limits>
44
45#include <pixman.h>
46
47using namespace KIGFX;
48
49
50CAIRO_GAL_BASE::CAIRO_GAL_BASE( GAL_DISPLAY_OPTIONS& aDisplayOptions ) : GAL( aDisplayOptions )
51{
52 // Initialise grouping
53 m_isGrouping = false;
54 m_isElementAdded = false;
56 m_currentGroup = nullptr;
57
58 m_lineWidth = 1.0;
60 m_lineWidthIsOdd = true;
61
62 // Initialise Cairo state
63 cairo_matrix_init_identity( &m_cairoWorldScreenMatrix );
64 m_currentContext = nullptr;
65 m_context = nullptr;
66 m_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 uninitialized variables:
73 cairo_matrix_init_identity( &m_currentXform );
74 cairo_matrix_init_identity( &m_currentWorld2Screen );
75}
76
77
79{
80 ClearCache();
81
82 if( m_surface )
83 cairo_surface_destroy( m_surface );
84
85 if( m_context )
86 cairo_destroy( m_context );
87
88 for( _cairo_surface* imageSurface : m_imageSurfaces )
89 cairo_surface_destroy( imageSurface );
90}
91
92
94{
96}
97
98
100{
101 // Force remaining objects to be drawn
102 Flush();
103}
104
105
107{
109}
110
111
112const 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
128const 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( m_currentWorld2Screen.xy, m_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
142void CAIRO_GAL_BASE::arc_angles_xform_and_normalize( double& aStartAngle, double& aEndAngle )
143{
144 // 360 deg arcs have a specific calculation.
145 bool is_360deg_arc = std::abs( aEndAngle - aStartAngle ) >= 2 * M_PI;
146 double startAngle = aStartAngle;
147 double endAngle = aEndAngle;
148
149 // When the view is flipped, the coordinates are flipped by the matrix transform
150 // However, arc angles need to be "flipped": the flipped angle is M_PI - initial angle.
151 if( IsFlippedX() )
152 {
153 startAngle = M_PI - startAngle;
154 endAngle = M_PI - endAngle;
155 }
156
157 // Normalize arc angles
158 SWAP( startAngle, >, endAngle );
159
160 // now rotate arc according to the rotation transform matrix
161 // Remark:
162 // We call angle_xform() to calculate angles according to the flip/rotation
163 // transform and normalize between -2M_PI and +2M_PI.
164 // Therefore, if aStartAngle = aEndAngle + 2*n*M_PI, the transform gives
165 // aEndAngle = aStartAngle
166 // So, if this is the case, force the aEndAngle value to draw a circle.
167 aStartAngle = angle_xform( startAngle );
168
169 if( is_360deg_arc ) // arc is a full circle
170 aEndAngle = aStartAngle + 2 * M_PI;
171 else
172 aEndAngle = angle_xform( endAngle );
173}
174
175
176const double CAIRO_GAL_BASE::xform( double x )
177{
178 double dx = m_currentWorld2Screen.xx * x;
179 double dy = m_currentWorld2Screen.yx * x;
180 return sqrt( dx * dx + dy * dy );
181}
182
183
184static double roundp( double x )
185{
186 return floor( x + 0.5 ) + 0.5;
187}
188
189
191{
193 return VECTOR2D( ::roundp( v.x ), ::roundp( v.y ) );
194 else
195 return VECTOR2D( floor( v.x + 0.5 ), floor( v.y + 0.5 ) );
196}
197
198
199void CAIRO_GAL_BASE::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
200{
202
203 VECTOR2D p0 = roundp( xform( aStartPoint ) );
204 VECTOR2D p1 = roundp( xform( aEndPoint ) );
205
206 cairo_move_to( m_currentContext, p0.x, p0.y );
207 cairo_line_to( m_currentContext, p1.x, p1.y );
208 flushPath();
209 m_isElementAdded = true;
210}
211
212
213void CAIRO_GAL_BASE::syncLineWidth( bool aForceWidth, double aWidth )
214{
215 double w = floor( xform( aForceWidth ? aWidth : m_lineWidth ) + 0.5 );
216
217 if( w <= 1.0 )
218 {
219 w = 1.0;
220 cairo_set_line_join( m_currentContext, CAIRO_LINE_JOIN_MITER );
221 cairo_set_line_cap( m_currentContext, CAIRO_LINE_CAP_BUTT );
222 cairo_set_line_width( m_currentContext, 1.0 );
223 m_lineWidthIsOdd = true;
224 }
225 else
226 {
227 cairo_set_line_join( m_currentContext, CAIRO_LINE_JOIN_ROUND );
228 cairo_set_line_cap( m_currentContext, CAIRO_LINE_CAP_ROUND );
229 cairo_set_line_width( m_currentContext, w );
230 m_lineWidthIsOdd = ( (int) w % 2 ) == 1;
231 }
232
234}
235
236
237void CAIRO_GAL_BASE::DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth )
238{
239 for( size_t i = 0; i + 1 < aPointList.size(); ++i )
240 DrawSegment( aPointList[i], aPointList[i + 1], aWidth );
241}
242
243
244void CAIRO_GAL_BASE::DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth )
245{
246 int numPoints = aLineChain.PointCount();
247
248 if( aLineChain.IsClosed() )
249 numPoints += 1;
250
251 for( int i = 0; i + 1 < numPoints; ++i )
252 DrawSegment( aLineChain.CPoint( i ), aLineChain.CPoint( i + 1 ), aWidth );
253}
254
255
256void CAIRO_GAL_BASE::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
257 double aWidth )
258{
259 if( m_isFillEnabled )
260 {
261 syncLineWidth( true, aWidth );
262
263 VECTOR2D p0 = roundp( xform( aStartPoint ) );
264 VECTOR2D p1 = roundp( xform( aEndPoint ) );
265
266 cairo_move_to( m_currentContext, p0.x, p0.y );
267 cairo_line_to( m_currentContext, p1.x, p1.y );
268 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
269 m_fillColor.a );
270 cairo_stroke( m_currentContext );
271 }
272 else
273 {
274 aWidth /= 2.0;
275 SetLineWidth( 1.0 );
277
278 // Outline mode for tracks
279 VECTOR2D startEndVector = aEndPoint - aStartPoint;
280 double lineAngle = atan2( startEndVector.y, startEndVector.x );
281
282 double sa = sin( lineAngle + M_PI / 2.0 );
283 double ca = cos( lineAngle + M_PI / 2.0 );
284
285 VECTOR2D pa0 = xform( aStartPoint + VECTOR2D( aWidth * ca, aWidth * sa ) );
286 VECTOR2D pa1 = xform( aStartPoint - VECTOR2D( aWidth * ca, aWidth * sa ) );
287 VECTOR2D pb0 = xform( aEndPoint + VECTOR2D( aWidth * ca, aWidth * sa ) );
288 VECTOR2D pb1 = xform( aEndPoint - VECTOR2D( aWidth * ca, aWidth * sa ) );
289
292
293 cairo_move_to( m_currentContext, pa0.x, pa0.y );
294 cairo_line_to( m_currentContext, pb0.x, pb0.y );
295
296 cairo_move_to( m_currentContext, pa1.x, pa1.y );
297 cairo_line_to( m_currentContext, pb1.x, pb1.y );
298 flushPath();
299
300 // Calculate the segment angle and arc center in normal/mirrored transform for rounded ends.
301 VECTOR2D center_a = xform( aStartPoint );
302 VECTOR2D center_b = xform( aEndPoint );
303 startEndVector = center_b - center_a;
304 lineAngle = atan2( startEndVector.y, startEndVector.x );
305 double radius = ( pa0 - center_a ).EuclideanNorm();
306
307 // Draw the rounded end point of the segment
308 double arcStartAngle = lineAngle - M_PI / 2.0;
309 cairo_arc( m_currentContext, center_b.x, center_b.y, radius, arcStartAngle,
310 arcStartAngle + M_PI );
311
312 // Draw the rounded start point of the segment
313 arcStartAngle = lineAngle + M_PI / 2.0;
314 cairo_arc( m_currentContext, center_a.x, center_a.y, radius, arcStartAngle,
315 arcStartAngle + M_PI );
316
317 flushPath();
318 }
319
320 m_isElementAdded = true;
321}
322
323
324void CAIRO_GAL_BASE::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
325{
327
328 VECTOR2D c = roundp( xform( aCenterPoint ) );
329 double r = ::roundp( xform( aRadius ) );
330
331 cairo_set_line_width( m_currentContext, std::min( 2.0 * r, m_lineWidthInPixels ) );
332 cairo_new_sub_path( m_currentContext );
333 cairo_arc( m_currentContext, c.x, c.y, r, 0.0, 2 * M_PI );
334 cairo_close_path( m_currentContext );
335 flushPath();
336 m_isElementAdded = true;
337}
338
339
340void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
341 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle )
342{
344
345 double startAngle = aStartAngle.AsRadians();
346 double endAngle = aEndAngle.AsRadians();
347
348 // calculate start and end arc angles according to the rotation transform matrix
349 // and normalize:
350 arc_angles_xform_and_normalize( startAngle, endAngle );
351
352 double r = xform( aRadius );
353
354 // N.B. This is backwards. We set this because we want to adjust the center
355 // point that changes both endpoints. In the worst case, this is twice as far.
356 // We cannot adjust radius or center based on the other because this causes the
357 // whole arc to change position/size
358 m_lineWidthIsOdd = !( static_cast<int>( aRadius ) % 2 );
359
360 auto mid = roundp( xform( aCenterPoint ) );
361
362 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
363 cairo_new_sub_path( m_currentContext );
364
365 if( m_isFillEnabled )
366 cairo_move_to( m_currentContext, mid.x, mid.y );
367
368 cairo_arc( m_currentContext, mid.x, mid.y, r, startAngle, endAngle );
369
370 if( m_isFillEnabled )
371 cairo_close_path( m_currentContext );
372
373 flushPath();
374
375 m_isElementAdded = true;
376}
377
378
379void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
380 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle,
381 double aWidth, double aMaxError )
382{
383 // Note: aMaxError is not used because Cairo can draw true arcs
384 if( m_isFillEnabled )
385 {
386 m_lineWidth = aWidth;
387 m_isStrokeEnabled = true;
388 m_isFillEnabled = false;
389 DrawArc( aCenterPoint, aRadius, aStartAngle, aEndAngle );
390 m_isFillEnabled = true;
391 m_isStrokeEnabled = false;
392 return;
393 }
394
396
397 // calculate start and end arc angles according to the rotation transform matrix
398 // and normalize:
399 double startAngleS = aStartAngle.AsRadians();
400 double endAngleS = aEndAngle.AsRadians();
401 arc_angles_xform_and_normalize( startAngleS, endAngleS );
402
403 double r = xform( aRadius );
404
405 // N.B. This is backwards. We set this because we want to adjust the center
406 // point that changes both endpoints. In the worst case, this is twice as far.
407 // We cannot adjust radius or center based on the other because this causes the
408 // whole arc to change position/size
409 m_lineWidthIsOdd = !( static_cast<int>( aRadius ) % 2 );
410
411 VECTOR2D mid = roundp( xform( aCenterPoint ) );
412 double width = xform( aWidth / 2.0 );
413 VECTOR2D startPointS = VECTOR2D( r, 0.0 );
414 VECTOR2D endPointS = VECTOR2D( r, 0.0 );
415 RotatePoint( startPointS, -EDA_ANGLE( startAngleS, RADIANS_T ) );
416 RotatePoint( endPointS, -EDA_ANGLE( endAngleS, RADIANS_T ) );
417
418 cairo_save( m_currentContext );
419
422
423 cairo_translate( m_currentContext, mid.x, mid.y );
424
425 cairo_new_sub_path( m_currentContext );
426 cairo_arc( m_currentContext, 0, 0, r - width, startAngleS, endAngleS );
427
428 cairo_new_sub_path( m_currentContext );
429 cairo_arc( m_currentContext, 0, 0, r + width, startAngleS, endAngleS );
430
431 cairo_new_sub_path( m_currentContext );
432 cairo_arc_negative( m_currentContext, startPointS.x, startPointS.y, width, startAngleS,
433 startAngleS + M_PI );
434
435 cairo_new_sub_path( m_currentContext );
436 cairo_arc( m_currentContext, endPointS.x, endPointS.y, width, endAngleS, endAngleS + M_PI );
437
438 cairo_restore( m_currentContext );
439 flushPath();
440
441 m_isElementAdded = true;
442}
443
444
445void CAIRO_GAL_BASE::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
446{
447 // Calculate the diagonal points
449
450 const VECTOR2D p0 = roundp( xform( aStartPoint ) );
451 const VECTOR2D p1 = roundp( xform( VECTOR2D( aEndPoint.x, aStartPoint.y ) ) );
452 const VECTOR2D p2 = roundp( xform( aEndPoint ) );
453 const VECTOR2D p3 = roundp( xform( VECTOR2D( aStartPoint.x, aEndPoint.y ) ) );
454
455 // The path is composed from 4 segments
456 cairo_move_to( m_currentContext, p0.x, p0.y );
457 cairo_line_to( m_currentContext, p1.x, p1.y );
458 cairo_line_to( m_currentContext, p2.x, p2.y );
459 cairo_line_to( m_currentContext, p3.x, p3.y );
460 cairo_close_path( m_currentContext );
461 flushPath();
462
463 m_isElementAdded = true;
464}
465
466
467void CAIRO_GAL_BASE::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
468{
469 for( int i = 0; i < aPolySet.OutlineCount(); ++i )
470 drawPoly( aPolySet.COutline( i ) );
471}
472
473
475{
476 drawPoly( aPolygon );
477}
478
479
480void CAIRO_GAL_BASE::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
481 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
482 double aFilterValue )
483{
484 // Note: aFilterValue is not used because the cubic Bezier curve is
485 // supported by Cairo.
487
488 const VECTOR2D sp = roundp( xform( aStartPoint ) );
489 const VECTOR2D cpa = roundp( xform( aControlPointA ) );
490 const VECTOR2D cpb = roundp( xform( aControlPointB ) );
491 const VECTOR2D ep = roundp( xform( aEndPoint ) );
492
493 cairo_move_to( m_currentContext, sp.x, sp.y );
494 cairo_curve_to( m_currentContext, cpa.x, cpa.y, cpb.x, cpb.y, ep.x, ep.y );
495 cairo_line_to( m_currentContext, ep.x, ep.y );
496
497 flushPath();
498 m_isElementAdded = true;
499}
500
501
502void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
503{
504 cairo_save( m_currentContext );
505
506 alphaBlend = std::clamp( alphaBlend, 0.0, 1.0 );
507
508 // We have to calculate the pixel size in users units to draw the image.
509 // m_worldUnitLength is a factor used for converting IU to inches
510 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
511
512 // The position of the bitmap is the bitmap center.
513 // move the draw origin to the top left bitmap corner:
514 int w = aBitmap.GetSizePixels().x;
515 int h = aBitmap.GetSizePixels().y;
516
517 cairo_set_matrix( m_currentContext, &m_currentWorld2Screen );
518 cairo_scale( m_currentContext, scale, scale );
519 cairo_translate( m_currentContext, -w / 2.0, -h / 2.0 );
520
521 cairo_new_path( m_currentContext );
522 cairo_surface_t* image = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w, h );
523 cairo_surface_flush( image );
524
525 unsigned char* pix_buffer = cairo_image_surface_get_data( image );
526
527 // The pixel buffer of the initial bitmap:
528 const wxImage& bm_pix_buffer = *aBitmap.GetImageData();
529
530 uint32_t mask_color = ( bm_pix_buffer.GetMaskRed() << 16 )
531 + ( bm_pix_buffer.GetMaskGreen() << 8 ) + ( bm_pix_buffer.GetMaskBlue() );
532
533 // Copy the source bitmap to the cairo bitmap buffer.
534 // In cairo bitmap buffer, a ARGB32 bitmap is an ARGB pixel packed into a uint_32
535 // 24 low bits only are used for color, top 8 are transparency.
536 for( int row = 0; row < h; row++ )
537 {
538 for( int col = 0; col < w; col++ )
539 {
540 // Build the RGB24 pixel:
541 uint32_t pixel = bm_pix_buffer.GetRed( col, row ) << 16;
542 pixel += bm_pix_buffer.GetGreen( col, row ) << 8;
543 pixel += bm_pix_buffer.GetBlue( col, row );
544
545 if( bm_pix_buffer.HasAlpha() )
546 pixel += bm_pix_buffer.GetAlpha( col, row ) << 24;
547 else if( bm_pix_buffer.HasMask() && pixel == mask_color )
548 pixel += ( wxALPHA_TRANSPARENT << 24 );
549 else
550 pixel += ( wxALPHA_OPAQUE << 24 );
551
552 // Write the pixel to the cairo image buffer:
553 uint32_t* pix_ptr = (uint32_t*) pix_buffer;
554 *pix_ptr = pixel;
555 pix_buffer += 4;
556 }
557 }
558
559 cairo_surface_mark_dirty( image );
560 cairo_set_source_surface( m_currentContext, image, 0, 0 );
561 cairo_paint_with_alpha( m_currentContext, alphaBlend );
562
563 // store the image handle so it can be destroyed later
564 m_imageSurfaces.push_back( image );
565
566 m_isElementAdded = true;
567
568 cairo_restore( m_currentContext );
569}
570
571
572void CAIRO_GAL_BASE::ResizeScreen( int aWidth, int aHeight )
573{
574 m_screenSize = VECTOR2I( aWidth, aHeight );
575}
576
577
579{
580 storePath();
581}
582
583
585{
586 cairo_set_source_rgb( m_currentContext, m_clearColor.r, m_clearColor.g, m_clearColor.b );
587 cairo_rectangle( m_currentContext, 0.0, 0.0, m_screenSize.x, m_screenSize.y );
588 cairo_fill( m_currentContext );
589}
590
591
592void CAIRO_GAL_BASE::SetIsFill( bool aIsFillEnabled )
593{
594 storePath();
595 m_isFillEnabled = aIsFillEnabled;
596
597 if( m_isGrouping )
598 {
599 GROUP_ELEMENT groupElement;
600 groupElement.m_Command = CMD_SET_FILL;
601 groupElement.m_Argument.BoolArg = aIsFillEnabled;
602 m_currentGroup->push_back( groupElement );
603 }
604}
605
606
607void CAIRO_GAL_BASE::SetIsStroke( bool aIsStrokeEnabled )
608{
609 storePath();
610 m_isStrokeEnabled = aIsStrokeEnabled;
611
612 if( m_isGrouping )
613 {
614 GROUP_ELEMENT groupElement;
615 groupElement.m_Command = CMD_SET_STROKE;
616 groupElement.m_Argument.BoolArg = aIsStrokeEnabled;
617 m_currentGroup->push_back( groupElement );
618 }
619}
620
621
623{
624 storePath();
625 m_strokeColor = aColor;
626
627 if( m_isGrouping )
628 {
629 GROUP_ELEMENT groupElement;
630 groupElement.m_Command = CMD_SET_STROKECOLOR;
631 groupElement.m_Argument.DblArg[0] = m_strokeColor.r;
632 groupElement.m_Argument.DblArg[1] = m_strokeColor.g;
633 groupElement.m_Argument.DblArg[2] = m_strokeColor.b;
634 groupElement.m_Argument.DblArg[3] = m_strokeColor.a;
635 m_currentGroup->push_back( groupElement );
636 }
637}
638
639
641{
642 storePath();
643 m_fillColor = aColor;
644
645 if( m_isGrouping )
646 {
647 GROUP_ELEMENT groupElement;
648 groupElement.m_Command = CMD_SET_FILLCOLOR;
649 groupElement.m_Argument.DblArg[0] = m_fillColor.r;
650 groupElement.m_Argument.DblArg[1] = m_fillColor.g;
651 groupElement.m_Argument.DblArg[2] = m_fillColor.b;
652 groupElement.m_Argument.DblArg[3] = m_fillColor.a;
653 m_currentGroup->push_back( groupElement );
654 }
655}
656
657
658void CAIRO_GAL_BASE::SetLineWidth( float aLineWidth )
659{
660 storePath();
661 GAL::SetLineWidth( aLineWidth );
662
663 if( m_isGrouping )
664 {
665 GROUP_ELEMENT groupElement;
666 groupElement.m_Command = CMD_SET_LINE_WIDTH;
667 groupElement.m_Argument.DblArg[0] = aLineWidth;
668 m_currentGroup->push_back( groupElement );
669 }
670 else
671 {
672 m_lineWidth = aLineWidth;
673 }
674}
675
676
677void CAIRO_GAL_BASE::SetLayerDepth( double aLayerDepth )
678{
679 super::SetLayerDepth( aLayerDepth );
680 storePath();
681}
682
683
684void CAIRO_GAL_BASE::Transform( const MATRIX3x3D& aTransformation )
685{
686 cairo_matrix_t cairoTransformation, newXform;
687
688 cairo_matrix_init( &cairoTransformation, aTransformation.m_data[0][0],
689 aTransformation.m_data[1][0], aTransformation.m_data[0][1],
690 aTransformation.m_data[1][1], aTransformation.m_data[0][2],
691 aTransformation.m_data[1][2] );
692
693 cairo_matrix_multiply( &newXform, &m_currentXform, &cairoTransformation );
694 m_currentXform = newXform;
696}
697
698
699void CAIRO_GAL_BASE::Rotate( double aAngle )
700{
701 storePath();
702
703 if( m_isGrouping )
704 {
705 GROUP_ELEMENT groupElement;
706 groupElement.m_Command = CMD_ROTATE;
707 groupElement.m_Argument.DblArg[0] = aAngle;
708 m_currentGroup->push_back( groupElement );
709 }
710 else
711 {
712 cairo_matrix_rotate( &m_currentXform, aAngle );
714 }
715}
716
717
718void CAIRO_GAL_BASE::Translate( const VECTOR2D& aTranslation )
719{
720 storePath();
721
722 if( m_isGrouping )
723 {
724 GROUP_ELEMENT groupElement;
725 groupElement.m_Command = CMD_TRANSLATE;
726 groupElement.m_Argument.DblArg[0] = aTranslation.x;
727 groupElement.m_Argument.DblArg[1] = aTranslation.y;
728 m_currentGroup->push_back( groupElement );
729 }
730 else
731 {
732 cairo_matrix_translate( &m_currentXform, aTranslation.x, aTranslation.y );
734 }
735}
736
737
738void CAIRO_GAL_BASE::Scale( const VECTOR2D& aScale )
739{
740 storePath();
741
742 if( m_isGrouping )
743 {
744 GROUP_ELEMENT groupElement;
745 groupElement.m_Command = CMD_SCALE;
746 groupElement.m_Argument.DblArg[0] = aScale.x;
747 groupElement.m_Argument.DblArg[1] = aScale.y;
748 m_currentGroup->push_back( groupElement );
749 }
750 else
751 {
752 cairo_matrix_scale( &m_currentXform, aScale.x, aScale.y );
754 }
755}
756
757
759{
760 storePath();
761
762 if( m_isGrouping )
763 {
764 GROUP_ELEMENT groupElement;
765 groupElement.m_Command = CMD_SAVE;
766 m_currentGroup->push_back( groupElement );
767 }
768 else
769 {
770 m_xformStack.push_back( m_currentXform );
772 }
773}
774
775
777{
778 storePath();
779
780 if( m_isGrouping )
781 {
782 GROUP_ELEMENT groupElement;
783 groupElement.m_Command = CMD_RESTORE;
784 m_currentGroup->push_back( groupElement );
785 }
786 else
787 {
788 if( !m_xformStack.empty() )
789 {
791 m_xformStack.pop_back();
793 }
794 }
795}
796
797
799{
800 // If the grouping is started: the actual path is stored in the group, when
801 // a attribute was changed or when grouping stops with the end group method.
802 storePath();
803
804 GROUP group;
805 int groupNumber = getNewGroupNumber();
806 m_groups.insert( std::make_pair( groupNumber, group ) );
807 m_currentGroup = &m_groups[groupNumber];
808 m_isGrouping = true;
809
810 return groupNumber;
811}
812
813
815{
816 storePath();
817 m_isGrouping = false;
818}
819
820
821void CAIRO_GAL_BASE::DrawGroup( int aGroupNumber )
822{
823 // This method implements a small Virtual Machine - all stored commands
824 // are executed; nested calling is also possible
825
826 storePath();
827
828 for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
829 {
830 switch( it->m_Command )
831 {
832 case CMD_SET_FILL:
833 m_isFillEnabled = it->m_Argument.BoolArg;
834 break;
835
836 case CMD_SET_STROKE:
837 m_isStrokeEnabled = it->m_Argument.BoolArg;
838 break;
839
841 m_fillColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
842 it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
843 break;
844
846 m_strokeColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
847 it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
848 break;
849
851 {
852 // Make lines appear at least 1 pixel wide, no matter of zoom
853 double x = 1.0, y = 1.0;
854 cairo_device_to_user_distance( m_currentContext, &x, &y );
855 double minWidth = std::min( fabs( x ), fabs( y ) );
856 cairo_set_line_width( m_currentContext,
857 std::max( it->m_Argument.DblArg[0], minWidth ) );
858 break;
859 }
860
861
862 case CMD_STROKE_PATH:
863 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
865 cairo_append_path( m_currentContext, it->m_CairoPath );
866 cairo_stroke( m_currentContext );
867 break;
868
869 case CMD_FILL_PATH:
870 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
872 cairo_append_path( m_currentContext, it->m_CairoPath );
873 cairo_fill( m_currentContext );
874 break;
875
876 /*
877 case CMD_TRANSFORM:
878 cairo_matrix_t matrix;
879 cairo_matrix_init( &matrix, it->argument.DblArg[0], it->argument.DblArg[1],
880 it->argument.DblArg[2], it->argument.DblArg[3],
881 it->argument.DblArg[4], it->argument.DblArg[5] );
882 cairo_transform( m_currentContext, &matrix );
883 break;
884 */
885
886 case CMD_ROTATE:
887 cairo_rotate( m_currentContext, it->m_Argument.DblArg[0] );
888 break;
889
890 case CMD_TRANSLATE:
891 cairo_translate( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
892 break;
893
894 case CMD_SCALE:
895 cairo_scale( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
896 break;
897
898 case CMD_SAVE:
899 cairo_save( m_currentContext );
900 break;
901
902 case CMD_RESTORE:
903 cairo_restore( m_currentContext );
904 break;
905
906 case CMD_CALL_GROUP:
907 DrawGroup( it->m_Argument.IntArg );
908 break;
909 }
910 }
911}
912
913
914void CAIRO_GAL_BASE::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
915{
916 storePath();
917
918 for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
919 {
920 if( it->m_Command == CMD_SET_FILLCOLOR || it->m_Command == CMD_SET_STROKECOLOR )
921 {
922 it->m_Argument.DblArg[0] = aNewColor.r;
923 it->m_Argument.DblArg[1] = aNewColor.g;
924 it->m_Argument.DblArg[2] = aNewColor.b;
925 it->m_Argument.DblArg[3] = aNewColor.a;
926 }
927 }
928}
929
930
931void CAIRO_GAL_BASE::ChangeGroupDepth( int aGroupNumber, int aDepth )
932{
933 // Cairo does not have any possibilities to change the depth coordinate of stored items,
934 // it depends only on the order of drawing
935}
936
937
938void CAIRO_GAL_BASE::DeleteGroup( int aGroupNumber )
939{
940 storePath();
941
942 // Delete the Cairo paths
943 std::deque<GROUP_ELEMENT>::iterator it, end;
944
945 for( it = m_groups[aGroupNumber].begin(), end = m_groups[aGroupNumber].end(); it != end; ++it )
946 {
947 if( it->m_Command == CMD_FILL_PATH || it->m_Command == CMD_STROKE_PATH )
948 cairo_path_destroy( it->m_CairoPath );
949 }
950
951 // Delete the group
952 m_groups.erase( aGroupNumber );
953}
954
955
957{
958 for( auto it = m_groups.begin(); it != m_groups.end(); )
959 DeleteGroup( ( it++ )->first );
960}
961
962
964{
965 cairo_set_operator( m_currentContext, aSetting ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_OVER );
966}
967
968
970{
973}
974
975
977{
978 m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_ADD );
979}
980
981
983{
986}
987
988
990{
991 m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_OVER );
992}
993
994
995void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition )
996{
997 m_cursorPosition = aCursorPosition;
998}
999
1000
1002{
1003}
1004
1005
1007{
1008 for( _cairo_surface* imageSurface : m_imageSurfaces )
1009 cairo_surface_destroy( imageSurface );
1010
1011 m_imageSurfaces.clear();
1012
1013 ClearScreen();
1014
1015 // Compute the world <-> screen transformations
1017
1018 cairo_matrix_init( &m_cairoWorldScreenMatrix, m_worldScreenMatrix.m_data[0][0],
1022
1023 // we work in screen-space coordinates and do the transforms outside.
1024 cairo_identity_matrix( m_context );
1025
1026 cairo_matrix_init_identity( &m_currentXform );
1027
1028 // Start drawing with a new path
1029 cairo_new_path( m_context );
1030 m_isElementAdded = true;
1031
1033
1034 m_lineWidth = 0;
1035}
1036
1037
1038void CAIRO_GAL_BASE::drawAxes( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1039{
1040 syncLineWidth();
1041
1042 VECTOR2D p0 = roundp( xform( aStartPoint ) );
1043 VECTOR2D p1 = roundp( xform( aEndPoint ) );
1044 VECTOR2D org = roundp( xform( VECTOR2D( 0.0, 0.0 ) ) ); // Axis origin = 0,0 coord
1045
1046 cairo_set_source_rgba( m_currentContext, m_axesColor.r, m_axesColor.g, m_axesColor.b,
1047 m_axesColor.a );
1048 cairo_move_to( m_currentContext, p0.x, org.y );
1049 cairo_line_to( m_currentContext, p1.x, org.y );
1050 cairo_move_to( m_currentContext, org.x, p0.y );
1051 cairo_line_to( m_currentContext, org.x, p1.y );
1052 cairo_stroke( m_currentContext );
1053}
1054
1055
1056void CAIRO_GAL_BASE::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1057{
1058 syncLineWidth();
1059 VECTOR2D p0 = roundp( xform( aStartPoint ) );
1060 VECTOR2D p1 = roundp( xform( aEndPoint ) );
1061
1062 cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1063 m_gridColor.a );
1064 cairo_move_to( m_currentContext, p0.x, p0.y );
1065 cairo_line_to( m_currentContext, p1.x, p1.y );
1066 cairo_stroke( m_currentContext );
1067}
1068
1069
1071{
1072 syncLineWidth();
1073 VECTOR2D offset( 0, 0 );
1074 double size = 2.0 * m_lineWidthInPixels + 0.5;
1075
1076 VECTOR2D p0 = roundp( xform( aPoint ) ) - VECTOR2D( size, 0 ) + offset;
1077 VECTOR2D p1 = roundp( xform( aPoint ) ) + VECTOR2D( size, 0 ) + offset;
1078 VECTOR2D p2 = roundp( xform( aPoint ) ) - VECTOR2D( 0, size ) + offset;
1079 VECTOR2D p3 = roundp( xform( aPoint ) ) + VECTOR2D( 0, size ) + offset;
1080
1081 cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1082 m_gridColor.a );
1083 cairo_move_to( m_currentContext, p0.x, p0.y );
1084 cairo_line_to( m_currentContext, p1.x, p1.y );
1085 cairo_move_to( m_currentContext, p2.x, p2.y );
1086 cairo_line_to( m_currentContext, p3.x, p3.y );
1087 cairo_stroke( m_currentContext );
1088}
1089
1090
1091void CAIRO_GAL_BASE::drawGridPoint( const VECTOR2D& aPoint, double aWidth, double aHeight )
1092{
1093 VECTOR2D p = roundp( xform( aPoint ) );
1094
1095 double sw = std::max( 1.0, aWidth );
1096 double sh = std::max( 1.0, aHeight );
1097
1098 cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1099 m_gridColor.a );
1100 cairo_rectangle( m_currentContext, p.x - std::floor( sw / 2 ) - 0.5,
1101 p.y - std::floor( sh / 2 ) - 0.5, sw, sh );
1102
1103 cairo_fill( m_currentContext );
1104}
1105
1106
1108{
1109 if( m_isFillEnabled )
1110 {
1111 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
1112 m_fillColor.a );
1113
1114 if( m_isStrokeEnabled )
1115 {
1116 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
1117 cairo_fill_preserve( m_currentContext );
1118 }
1119 else
1120 {
1121 cairo_fill( m_currentContext );
1122 }
1123 }
1124
1125 if( m_isStrokeEnabled )
1126 {
1127 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
1128 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
1129 m_strokeColor.a );
1130 cairo_stroke( m_currentContext );
1131 }
1132}
1133
1134
1136{
1137 if( m_isElementAdded )
1138 {
1139 m_isElementAdded = false;
1140
1141 if( !m_isGrouping )
1142 {
1143 if( m_isFillEnabled )
1144 {
1145 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g,
1147 cairo_fill_preserve( m_currentContext );
1148 }
1149
1150 if( m_isStrokeEnabled )
1151 {
1152 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
1154 cairo_stroke_preserve( m_currentContext );
1155 }
1156 }
1157 else
1158 {
1159 // Copy the actual path, append it to the global path list
1160 // then check, if the path needs to be stroked/filled and
1161 // add this command to the group list;
1162 if( m_isStrokeEnabled )
1163 {
1164 GROUP_ELEMENT groupElement;
1165 groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1166 groupElement.m_Command = CMD_STROKE_PATH;
1167 m_currentGroup->push_back( groupElement );
1168 }
1169
1170 if( m_isFillEnabled )
1171 {
1172 GROUP_ELEMENT groupElement;
1173 groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1174 groupElement.m_Command = CMD_FILL_PATH;
1175 m_currentGroup->push_back( groupElement );
1176 }
1177 }
1178
1179 cairo_new_path( m_currentContext );
1180 }
1181}
1182
1183
1184void CAIRO_GAL_BASE::blitCursor( wxMemoryDC& clientDC )
1185{
1186 if( !IsCursorEnabled() )
1187 return;
1188
1190 const COLOR4D cColor = getCursorColor();
1191 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
1192
1193 wxColour color( cColor.r * cColor.a * 255, cColor.g * cColor.a * 255, cColor.b * cColor.a * 255,
1194 255 );
1195 clientDC.SetPen( wxPen( color ) );
1196 clientDC.DrawLine( p.x - cursorSize / 2, p.y, p.x + cursorSize / 2, p.y );
1197 clientDC.DrawLine( p.x, p.y - cursorSize / 2, p.x, p.y + cursorSize / 2 );
1198}
1199
1200
1201void CAIRO_GAL_BASE::drawPoly( const std::deque<VECTOR2D>& aPointList )
1202{
1203 wxCHECK( aPointList.size() > 1, /* void */ );
1204
1205 // Iterate over the point list and draw the segments
1206 std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
1207
1208 syncLineWidth();
1209
1210 const VECTOR2D p = roundp( xform( it->x, it->y ) );
1211
1212 cairo_move_to( m_currentContext, p.x, p.y );
1213
1214 for( ++it; it != aPointList.end(); ++it )
1215 {
1216 const VECTOR2D p2 = roundp( xform( it->x, it->y ) );
1217
1218 cairo_line_to( m_currentContext, p2.x, p2.y );
1219 }
1220
1221 flushPath();
1222 m_isElementAdded = true;
1223}
1224
1225
1226void CAIRO_GAL_BASE::drawPoly( const std::vector<VECTOR2D>& aPointList )
1227{
1228 wxCHECK( aPointList.size() > 1, /* void */ );
1229
1230 // Iterate over the point list and draw the segments
1231 std::vector<VECTOR2D>::const_iterator it = aPointList.begin();
1232
1233 syncLineWidth();
1234
1235 const VECTOR2D p = roundp( xform( it->x, it->y ) );
1236
1237 cairo_move_to( m_currentContext, p.x, p.y );
1238
1239 for( ++it; it != aPointList.end(); ++it )
1240 {
1241 const VECTOR2D p2 = roundp( xform( it->x, it->y ) );
1242
1243 cairo_line_to( m_currentContext, p2.x, p2.y );
1244 }
1245
1246 flushPath();
1247 m_isElementAdded = true;
1248}
1249
1250
1251void CAIRO_GAL_BASE::drawPoly( const VECTOR2D aPointList[], int aListSize )
1252{
1253 wxCHECK( aListSize > 1, /* void */ );
1254
1255 // Iterate over the point list and draw the segments
1256 const VECTOR2D* ptr = aPointList;
1257
1258 syncLineWidth();
1259
1260 const VECTOR2D p = roundp( xform( ptr->x, ptr->y ) );
1261 cairo_move_to( m_currentContext, p.x, p.y );
1262
1263 for( int i = 1; i < aListSize; ++i )
1264 {
1265 ++ptr;
1266 const VECTOR2D p2 = roundp( xform( ptr->x, ptr->y ) );
1267 cairo_line_to( m_currentContext, p2.x, p2.y );
1268 }
1269
1270 flushPath();
1271 m_isElementAdded = true;
1272}
1273
1274
1276{
1277 wxCHECK( aLineChain.PointCount() > 1, /* void */ );
1278
1279 syncLineWidth();
1280
1281 auto numPoints = aLineChain.PointCount();
1282
1283 if( aLineChain.IsClosed() )
1284 numPoints += 1;
1285
1286 const VECTOR2I start = aLineChain.CPoint( 0 );
1287 const VECTOR2D p = roundp( xform( start.x, start.y ) );
1288 cairo_move_to( m_currentContext, p.x, p.y );
1289
1290 for( int i = 1; i < numPoints; ++i )
1291 {
1292 const VECTOR2I& pw = aLineChain.CPoint( i );
1293 const VECTOR2D ps = roundp( xform( pw.x, pw.y ) );
1294 cairo_line_to( m_currentContext, ps.x, ps.y );
1295 }
1296
1297 flushPath();
1298 m_isElementAdded = true;
1299}
1300
1301
1303{
1304 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
1305 wxT( "There are no free slots to store a group" ) );
1306
1307 while( m_groups.find( m_groupCounter ) != m_groups.end() )
1309
1310 return m_groupCounter++;
1311}
1312
1313
1314CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
1315 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
1316 const wxString& aName ) :
1317 CAIRO_GAL_BASE( aDisplayOptions ),
1318 wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName )
1319{
1320 // Initialise compositing state
1321 m_mainBuffer = 0;
1322 m_overlayBuffer = 0;
1323 m_tempBuffer = 0;
1324 m_savedBuffer = 0;
1325 m_validCompositor = false;
1328
1329 m_bitmapBuffer = nullptr;
1330 m_wxOutput = nullptr;
1331
1332 m_parentWindow = aParent;
1333 m_mouseListener = aMouseListener;
1334 m_paintListener = aPaintListener;
1335
1336 // Connect the native cursor handler
1337 Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( CAIRO_GAL::onSetNativeCursor ), nullptr,
1338 this );
1339
1340 // Connecting the event handlers
1341 Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) );
1342
1343 // Mouse events are skipped to the parent
1344 Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1345 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1346 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1347 Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1348 Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1349 Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1350 Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1351 Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1352 Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1353 Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1354 Connect( wxEVT_AUX1_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1355 Connect( wxEVT_AUX1_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1356 Connect( wxEVT_AUX1_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1357 Connect( wxEVT_AUX2_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1358 Connect( wxEVT_AUX2_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1359 Connect( wxEVT_AUX2_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1360 Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1361#if defined _WIN32 || defined _WIN64
1362 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1363#endif
1364
1365 SetSize( aParent->GetClientSize() );
1366 m_screenSize = VECTOR2I( aParent->GetClientSize() );
1367
1368 // Allocate memory for pixel storage
1370
1371 m_isInitialized = false;
1372}
1373
1374
1376{
1377 deleteBitmaps();
1378}
1379
1380
1382{
1383 initSurface();
1384
1386
1387 if( !m_validCompositor )
1388 setCompositor();
1389
1390 m_compositor->SetMainContext( m_context );
1391 m_compositor->SetBuffer( m_mainBuffer );
1392}
1393
1394
1396{
1398
1399 // Merge buffers on the screen
1400 m_compositor->DrawBuffer( m_mainBuffer );
1401 m_compositor->DrawBuffer( m_overlayBuffer );
1402
1403 // Now translate the raw context data from the format stored
1404 // by cairo into a format understood by wxImage.
1405 pixman_image_t* dstImg = pixman_image_create_bits(
1406 wxPlatformInfo::Get().GetEndianness() == wxENDIAN_LITTLE ? PIXMAN_b8g8r8
1407 : PIXMAN_r8g8b8,
1409 pixman_image_t* srcImg =
1410 pixman_image_create_bits( PIXMAN_a8r8g8b8, m_screenSize.x, m_screenSize.y,
1411 (uint32_t*) m_bitmapBuffer, m_wxBufferWidth * 4 );
1412
1413 pixman_image_composite( PIXMAN_OP_SRC, srcImg, nullptr, dstImg, 0, 0, 0, 0, 0, 0,
1415
1416 // Free allocated memory
1417 pixman_image_unref( srcImg );
1418 pixman_image_unref( dstImg );
1419
1420 wxImage img( m_wxBufferWidth, m_screenSize.y, m_wxOutput, true );
1421 wxBitmap bmp( img );
1422 wxMemoryDC mdc( bmp );
1423 wxClientDC clientDC( this );
1424
1425 // Now it is the time to blit the mouse cursor
1426 blitCursor( mdc );
1427 clientDC.Blit( 0, 0, m_screenSize.x, m_screenSize.y, &mdc, 0, 0, wxCOPY );
1428
1429 deinitSurface();
1430}
1431
1432
1433void CAIRO_GAL::PostPaint( wxPaintEvent& aEvent )
1434{
1435 // posts an event to m_paint_listener to ask for redraw the canvas.
1436 if( m_paintListener )
1437 wxPostEvent( m_paintListener, aEvent );
1438}
1439
1440
1441void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
1442{
1443 CAIRO_GAL_BASE::ResizeScreen( aWidth, aHeight );
1444
1445 // Recreate the bitmaps
1446 deleteBitmaps();
1448
1449 if( m_validCompositor )
1450 m_compositor->Resize( aWidth, aHeight );
1451
1452 m_validCompositor = false;
1453
1454 SetSize( wxSize( aWidth, aHeight ) );
1455}
1456
1457
1458bool CAIRO_GAL::Show( bool aShow )
1459{
1460 bool s = wxWindow::Show( aShow );
1461
1462 if( aShow )
1463 wxWindow::Raise();
1464
1465 return s;
1466}
1467
1468
1470{
1471 initSurface();
1473}
1474
1475
1477{
1479 deinitSurface();
1480}
1481
1482
1484{
1485 // If the compositor is not set, that means that there is a recaching process going on
1486 // and we do not need the compositor now
1487 if( !m_validCompositor )
1488 return;
1489
1490 // Cairo grouping prevents display of overlapping items on the same layer in the lighter color
1491 if( m_isInitialized )
1492 storePath();
1493
1494 switch( aTarget )
1495 {
1496 default:
1497 case TARGET_CACHED:
1498 case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1499 case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1500 case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
1501 }
1502
1503 m_currentTarget = aTarget;
1504}
1505
1506
1508{
1509 return m_currentTarget;
1510}
1511
1512
1514{
1515 // Save the current state
1516 unsigned int currentBuffer = m_compositor->GetBuffer();
1517
1518 switch( aTarget )
1519 {
1520 // Cached and noncached items are rendered to the same buffer
1521 default:
1522 case TARGET_CACHED:
1523 case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1524 case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1525 case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
1526 }
1527
1528 m_compositor->ClearBuffer( COLOR4D::BLACK );
1529
1530 // Restore the previous state
1531 m_compositor->SetBuffer( currentBuffer );
1532}
1533
1534
1536{
1537 if( m_isInitialized )
1538 return;
1539
1540 m_surface = cairo_image_surface_create_for_data( m_bitmapBuffer, GAL_FORMAT, m_wxBufferWidth,
1542
1543 m_context = cairo_create( m_surface );
1544
1545#ifdef DEBUG
1546 cairo_status_t status = cairo_status( m_context );
1547 wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) );
1548#endif /* DEBUG */
1549
1551
1552 m_isInitialized = true;
1553}
1554
1555
1557{
1558 if( !m_isInitialized )
1559 return;
1560
1561 cairo_destroy( m_context );
1562 m_context = nullptr;
1563 cairo_surface_destroy( m_surface );
1564 m_surface = nullptr;
1565
1566 m_isInitialized = false;
1567}
1568
1569
1571{
1573
1574 while( ( ( m_wxBufferWidth * 3 ) % 4 ) != 0 )
1576
1577 // Create buffer, use the system independent Cairo context backend
1578 m_stride = cairo_format_stride_for_width( GAL_FORMAT, m_wxBufferWidth );
1580
1581 wxASSERT( m_bitmapBuffer == nullptr );
1582 m_bitmapBuffer = new unsigned char[m_bufferSize * 4];
1583
1584 wxASSERT( m_wxOutput == nullptr );
1585 m_wxOutput = new unsigned char[m_wxBufferWidth * 3 * m_screenSize.y];
1586}
1587
1588
1590{
1591 delete[] m_bitmapBuffer;
1592 m_bitmapBuffer = nullptr;
1593
1594 delete[] m_wxOutput;
1595 m_wxOutput = nullptr;
1596}
1597
1598
1600{
1601 // Recreate the compositor with the new Cairo context
1604 m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
1605
1606 // Prepare buffers
1607 m_mainBuffer = m_compositor->CreateBuffer();
1608 m_overlayBuffer = m_compositor->CreateBuffer();
1609 m_tempBuffer = m_compositor->CreateBuffer();
1610
1611 m_validCompositor = true;
1612}
1613
1614
1615void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
1616{
1617 PostPaint( aEvent );
1618}
1619
1620
1621void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1622{
1623 // Post the mouse event to the event listener registered in constructor, if any
1624 if( m_mouseListener )
1625 wxPostEvent( m_mouseListener, aEvent );
1626}
1627
1628
1630{
1631 bool refresh = false;
1632
1633 if( m_validCompositor &&
1634 aOptions.cairo_antialiasing_mode != m_compositor->GetAntialiasingMode() )
1635 {
1636 m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
1637 m_validCompositor = false;
1638 deinitSurface();
1639
1640 refresh = true;
1641 }
1642
1643 if( super::updatedGalDisplayOptions( aOptions ) )
1644 {
1645 Refresh();
1646 refresh = true;
1647 }
1648
1649 return refresh;
1650}
1651
1652
1654{
1655 // Store the current cursor type and get the wxCursor for it
1656 if( !GAL::SetNativeCursorStyle( aCursor ) )
1657 return false;
1658
1660
1661 // Update the cursor in the wx control
1662 wxWindow::SetCursor( m_currentwxCursor );
1663
1664 return true;
1665}
1666
1667
1668void CAIRO_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
1669{
1670 aEvent.SetCursor( m_currentwxCursor );
1671}
1672
1673
1675{
1677
1678 // Draw the grid
1679 // For the drawing the start points, end points and increments have
1680 // to be calculated in world coordinates
1681 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1683
1684 // Compute the line marker or point radius of the grid
1685 // Note: generic grids can't handle sub-pixel lines without
1686 // either losing fine/course distinction or having some dots
1687 // fail to render
1688 float marker = std::fmax( 1.0f, m_gridLineWidth ) / m_worldScale;
1689 float doubleMarker = 2.0f * marker;
1690
1691 // Draw axes if desired
1692 if( m_axesEnabled )
1693 {
1694 SetLineWidth( marker );
1695 drawAxes( worldStartPoint, worldEndPoint );
1696 }
1697
1698 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1699 return;
1700
1701 VECTOR2D gridScreenSize( m_gridSize );
1702
1703 double gridThreshold = KiROUND( computeMinGridSpacing() / m_worldScale );
1704
1706 gridThreshold *= 2.0;
1707
1708 // If we cannot display the grid density, scale down by a tick size and
1709 // try again. Eventually, we get some representation of the grid
1710 while( std::min( gridScreenSize.x, gridScreenSize.y ) <= gridThreshold )
1711 {
1712 gridScreenSize = gridScreenSize * static_cast<double>( m_gridTick );
1713 }
1714
1715 // Compute grid starting and ending indexes to draw grid points on the
1716 // visible screen area
1717 // Note: later any point coordinate will be offsetted by m_gridOrigin
1718 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1719 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1720 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1721 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1722
1723 // Ensure start coordinate > end coordinate
1724 SWAP( gridStartX, >, gridEndX );
1725 SWAP( gridStartY, >, gridEndY );
1726
1727 // Ensure the grid fills the screen
1728 --gridStartX;
1729 ++gridEndX;
1730 --gridStartY;
1731 ++gridEndY;
1732
1733 // Draw the grid behind all other layers
1734 SetLayerDepth( m_depthRange.y * 0.75 );
1735
1737 {
1738 // Now draw the grid, every coarse grid line gets the double width
1739
1740 // Vertical lines
1741 for( int j = gridStartY; j <= gridEndY; j++ )
1742 {
1743 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1744
1745 if( m_axesEnabled && y == 0.0 )
1746 continue;
1747
1748 SetLineWidth( ( j % m_gridTick ) ? marker : doubleMarker );
1749 drawGridLine( VECTOR2D( gridStartX * gridScreenSize.x + m_gridOrigin.x, y ),
1750 VECTOR2D( gridEndX * gridScreenSize.x + m_gridOrigin.x, y ) );
1751 }
1752
1753 // Horizontal lines
1754 for( int i = gridStartX; i <= gridEndX; i++ )
1755 {
1756 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1757
1758 if( m_axesEnabled && x == 0.0 )
1759 continue;
1760
1761 SetLineWidth( ( i % m_gridTick ) ? marker : doubleMarker );
1762 drawGridLine( VECTOR2D( x, gridStartY * gridScreenSize.y + m_gridOrigin.y ),
1763 VECTOR2D( x, gridEndY * gridScreenSize.y + m_gridOrigin.y ) );
1764 }
1765 }
1766 else // Dots or Crosses grid
1767 {
1768 m_lineWidthIsOdd = true;
1769 m_isStrokeEnabled = true;
1770
1771 for( int j = gridStartY; j <= gridEndY; j++ )
1772 {
1773 bool tickY = ( j % m_gridTick == 0 );
1774
1775 for( int i = gridStartX; i <= gridEndX; i++ )
1776 {
1777 bool tickX = ( i % m_gridTick == 0 );
1778 VECTOR2D pos{ i * gridScreenSize.x + m_gridOrigin.x,
1779 j * gridScreenSize.y + m_gridOrigin.y };
1780
1782 {
1783 SetLineWidth( ( tickX && tickY ) ? doubleMarker : marker );
1784 drawGridCross( pos );
1785 }
1786 else if( m_gridStyle == GRID_STYLE::DOTS )
1787 {
1788 double doubleGridLineWidth = m_gridLineWidth * 2.0f;
1789 drawGridPoint( pos, ( tickX ) ? doubleGridLineWidth : m_gridLineWidth,
1790 ( tickY ) ? doubleGridLineWidth : m_gridLineWidth );
1791 }
1792 }
1793 }
1794 }
1795}
1796
1797
1798void CAIRO_GAL_BASE::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
1799{
1800 if( aGlyph.IsStroke() )
1801 {
1802 const KIFONT::STROKE_GLYPH& glyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
1803
1804 for( const std::vector<VECTOR2D>& pointList : glyph )
1805 drawPoly( pointList );
1806 }
1807 else if( aGlyph.IsOutline() )
1808 {
1809 const KIFONT::OUTLINE_GLYPH& glyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
1810
1811 if( aNth == 0 )
1812 {
1813 cairo_close_path( m_currentContext );
1814 flushPath();
1815
1816 cairo_new_path( m_currentContext );
1817 SetIsFill( true );
1818 SetIsStroke( false );
1819 }
1820
1821 // eventually glyphs should not be drawn as polygons at all,
1822 // but as bitmaps with antialiasing, this is just a stopgap measure
1823 // of getting some form of outline font display
1824
1825 glyph.Triangulate(
1826 [&]( const VECTOR2D& aVertex1, const VECTOR2D& aVertex2, const VECTOR2D& aVertex3 )
1827 {
1828 syncLineWidth();
1829
1830 const VECTOR2D p0 = roundp( xform( aVertex1 ) );
1831 const VECTOR2D p1 = roundp( xform( aVertex2 ) );
1832 const VECTOR2D p2 = roundp( xform( aVertex3 ) );
1833
1834 cairo_move_to( m_currentContext, p0.x, p0.y );
1835 cairo_line_to( m_currentContext, p1.x, p1.y );
1836 cairo_line_to( m_currentContext, p2.x, p2.y );
1837 cairo_close_path( m_currentContext );
1838 cairo_set_fill_rule( m_currentContext, CAIRO_FILL_RULE_EVEN_ODD );
1839 flushPath();
1840 cairo_fill( m_currentContext );
1841 } );
1842
1843 if( aNth == aTotal - 1 )
1844 {
1845 /*
1846 cairo_close_path( currentContext );
1847 setSourceRgba( currentContext, fillColor );
1848 cairo_set_fill_rule( currentContext, CAIRO_FILL_RULE_EVEN_ODD );
1849 cairo_fill_preserve( currentContext );
1850 setSourceRgba( currentContext, strokeColor );
1851 cairo_stroke( currentContext );
1852 */
1853 flushPath();
1854 m_isElementAdded = true;
1855 }
1856 }
1857}
int color
Definition: DXF_plotter.cpp:57
Class that handles multitarget rendering (ie.
static double roundp(double x)
Definition: cairo_gal.cpp:184
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:51
wxSize GetSizePixels() const
Definition: bitmap_base.h:123
int GetPPI() const
Definition: bitmap_base.h:134
wxImage * GetImageData()
Definition: bitmap_base.h:69
static const wxCursor GetCursor(KICURSOR aCursorType)
Definition: cursors.cpp:394
double AsRadians() const
Definition: eda_angle.h:153
virtual bool IsStroke() const
Definition: glyph.h:52
virtual bool IsOutline() const
Definition: glyph.h:51
void Triangulate(std::function< void(const VECTOR2I &aPt1, const VECTOR2I &aPt2, const VECTOR2I &aPt3)> aCallback) const
Definition: glyph.cpp:121
void DrawGlyph(const KIFONT::GLYPH &aPolySet, int aNth, int aTotal) override
Draw a polygon representing a font glyph.
Definition: cairo_gal.cpp:1798
void blitCursor(wxMemoryDC &clientDC)
Blit cursor into the current screen.
Definition: cairo_gal.cpp:1184
void drawGridLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a grid line (usually a simplified line function).
Definition: cairo_gal.cpp:1056
cairo_surface_t * m_surface
Cairo surface.
Definition: cairo_gal.h:365
cairo_matrix_t m_cairoWorldScreenMatrix
Cairo world to screen transform matrix.
Definition: cairo_gal.h:360
void Flush() override
Force all remaining objects to be drawn.
Definition: cairo_gal.cpp:578
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: cairo_gal.cpp:93
void Restore() override
Restore the context.
Definition: cairo_gal.cpp:776
unsigned int m_groupCounter
Counter used for generating group keys.
Definition: cairo_gal.h:354
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
Definition: cairo_gal.cpp:718
void Save() override
Save the context.
Definition: cairo_gal.cpp:758
void ClearCache() override
Delete all data created during caching of graphic items.
Definition: cairo_gal.cpp:956
bool m_isElementAdded
Was an graphic element added ?
Definition: cairo_gal.h:352
const double xform(double x)
Definition: cairo_gal.cpp:176
void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
Definition: cairo_gal.cpp:938
std::deque< GROUP_ELEMENT > GROUP
A graphic group type definition.
Definition: cairo_gal.h:348
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: cairo_gal.cpp:199
void ClearScreen() override
Clear the screen.
Definition: cairo_gal.cpp:584
void storePath()
Store the actual path.
Definition: cairo_gal.cpp:1135
void DrawGroup(int aGroupNumber) override
Draw the stored group.
Definition: cairo_gal.cpp:821
void drawAxes(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Definition: cairo_gal.cpp:1038
std::vector< cairo_surface_t * > m_imageSurfaces
List of surfaces that were created by painting images, to be cleaned up later.
Definition: cairo_gal.h:368
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle) override
Draw an arc.
Definition: cairo_gal.cpp:340
void SetFillColor(const COLOR4D &aColor) override
Set the fill color.
Definition: cairo_gal.cpp:640
GROUP * m_currentGroup
Currently used group.
Definition: cairo_gal.h:355
void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
Definition: cairo_gal.cpp:995
const VECTOR2D roundp(const VECTOR2D &v)
Definition: cairo_gal.cpp:190
bool m_isGrouping
Is grouping enabled ?
Definition: cairo_gal.h:351
static constexpr cairo_format_t GAL_FORMAT
Format used to store pixels.
Definition: cairo_gal.h:372
void SetNegativeDrawMode(bool aSetting) override
Set negative draw mode in the renderer.
Definition: cairo_gal.cpp:963
void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Change the depth (Z-axis position) of the group.
Definition: cairo_gal.cpp:931
int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:798
void SetLayerDepth(double aLayerDepth) override
Set the depth of the layer (position on the z-axis)
Definition: cairo_gal.cpp:677
unsigned int getNewGroupNumber()
Return a valid key that can be used as a new group number.
Definition: cairo_gal.cpp:1302
cairo_matrix_t m_currentXform
Definition: cairo_gal.h:361
void SetIsStroke(bool aIsStrokeEnabled) override
Enable/disable stroked outlines.
Definition: cairo_gal.cpp:607
void EndGroup() override
End the group.
Definition: cairo_gal.cpp:814
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:1201
void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
Definition: cairo_gal.cpp:684
void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
Definition: cairo_gal.cpp:445
void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Change the color used to draw the group.
Definition: cairo_gal.cpp:914
std::map< int, GROUP > m_groups
List of graphic groups.
Definition: cairo_gal.h:353
void SetStrokeColor(const COLOR4D &aColor) override
Set the stroke color.
Definition: cairo_gal.cpp:622
cairo_t * m_context
Cairo image.
Definition: cairo_gal.h:364
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition: cairo_gal.cpp:99
void updateWorldScreenMatrix()
Definition: cairo_gal.cpp:106
void EnableDepthTest(bool aEnabled=false) override
Definition: cairo_gal.cpp:1001
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:480
void Scale(const VECTOR2D &aScale) override
Scale the context.
Definition: cairo_gal.cpp:738
std::vector< cairo_matrix_t > m_xformStack
Definition: cairo_gal.h:370
void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
Definition: cairo_gal.h:114
void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle, double aWidth, double aMaxError) override
Draw an arc segment.
Definition: cairo_gal.cpp:379
cairo_matrix_t m_currentWorld2Screen
Definition: cairo_gal.h:362
void drawGridCross(const VECTOR2D &aPoint)
Definition: cairo_gal.cpp:1070
void SetLineWidth(float aLineWidth) override
Set the line width.
Definition: cairo_gal.cpp:658
void arc_angles_xform_and_normalize(double &aStartAngle, double &aEndAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix for the start angle a...
Definition: cairo_gal.cpp:142
void syncLineWidth(bool aForceWidth=false, double aWidth=0.0)
Definition: cairo_gal.cpp:213
cairo_t * m_currentContext
Currently used Cairo context for drawing.
Definition: cairo_gal.h:363
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: cairo_gal.cpp:324
const double angle_xform(const double aAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix.
Definition: cairo_gal.cpp:128
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: cairo_gal.cpp:256
void drawGridPoint(const VECTOR2D &aPoint, double aWidth, double aHeight)
Definition: cairo_gal.cpp:1091
void SetIsFill(bool aIsFillEnabled) override
Enable/disable fill.
Definition: cairo_gal.cpp:592
void DrawBitmap(const BITMAP_BASE &aBitmap, double alphaBlend=1.0) override
Draw a bitmap image.
Definition: cairo_gal.cpp:502
@ CMD_SET_STROKE
Enable/disable stroking.
Definition: cairo_gal.h:321
@ CMD_SAVE
Save the transformation matrix.
Definition: cairo_gal.h:331
@ CMD_SCALE
Scale the context.
Definition: cairo_gal.h:330
@ CMD_SET_LINE_WIDTH
Set the line width.
Definition: cairo_gal.h:324
@ CMD_SET_FILL
Enable/disable filling.
Definition: cairo_gal.h:320
@ CMD_CALL_GROUP
Call a group.
Definition: cairo_gal.h:333
@ CMD_ROTATE
Rotate the context.
Definition: cairo_gal.h:328
@ CMD_STROKE_PATH
Set the stroke path.
Definition: cairo_gal.h:325
@ CMD_TRANSLATE
Translate the context.
Definition: cairo_gal.h:329
@ CMD_SET_FILLCOLOR
Set the fill color.
Definition: cairo_gal.h:322
@ CMD_FILL_PATH
Set the fill path.
Definition: cairo_gal.h:326
@ CMD_RESTORE
Restore the transformation matrix.
Definition: cairo_gal.h:332
@ CMD_SET_STROKECOLOR
Set the stroke color.
Definition: cairo_gal.h:323
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:572
void Rotate(double aAngle) override
Rotate the context.
Definition: cairo_gal.cpp:699
void DrawSegmentChain(const std::vector< VECTOR2D > &aPointList, double aWidth) override
Draw a chain of rounded segments.
Definition: cairo_gal.cpp:237
double m_lineWidthInPixels
Definition: cairo_gal.h:357
void DrawGrid() override
Definition: cairo_gal.cpp:1674
void EndGroup() override
End the group.
Definition: cairo_gal.cpp:1476
void deinitSurface()
Destroy Cairo surfaces when are not needed anymore.
Definition: cairo_gal.cpp:1556
void PostPaint(wxPaintEvent &aEvent)
Post an event to m_paint_listener.
Definition: cairo_gal.cpp:1433
bool Show(bool aShow) override
Show/hide the GAL canvas.
Definition: cairo_gal.cpp:1458
void SetTarget(RENDER_TARGET aTarget) override
Set the target for rendering.
Definition: cairo_gal.cpp:1483
wxCursor m_currentwxCursor
wxCursor showing the current native cursor
Definition: cairo_gal.h:518
void skipMouseEvent(wxMouseEvent &aEvent)
Mouse event handler, forwards the event to the child.
Definition: cairo_gal.cpp:1621
unsigned int m_overlayBuffer
Handle to the overlay buffer.
Definition: cairo_gal.h:499
unsigned int m_bufferSize
Size of buffers cairoOutput, bitmapBuffers.
Definition: cairo_gal.h:509
CAIRO_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("CairoCanvas"))
Definition: cairo_gal.cpp:1314
void initSurface()
Prepare Cairo surfaces for drawing.
Definition: cairo_gal.cpp:1535
int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:1469
bool SetNativeCursorStyle(KICURSOR aCursor) override
Set the cursor in the native panel.
Definition: cairo_gal.cpp:1653
unsigned char * m_wxOutput
wxImage compatible buffer
Definition: cairo_gal.h:510
bool m_isInitialized
Are Cairo image & surface ready to use.
Definition: cairo_gal.h:516
bool m_validCompositor
Compositor initialization flag.
Definition: cairo_gal.h:503
unsigned int m_mainBuffer
Handle to the main buffer.
Definition: cairo_gal.h:498
int m_stride
Stride value for Cairo.
Definition: cairo_gal.h:514
void onSetNativeCursor(wxSetCursorEvent &aEvent)
Give the correct cursor image when the native widget asks for it.
Definition: cairo_gal.cpp:1668
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition: cairo_gal.cpp:1395
void allocateBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1570
void onPaint(wxPaintEvent &aEvent)
Paint event handler.
Definition: cairo_gal.cpp:1615
void StartNegativesLayer() override
Begins rendering in a new layer that will be copied to the main layer in EndNegativesLayer().
Definition: cairo_gal.cpp:982
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:1441
RENDER_TARGET GetTarget() const override
Get the currently used target for rendering.
Definition: cairo_gal.cpp:1507
void setCompositor()
Prepare the compositor.
Definition: cairo_gal.cpp:1599
unsigned char * m_bitmapBuffer
Storage of the Cairo image.
Definition: cairo_gal.h:513
wxEvtHandler * m_mouseListener
Mouse listener.
Definition: cairo_gal.h:507
void ClearTarget(RENDER_TARGET aTarget) override
Clear the target for rendering.
Definition: cairo_gal.cpp:1513
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Handle updating display options.
Definition: cairo_gal.cpp:1629
~CAIRO_GAL()
Return true if the GAL canvas is visible on the screen.
Definition: cairo_gal.cpp:1375
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: cairo_gal.cpp:1381
wxEvtHandler * m_paintListener
Paint listener.
Definition: cairo_gal.h:508
unsigned int m_tempBuffer
Handle to the temp buffer.
Definition: cairo_gal.h:500
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: cairo_gal.h:502
wxWindow * m_parentWindow
Parent window.
Definition: cairo_gal.h:506
void EndDiffLayer() override
Ends rendering of a differential layer.
Definition: cairo_gal.cpp:976
std::shared_ptr< CAIRO_COMPOSITOR > m_compositor
Object for layers compositing.
Definition: cairo_gal.h:497
void deleteBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1589
void EndNegativesLayer() override
Ends rendering of a negatives layer and draws it to the main layer.
Definition: cairo_gal.cpp:989
void StartDiffLayer() override
Begins rendering of a differential layer.
Definition: cairo_gal.cpp:969
unsigned int m_savedBuffer
Handle to buffer to restore after rendering to temp buffer.
Definition: cairo_gal.h:501
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:384
double g
Green component.
Definition: color4d.h:385
double a
Alpha component.
Definition: color4d.h:387
static const COLOR4D BLACK
Definition: color4d.h:394
double b
Blue component.
Definition: color4d.h:386
CAIRO_ANTIALIASING_MODE cairo_antialiasing_mode
Abstract interface for drawing on a 2D-surface.
void SetGridColor(const COLOR4D &aGridColor)
Set the grid color.
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
bool IsCursorEnabled() const
Return information about cursor visibility.
MATRIX3x3D m_worldScreenMatrix
World transformation.
MATRIX3x3D m_screenWorldMatrix
Screen transformation.
bool m_axesEnabled
Should the axes be drawn.
float m_gridLineWidth
Line width of the grid.
VECTOR2I m_screenSize
Screen size in screen coordinates.
VECTOR2D m_depthRange
Range of the depth.
VECTOR2D ToScreen(const VECTOR2D &aPoint) const
Compute the point position in screen coordinates from given world coordinates.
GRID_STYLE m_gridStyle
Grid display style.
COLOR4D m_axesColor
Color of the axes.
float m_lineWidth
The line width.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
VECTOR2D m_gridSize
The grid size.
COLOR4D getCursorColor() const
Get the actual cursor color to draw.
COLOR4D m_fillColor
The fill color.
double m_worldUnitLength
The unit length of the world coordinates [inch].
virtual bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions)
Handle updating display options.
void SetAxesColor(const COLOR4D &aAxesColor)
Set the axes color.
VECTOR2D m_cursorPosition
Current cursor position (world coordinates)
int m_gridTick
Every tick line gets the double width.
double m_worldScale
The scale factor world->screen.
virtual bool SetNativeCursorStyle(KICURSOR aCursor)
Set the cursor in the native panel.
VECTOR2D m_gridOrigin
The grid origin.
KICURSOR m_currentNativeCursor
Current cursor.
bool m_fullscreenCursor
Shape of the cursor (fullscreen or small cross)
bool m_isFillEnabled
Is filling of graphic objects enabled ?
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
COLOR4D m_gridColor
Color of the grid.
COLOR4D m_strokeColor
The color of the outlines.
double computeMinGridSpacing() const
compute minimum grid spacing from the grid settings
bool m_isStrokeEnabled
Are the outlines stroked ?
GAL_DISPLAY_OPTIONS & m_options
bool m_gridVisibility
Should the grid be shown.
virtual void SetTarget(RENDER_TARGET aTarget)
Set the target for rendering.
T m_data[3][3]
Definition: matrix3x3.h:64
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
@ BLUE
Definition: color4d.h:56
KICURSOR
Definition: cursors.h:34
#define SWAP(varA, condition, varB)
Swap the variables if a condition is met.
Definition: definitions.h:31
@ RADIANS_T
Definition: eda_angle.h:32
E_SERIE r
Definition: eserie.cpp:41
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:266
@ SMALL_CROSS
Use small cross instead of dots for the grid.
@ DOTS
Use dots for the grid.
@ LINES
Use lines for the grid.
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:47
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition: definitions.h:49
@ TARGET_TEMP
Temporary target for drawing in separate layer.
Definition: definitions.h:51
@ TARGET_CACHED
Main rendering target (cached)
Definition: definitions.h:48
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
const int scale
Type definition for an graphics group element.
Definition: cairo_gal.h:338
GRAPHICS_COMMAND m_Command
Command to execute.
Definition: cairo_gal.h:339
cairo_path_t * m_CairoPath
Pointer to a Cairo path.
Definition: cairo_gal.h:345
double DblArg[MAX_CAIRO_ARGUMENTS]
Arguments for Cairo commands.
Definition: cairo_gal.h:341
union KIGFX::CAIRO_GAL_BASE::GROUP_ELEMENT::@27 m_Argument
bool BoolArg
A bool argument.
Definition: cairo_gal.h:342
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
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:85
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618