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