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