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-2024 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/rawbmp.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
96{
98}
99
100
102{
103 // Force remaining objects to be drawn
104 Flush();
105}
106
107
109{
111}
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
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::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
327{
329
330 VECTOR2D c = roundp( xform( aCenterPoint ) );
331 double r = ::roundp( xform( aRadius ) );
332
333 cairo_set_line_width( m_currentContext, std::min( 2.0 * r, m_lineWidthInPixels ) );
334 cairo_new_sub_path( m_currentContext );
335 cairo_arc( m_currentContext, c.x, c.y, r, 0.0, 2 * M_PI );
336 cairo_close_path( m_currentContext );
337 flushPath();
338 m_isElementAdded = true;
339}
340
341
342void CAIRO_GAL_BASE::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
343 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle )
344{
346
347 double startAngle = aStartAngle.AsRadians();
348 double endAngle = startAngle + aAngle.AsRadians();
349
350 // calculate start and end arc angles according to the rotation transform matrix
351 // and normalize:
352 arc_angles_xform_and_normalize( startAngle, endAngle );
353
354 double r = xform( aRadius );
355
356 // Adjust center and radius slightly to better match the rounding of endpoints.
357 VECTOR2D mid = roundp( xform( aCenterPoint ) );
358
359 VECTOR2D startPointS = VECTOR2D( r, 0.0 );
360 VECTOR2D endPointS = VECTOR2D( r, 0.0 );
361 RotatePoint( startPointS, -EDA_ANGLE( startAngle, RADIANS_T ) );
362 RotatePoint( endPointS, -EDA_ANGLE( endAngle, RADIANS_T ) );
363
364 VECTOR2D refStart = roundp( xform( aCenterPoint ) + startPointS );
365 VECTOR2D refEnd = roundp( xform( aCenterPoint ) + endPointS );
366
367 r = ( ( refStart - mid ).EuclideanNorm() + ( refEnd - mid ).EuclideanNorm() ) / 2.0;
368
369 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
370 cairo_new_sub_path( m_currentContext );
371
372 if( m_isFillEnabled )
373 cairo_move_to( m_currentContext, mid.x, mid.y );
374
375 cairo_arc( m_currentContext, mid.x, mid.y, r, startAngle, endAngle );
376
377 if( m_isFillEnabled )
378 cairo_close_path( m_currentContext );
379
380 flushPath();
381
382 m_isElementAdded = true;
383}
384
385
386void CAIRO_GAL_BASE::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
387 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle,
388 double aWidth, double aMaxError )
389{
390 // Note: aMaxError is not used because Cairo can draw true arcs
391 if( m_isFillEnabled )
392 {
393 m_lineWidth = aWidth;
394 m_isStrokeEnabled = true;
395 m_isFillEnabled = false;
396 DrawArc( aCenterPoint, aRadius, aStartAngle, aAngle );
397 m_isFillEnabled = true;
398 m_isStrokeEnabled = false;
399 return;
400 }
401
403
404 // calculate start and end arc angles according to the rotation transform matrix
405 // and normalize:
406 double startAngleS = aStartAngle.AsRadians();
407 double endAngleS = startAngleS + aAngle.AsRadians();
408 arc_angles_xform_and_normalize( startAngleS, endAngleS );
409
410 double r = xform( aRadius );
411
412 VECTOR2D mid = xform( aCenterPoint );
413 double width = xform( aWidth / 2.0 );
414 VECTOR2D startPointS = VECTOR2D( r, 0.0 );
415 VECTOR2D endPointS = VECTOR2D( r, 0.0 );
416 RotatePoint( startPointS, -EDA_ANGLE( startAngleS, RADIANS_T ) );
417 RotatePoint( endPointS, -EDA_ANGLE( endAngleS, RADIANS_T ) );
418
419 cairo_save( m_currentContext );
420
423
424 cairo_translate( m_currentContext, mid.x, mid.y );
425
426 cairo_new_sub_path( m_currentContext );
427 cairo_arc( m_currentContext, 0, 0, r - width, startAngleS, endAngleS );
428
429 cairo_new_sub_path( m_currentContext );
430 cairo_arc( m_currentContext, 0, 0, r + width, startAngleS, endAngleS );
431
432 cairo_new_sub_path( m_currentContext );
433 cairo_arc_negative( m_currentContext, startPointS.x, startPointS.y, width, startAngleS,
434 startAngleS + M_PI );
435
436 cairo_new_sub_path( m_currentContext );
437 cairo_arc( m_currentContext, endPointS.x, endPointS.y, width, endAngleS, endAngleS + M_PI );
438
439 cairo_restore( m_currentContext );
440 flushPath();
441
442 m_isElementAdded = true;
443}
444
445
446void CAIRO_GAL_BASE::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
447{
448 // Calculate the diagonal points
450
451 const VECTOR2D p0 = roundp( xform( aStartPoint ) );
452 const VECTOR2D p1 = roundp( xform( VECTOR2D( aEndPoint.x, aStartPoint.y ) ) );
453 const VECTOR2D p2 = roundp( xform( aEndPoint ) );
454 const VECTOR2D p3 = roundp( xform( VECTOR2D( aStartPoint.x, aEndPoint.y ) ) );
455
456 // The path is composed from 4 segments
457 cairo_move_to( m_currentContext, p0.x, p0.y );
458 cairo_line_to( m_currentContext, p1.x, p1.y );
459 cairo_line_to( m_currentContext, p2.x, p2.y );
460 cairo_line_to( m_currentContext, p3.x, p3.y );
461 cairo_close_path( m_currentContext );
462 flushPath();
463
464 m_isElementAdded = true;
465}
466
467
468void CAIRO_GAL_BASE::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
469{
470 for( int i = 0; i < aPolySet.OutlineCount(); ++i )
471 drawPoly( aPolySet.COutline( i ) );
472}
473
474
476{
477 drawPoly( aPolygon );
478}
479
480
481void CAIRO_GAL_BASE::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
482 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
483 double aFilterValue )
484{
485 // Note: aFilterValue is not used because the cubic Bezier curve is
486 // supported by Cairo.
488
489 const VECTOR2D sp = roundp( xform( aStartPoint ) );
490 const VECTOR2D cpa = roundp( xform( aControlPointA ) );
491 const VECTOR2D cpb = roundp( xform( aControlPointB ) );
492 const VECTOR2D ep = roundp( xform( aEndPoint ) );
493
494 cairo_move_to( m_currentContext, sp.x, sp.y );
495 cairo_curve_to( m_currentContext, cpa.x, cpa.y, cpb.x, cpb.y, ep.x, ep.y );
496 cairo_line_to( m_currentContext, ep.x, ep.y );
497
498 flushPath();
499 m_isElementAdded = true;
500}
501
502
503void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
504{
505 cairo_save( m_currentContext );
506
507 alphaBlend = std::clamp( alphaBlend, 0.0, 1.0 );
508
509 // We have to calculate the pixel size in users units to draw the image.
510 // m_worldUnitLength is a factor used for converting IU to inches
511 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
512
513 // The position of the bitmap is the bitmap center.
514 // move the draw origin to the top left bitmap corner:
515 int w = aBitmap.GetSizePixels().x;
516 int h = aBitmap.GetSizePixels().y;
517
518 cairo_set_matrix( m_currentContext, &m_currentWorld2Screen );
519 cairo_scale( m_currentContext, scale, scale );
520 cairo_translate( m_currentContext, -w / 2.0, -h / 2.0 );
521
522 cairo_new_path( m_currentContext );
523 cairo_surface_t* image = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w, h );
524 cairo_surface_flush( image );
525
526 unsigned char* pix_buffer = cairo_image_surface_get_data( image );
527
528 // The pixel buffer of the initial bitmap:
529 const wxImage& bm_pix_buffer = *aBitmap.GetImageData();
530
531 uint32_t mask_color = ( bm_pix_buffer.GetMaskRed() << 16 )
532 + ( bm_pix_buffer.GetMaskGreen() << 8 ) + ( bm_pix_buffer.GetMaskBlue() );
533
534 // Copy the source bitmap to the cairo bitmap buffer.
535 // In cairo bitmap buffer, a ARGB32 bitmap is an ARGB pixel packed into a uint_32
536 // 24 low bits only are used for color, top 8 are transparency.
537 for( int row = 0; row < h; row++ )
538 {
539 for( int col = 0; col < w; col++ )
540 {
541 unsigned char r = bm_pix_buffer.GetRed( col, row );
542 unsigned char g = bm_pix_buffer.GetGreen( col, row );
543 unsigned char b = bm_pix_buffer.GetBlue( col, row );
544 unsigned char a = wxALPHA_OPAQUE;
545
546 if( bm_pix_buffer.HasAlpha() )
547 {
548 a = bm_pix_buffer.GetAlpha( col, row );
549
550 // ARGB32 format needs pre-multiplied alpha
551 r = uint32_t( r ) * a / 0xFF;
552 g = uint32_t( g ) * a / 0xFF;
553 b = uint32_t( b ) * a / 0xFF;
554 }
555 else if( bm_pix_buffer.HasMask() && (uint32_t)( r << 16 | g << 8 | b ) == mask_color )
556 {
557 a = wxALPHA_TRANSPARENT;
558 }
559
560 // Build the ARGB24 pixel:
561 uint32_t pixel = a << 24 | r << 16 | g << 8 | b;
562
563 // Write the pixel to the cairo image buffer:
564 uint32_t* pix_ptr = (uint32_t*) pix_buffer;
565 *pix_ptr = pixel;
566 pix_buffer += 4;
567 }
568 }
569
570 cairo_surface_mark_dirty( image );
571 cairo_set_source_surface( m_currentContext, image, 0, 0 );
572 cairo_paint_with_alpha( m_currentContext, alphaBlend );
573
574 // store the image handle so it can be destroyed later
575 m_imageSurfaces.push_back( image );
576
577 m_isElementAdded = true;
578
579 cairo_restore( m_currentContext );
580}
581
582
583void CAIRO_GAL_BASE::ResizeScreen( int aWidth, int aHeight )
584{
585 m_screenSize = VECTOR2I( aWidth, aHeight );
586
588 std::ceil( m_screenSize.y * getScalingFactor() ) );
589}
590
591
593{
594 storePath();
595}
596
597
599{
600 cairo_set_source_rgb( m_context, m_clearColor.r, m_clearColor.g, m_clearColor.b );
601 cairo_rectangle( m_context, 0.0, 0.0, m_bitmapSize.x, m_bitmapSize.y );
602 cairo_fill( m_context );
603}
604
605
606void CAIRO_GAL_BASE::SetIsFill( bool aIsFillEnabled )
607{
608 storePath();
609 m_isFillEnabled = aIsFillEnabled;
610
611 if( m_isGrouping )
612 {
613 GROUP_ELEMENT groupElement;
614 groupElement.m_Command = CMD_SET_FILL;
615 groupElement.m_Argument.BoolArg = aIsFillEnabled;
616 m_currentGroup->push_back( groupElement );
617 }
618}
619
620
621void CAIRO_GAL_BASE::SetIsStroke( bool aIsStrokeEnabled )
622{
623 storePath();
624 m_isStrokeEnabled = aIsStrokeEnabled;
625
626 if( m_isGrouping )
627 {
628 GROUP_ELEMENT groupElement;
629 groupElement.m_Command = CMD_SET_STROKE;
630 groupElement.m_Argument.BoolArg = aIsStrokeEnabled;
631 m_currentGroup->push_back( groupElement );
632 }
633}
634
635
637{
638 storePath();
639 m_strokeColor = aColor;
640
641 if( m_isGrouping )
642 {
643 GROUP_ELEMENT groupElement;
644 groupElement.m_Command = CMD_SET_STROKECOLOR;
645 groupElement.m_Argument.DblArg[0] = m_strokeColor.r;
646 groupElement.m_Argument.DblArg[1] = m_strokeColor.g;
647 groupElement.m_Argument.DblArg[2] = m_strokeColor.b;
648 groupElement.m_Argument.DblArg[3] = m_strokeColor.a;
649 m_currentGroup->push_back( groupElement );
650 }
651}
652
653
655{
656 storePath();
657 m_fillColor = aColor;
658
659 if( m_isGrouping )
660 {
661 GROUP_ELEMENT groupElement;
662 groupElement.m_Command = CMD_SET_FILLCOLOR;
663 groupElement.m_Argument.DblArg[0] = m_fillColor.r;
664 groupElement.m_Argument.DblArg[1] = m_fillColor.g;
665 groupElement.m_Argument.DblArg[2] = m_fillColor.b;
666 groupElement.m_Argument.DblArg[3] = m_fillColor.a;
667 m_currentGroup->push_back( groupElement );
668 }
669}
670
671
672void CAIRO_GAL_BASE::SetLineWidth( float aLineWidth )
673{
674 storePath();
675 GAL::SetLineWidth( aLineWidth );
676
677 if( m_isGrouping )
678 {
679 GROUP_ELEMENT groupElement;
680 groupElement.m_Command = CMD_SET_LINE_WIDTH;
681 groupElement.m_Argument.DblArg[0] = aLineWidth;
682 m_currentGroup->push_back( groupElement );
683 }
684 else
685 {
686 m_lineWidth = aLineWidth;
687 }
688}
689
690
691void CAIRO_GAL_BASE::SetLayerDepth( double aLayerDepth )
692{
693 super::SetLayerDepth( aLayerDepth );
694 storePath();
695}
696
697
698void CAIRO_GAL_BASE::Transform( const MATRIX3x3D& aTransformation )
699{
700 cairo_matrix_t cairoTransformation, newXform;
701
702 cairo_matrix_init( &cairoTransformation, aTransformation.m_data[0][0],
703 aTransformation.m_data[1][0], aTransformation.m_data[0][1],
704 aTransformation.m_data[1][1], aTransformation.m_data[0][2],
705 aTransformation.m_data[1][2] );
706
707 cairo_matrix_multiply( &newXform, &m_currentXform, &cairoTransformation );
708 m_currentXform = newXform;
710}
711
712
713void CAIRO_GAL_BASE::Rotate( double aAngle )
714{
715 storePath();
716
717 if( m_isGrouping )
718 {
719 GROUP_ELEMENT groupElement;
720 groupElement.m_Command = CMD_ROTATE;
721 groupElement.m_Argument.DblArg[0] = aAngle;
722 m_currentGroup->push_back( groupElement );
723 }
724 else
725 {
726 cairo_matrix_rotate( &m_currentXform, aAngle );
728 }
729}
730
731
732void CAIRO_GAL_BASE::Translate( const VECTOR2D& aTranslation )
733{
734 storePath();
735
736 if( m_isGrouping )
737 {
738 GROUP_ELEMENT groupElement;
739 groupElement.m_Command = CMD_TRANSLATE;
740 groupElement.m_Argument.DblArg[0] = aTranslation.x;
741 groupElement.m_Argument.DblArg[1] = aTranslation.y;
742 m_currentGroup->push_back( groupElement );
743 }
744 else
745 {
746 cairo_matrix_translate( &m_currentXform, aTranslation.x, aTranslation.y );
748 }
749}
750
751
752void CAIRO_GAL_BASE::Scale( const VECTOR2D& aScale )
753{
754 storePath();
755
756 if( m_isGrouping )
757 {
758 GROUP_ELEMENT groupElement;
759 groupElement.m_Command = CMD_SCALE;
760 groupElement.m_Argument.DblArg[0] = aScale.x;
761 groupElement.m_Argument.DblArg[1] = aScale.y;
762 m_currentGroup->push_back( groupElement );
763 }
764 else
765 {
766 cairo_matrix_scale( &m_currentXform, aScale.x, aScale.y );
768 }
769}
770
771
773{
774 storePath();
775
776 if( m_isGrouping )
777 {
778 GROUP_ELEMENT groupElement;
779 groupElement.m_Command = CMD_SAVE;
780 m_currentGroup->push_back( groupElement );
781 }
782 else
783 {
784 m_xformStack.push_back( m_currentXform );
786 }
787}
788
789
791{
792 storePath();
793
794 if( m_isGrouping )
795 {
796 GROUP_ELEMENT groupElement;
797 groupElement.m_Command = CMD_RESTORE;
798 m_currentGroup->push_back( groupElement );
799 }
800 else
801 {
802 if( !m_xformStack.empty() )
803 {
805 m_xformStack.pop_back();
807 }
808 }
809}
810
811
813{
814 // If the grouping is started: the actual path is stored in the group, when
815 // a attribute was changed or when grouping stops with the end group method.
816 storePath();
817
818 GROUP group;
819 int groupNumber = getNewGroupNumber();
820 m_groups.insert( std::make_pair( groupNumber, group ) );
821 m_currentGroup = &m_groups[groupNumber];
822 m_isGrouping = true;
823
824 return groupNumber;
825}
826
827
829{
830 storePath();
831 m_isGrouping = false;
832}
833
834
835void CAIRO_GAL_BASE::DrawGroup( int aGroupNumber )
836{
837 // This method implements a small Virtual Machine - all stored commands
838 // are executed; nested calling is also possible
839
840 storePath();
841
842 for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
843 {
844 switch( it->m_Command )
845 {
846 case CMD_SET_FILL:
847 m_isFillEnabled = it->m_Argument.BoolArg;
848 break;
849
850 case CMD_SET_STROKE:
851 m_isStrokeEnabled = it->m_Argument.BoolArg;
852 break;
853
855 m_fillColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
856 it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
857 break;
858
860 m_strokeColor = COLOR4D( it->m_Argument.DblArg[0], it->m_Argument.DblArg[1],
861 it->m_Argument.DblArg[2], it->m_Argument.DblArg[3] );
862 break;
863
865 {
866 // Make lines appear at least 1 pixel wide, no matter of zoom
867 double x = 1.0, y = 1.0;
868 cairo_device_to_user_distance( m_currentContext, &x, &y );
869 double minWidth = std::min( fabs( x ), fabs( y ) );
870 cairo_set_line_width( m_currentContext,
871 std::max( it->m_Argument.DblArg[0], minWidth ) );
872 break;
873 }
874
875
876 case CMD_STROKE_PATH:
877 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
879 cairo_append_path( m_currentContext, it->m_CairoPath );
880 cairo_stroke( m_currentContext );
881 break;
882
883 case CMD_FILL_PATH:
884 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
886 cairo_append_path( m_currentContext, it->m_CairoPath );
887 cairo_fill( m_currentContext );
888 break;
889
890 /*
891 case CMD_TRANSFORM:
892 cairo_matrix_t matrix;
893 cairo_matrix_init( &matrix, it->argument.DblArg[0], it->argument.DblArg[1],
894 it->argument.DblArg[2], it->argument.DblArg[3],
895 it->argument.DblArg[4], it->argument.DblArg[5] );
896 cairo_transform( m_currentContext, &matrix );
897 break;
898 */
899
900 case CMD_ROTATE:
901 cairo_rotate( m_currentContext, it->m_Argument.DblArg[0] );
902 break;
903
904 case CMD_TRANSLATE:
905 cairo_translate( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
906 break;
907
908 case CMD_SCALE:
909 cairo_scale( m_currentContext, it->m_Argument.DblArg[0], it->m_Argument.DblArg[1] );
910 break;
911
912 case CMD_SAVE:
913 cairo_save( m_currentContext );
914 break;
915
916 case CMD_RESTORE:
917 cairo_restore( m_currentContext );
918 break;
919
920 case CMD_CALL_GROUP:
921 DrawGroup( it->m_Argument.IntArg );
922 break;
923 }
924 }
925}
926
927
928void CAIRO_GAL_BASE::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
929{
930 storePath();
931
932 for( auto it = m_groups[aGroupNumber].begin(); it != m_groups[aGroupNumber].end(); ++it )
933 {
934 if( it->m_Command == CMD_SET_FILLCOLOR || it->m_Command == CMD_SET_STROKECOLOR )
935 {
936 it->m_Argument.DblArg[0] = aNewColor.r;
937 it->m_Argument.DblArg[1] = aNewColor.g;
938 it->m_Argument.DblArg[2] = aNewColor.b;
939 it->m_Argument.DblArg[3] = aNewColor.a;
940 }
941 }
942}
943
944
945void CAIRO_GAL_BASE::ChangeGroupDepth( int aGroupNumber, int aDepth )
946{
947 // Cairo does not have any possibilities to change the depth coordinate of stored items,
948 // it depends only on the order of drawing
949}
950
951
952void CAIRO_GAL_BASE::DeleteGroup( int aGroupNumber )
953{
954 storePath();
955
956 // Delete the Cairo paths
957 std::deque<GROUP_ELEMENT>::iterator it, end;
958
959 for( it = m_groups[aGroupNumber].begin(), end = m_groups[aGroupNumber].end(); it != end; ++it )
960 {
961 if( it->m_Command == CMD_FILL_PATH || it->m_Command == CMD_STROKE_PATH )
962 cairo_path_destroy( it->m_CairoPath );
963 }
964
965 // Delete the group
966 m_groups.erase( aGroupNumber );
967}
968
969
971{
972 for( auto it = m_groups.begin(); it != m_groups.end(); )
973 DeleteGroup( ( it++ )->first );
974}
975
976
978{
979 cairo_set_operator( m_currentContext, aSetting ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_OVER );
980}
981
982
984{
987}
988
989
991{
992 m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_ADD );
993}
994
995
997{
1000}
1001
1002
1004{
1005 m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_OVER );
1006}
1007
1008
1009void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition )
1010{
1011 m_cursorPosition = aCursorPosition;
1012}
1013
1014
1016{
1017}
1018
1019
1021{
1022 for( _cairo_surface* imageSurface : m_imageSurfaces )
1023 cairo_surface_destroy( imageSurface );
1024
1025 m_imageSurfaces.clear();
1026
1027 // Compute the world <-> screen transformations
1029
1030 double sf = getScalingFactor();
1031
1032 cairo_matrix_init( &m_cairoWorldScreenMatrix,
1035 m_worldScreenMatrix.m_data[0][2] * sf, m_worldScreenMatrix.m_data[1][2] * sf );
1036
1037 // we work in screen-space coordinates and do the transforms outside.
1038 cairo_identity_matrix( m_context );
1039
1040 cairo_matrix_init_identity( &m_currentXform );
1041
1042 // Start drawing with a new path
1043 cairo_new_path( m_context );
1044 m_isElementAdded = true;
1045
1047
1048 m_lineWidth = 0;
1049}
1050
1051
1052void CAIRO_GAL_BASE::drawAxes( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1053{
1054 syncLineWidth();
1055
1056 VECTOR2D p0 = roundp( xform( aStartPoint ) );
1057 VECTOR2D p1 = roundp( xform( aEndPoint ) );
1058 VECTOR2D org = roundp( xform( VECTOR2D( 0.0, 0.0 ) ) ); // Axis origin = 0,0 coord
1059
1060 cairo_set_source_rgba( m_currentContext, m_axesColor.r, m_axesColor.g, m_axesColor.b,
1061 m_axesColor.a );
1062 cairo_move_to( m_currentContext, p0.x, org.y );
1063 cairo_line_to( m_currentContext, p1.x, org.y );
1064 cairo_move_to( m_currentContext, org.x, p0.y );
1065 cairo_line_to( m_currentContext, org.x, p1.y );
1066 cairo_stroke( m_currentContext );
1067}
1068
1069
1070void CAIRO_GAL_BASE::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1071{
1072 syncLineWidth();
1073 VECTOR2D p0 = roundp( xform( aStartPoint ) );
1074 VECTOR2D p1 = roundp( xform( aEndPoint ) );
1075
1076 cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1077 m_gridColor.a );
1078 cairo_move_to( m_currentContext, p0.x, p0.y );
1079 cairo_line_to( m_currentContext, p1.x, p1.y );
1080 cairo_stroke( m_currentContext );
1081}
1082
1083
1085{
1086 syncLineWidth();
1087 VECTOR2D offset( 0, 0 );
1088 double size = 2.0 * m_lineWidthInPixels + 0.5;
1089
1090 VECTOR2D p0 = roundp( xform( aPoint ) ) - VECTOR2D( size, 0 ) + offset;
1091 VECTOR2D p1 = roundp( xform( aPoint ) ) + VECTOR2D( size, 0 ) + offset;
1092 VECTOR2D p2 = roundp( xform( aPoint ) ) - VECTOR2D( 0, size ) + offset;
1093 VECTOR2D p3 = roundp( xform( aPoint ) ) + VECTOR2D( 0, size ) + offset;
1094
1095 cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1096 m_gridColor.a );
1097 cairo_move_to( m_currentContext, p0.x, p0.y );
1098 cairo_line_to( m_currentContext, p1.x, p1.y );
1099 cairo_move_to( m_currentContext, p2.x, p2.y );
1100 cairo_line_to( m_currentContext, p3.x, p3.y );
1101 cairo_stroke( m_currentContext );
1102}
1103
1104
1105void CAIRO_GAL_BASE::drawGridPoint( const VECTOR2D& aPoint, double aWidth, double aHeight )
1106{
1107 VECTOR2D p = roundp( xform( aPoint ) );
1108
1109 double sw = std::max( 1.0, aWidth );
1110 double sh = std::max( 1.0, aHeight );
1111
1112 cairo_set_source_rgba( m_currentContext, m_gridColor.r, m_gridColor.g, m_gridColor.b,
1113 m_gridColor.a );
1114 cairo_rectangle( m_currentContext, p.x - std::floor( sw / 2 ) - 0.5,
1115 p.y - std::floor( sh / 2 ) - 0.5, sw, sh );
1116
1117 cairo_fill( m_currentContext );
1118}
1119
1120
1122{
1123 if( m_isFillEnabled )
1124 {
1125 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g, m_fillColor.b,
1126 m_fillColor.a );
1127
1128 if( m_isStrokeEnabled )
1129 {
1130 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
1131 cairo_fill_preserve( m_currentContext );
1132 }
1133 else
1134 {
1135 cairo_fill( m_currentContext );
1136 }
1137 }
1138
1139 if( m_isStrokeEnabled )
1140 {
1141 cairo_set_line_width( m_currentContext, m_lineWidthInPixels );
1142 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g, m_strokeColor.b,
1143 m_strokeColor.a );
1144 cairo_stroke( m_currentContext );
1145 }
1146}
1147
1148
1150{
1151 if( m_isElementAdded )
1152 {
1153 m_isElementAdded = false;
1154
1155 if( !m_isGrouping )
1156 {
1157 if( m_isFillEnabled )
1158 {
1159 cairo_set_source_rgba( m_currentContext, m_fillColor.r, m_fillColor.g,
1161 cairo_fill_preserve( m_currentContext );
1162 }
1163
1164 if( m_isStrokeEnabled )
1165 {
1166 cairo_set_source_rgba( m_currentContext, m_strokeColor.r, m_strokeColor.g,
1168 cairo_stroke_preserve( m_currentContext );
1169 }
1170 }
1171 else
1172 {
1173 // Copy the actual path, append it to the global path list
1174 // then check, if the path needs to be stroked/filled and
1175 // add this command to the group list;
1176 if( m_isStrokeEnabled )
1177 {
1178 GROUP_ELEMENT groupElement;
1179 groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1180 groupElement.m_Command = CMD_STROKE_PATH;
1181 m_currentGroup->push_back( groupElement );
1182 }
1183
1184 if( m_isFillEnabled )
1185 {
1186 GROUP_ELEMENT groupElement;
1187 groupElement.m_CairoPath = cairo_copy_path( m_currentContext );
1188 groupElement.m_Command = CMD_FILL_PATH;
1189 m_currentGroup->push_back( groupElement );
1190 }
1191 }
1192
1193 cairo_new_path( m_currentContext );
1194 }
1195}
1196
1197
1198void CAIRO_GAL_BASE::blitCursor( wxMemoryDC& clientDC )
1199{
1200 if( !IsCursorEnabled() )
1201 return;
1202
1204 const COLOR4D cColor = getCursorColor();
1205 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
1206
1207 wxColour color( cColor.r * cColor.a * 255, cColor.g * cColor.a * 255, cColor.b * cColor.a * 255,
1208 255 );
1209 clientDC.SetPen( wxPen( color ) );
1210 clientDC.DrawLine( p.x - cursorSize / 2, p.y, p.x + cursorSize / 2, p.y );
1211 clientDC.DrawLine( p.x, p.y - cursorSize / 2, p.x, p.y + cursorSize / 2 );
1212}
1213
1214
1215void CAIRO_GAL_BASE::drawPoly( const std::deque<VECTOR2D>& aPointList )
1216{
1217 wxCHECK( aPointList.size() > 1, /* void */ );
1218
1219 // Iterate over the point list and draw the segments
1220 std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
1221
1222 syncLineWidth();
1223
1224 const VECTOR2D p = roundp( xform( it->x, it->y ) );
1225
1226 cairo_move_to( m_currentContext, p.x, p.y );
1227
1228 for( ++it; it != aPointList.end(); ++it )
1229 {
1230 const VECTOR2D p2 = roundp( xform( it->x, it->y ) );
1231
1232 cairo_line_to( m_currentContext, p2.x, p2.y );
1233 }
1234
1235 flushPath();
1236 m_isElementAdded = true;
1237}
1238
1239
1240void CAIRO_GAL_BASE::drawPoly( const std::vector<VECTOR2D>& aPointList )
1241{
1242 wxCHECK( aPointList.size() > 1, /* void */ );
1243
1244 // Iterate over the point list and draw the segments
1245 std::vector<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 VECTOR2D aPointList[], int aListSize )
1266{
1267 wxCHECK( aListSize > 1, /* void */ );
1268
1269 // Iterate over the point list and draw the segments
1270 const VECTOR2D* ptr = aPointList;
1271
1272 syncLineWidth();
1273
1274 const VECTOR2D p = roundp( xform( ptr->x, ptr->y ) );
1275 cairo_move_to( m_currentContext, p.x, p.y );
1276
1277 for( int i = 1; i < aListSize; ++i )
1278 {
1279 ++ptr;
1280 const VECTOR2D p2 = roundp( xform( ptr->x, ptr->y ) );
1281 cairo_line_to( m_currentContext, p2.x, p2.y );
1282 }
1283
1284 flushPath();
1285 m_isElementAdded = true;
1286}
1287
1288
1290{
1291 wxCHECK( aLineChain.PointCount() > 1, /* void */ );
1292
1293 syncLineWidth();
1294
1295 auto numPoints = aLineChain.PointCount();
1296
1297 if( aLineChain.IsClosed() )
1298 numPoints += 1;
1299
1300 const VECTOR2I start = aLineChain.CPoint( 0 );
1301 const VECTOR2D p = roundp( xform( start.x, start.y ) );
1302 cairo_move_to( m_currentContext, p.x, p.y );
1303
1304 for( int i = 1; i < numPoints; ++i )
1305 {
1306 const VECTOR2I& pw = aLineChain.CPoint( i );
1307 const VECTOR2D ps = roundp( xform( pw.x, pw.y ) );
1308 cairo_line_to( m_currentContext, ps.x, ps.y );
1309 }
1310
1311 flushPath();
1312 m_isElementAdded = true;
1313}
1314
1315
1317{
1318 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
1319 wxT( "There are no free slots to store a group" ) );
1320
1321 while( m_groups.find( m_groupCounter ) != m_groups.end() )
1323
1324 return m_groupCounter++;
1325}
1326
1327
1328CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
1329 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
1330 const wxString& aName ) :
1331 CAIRO_GAL_BASE( aDisplayOptions ),
1332 wxWindow( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxEXPAND, aName )
1333{
1334 // Initialise compositing state
1335 m_mainBuffer = 0;
1336 m_overlayBuffer = 0;
1337 m_tempBuffer = 0;
1338 m_savedBuffer = 0;
1339 m_validCompositor = false;
1342
1343 SetBackgroundStyle( wxBG_STYLE_PAINT );
1344
1345 m_bitmapBuffer = nullptr;
1346 m_wxBitmap = nullptr;
1347
1348 m_parentWindow = aParent;
1349 m_mouseListener = aMouseListener;
1350 m_paintListener = aPaintListener;
1351
1352 // Connect the native cursor handler
1353 Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( CAIRO_GAL::onSetNativeCursor ), nullptr,
1354 this );
1355
1356 // Connecting the event handlers
1357 Connect( wxEVT_PAINT, wxPaintEventHandler( CAIRO_GAL::onPaint ) );
1358
1359 // Mouse events are skipped to the parent
1360 Connect( wxEVT_MOTION, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1361 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1362 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1363 Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1364 Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1365 Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1366 Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1367 Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1368 Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1369 Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1370 Connect( wxEVT_AUX1_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1371 Connect( wxEVT_AUX1_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1372 Connect( wxEVT_AUX1_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1373 Connect( wxEVT_AUX2_DOWN, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1374 Connect( wxEVT_AUX2_UP, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1375 Connect( wxEVT_AUX2_DCLICK, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1376 Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1377#if defined _WIN32 || defined _WIN64
1378 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( CAIRO_GAL::skipMouseEvent ) );
1379#endif
1380
1381 Bind( wxEVT_GESTURE_ZOOM, &CAIRO_GAL::skipGestureEvent, this );
1382 Bind( wxEVT_GESTURE_PAN, &CAIRO_GAL::skipGestureEvent, this );
1383
1384 SetSize( aParent->GetClientSize() );
1385
1386 m_isInitialized = false;
1387}
1388
1389
1391{
1392 deleteBitmaps();
1393}
1394
1395
1397{
1398 initSurface();
1399
1401
1402 if( !m_validCompositor )
1403 setCompositor();
1404
1405 m_compositor->SetMainContext( m_context );
1406 m_compositor->SetBuffer( m_mainBuffer );
1407}
1408
1409
1411{
1413
1414 // Merge buffers on the screen
1415 m_compositor->DrawBuffer( m_mainBuffer );
1416 m_compositor->DrawBuffer( m_overlayBuffer );
1417
1418 // Now translate the raw context data from the format stored
1419 // by cairo into a format understood by wxImage.
1420 int height = m_bitmapSize.y;
1421 int stride = m_stride;
1422
1423 unsigned char* srcRow = m_bitmapBuffer;
1424
1425 wxNativePixelData dstData( *m_wxBitmap );
1426 wxNativePixelData::Iterator di( dstData );
1427
1428 for( int y = 0; y < height; y++ )
1429 {
1430 wxNativePixelData::Iterator rowStart = di;
1431
1432 for( int x = 0; x < stride; x += 4, ++di )
1433 {
1434 const unsigned char* src = srcRow + x;
1435
1436#if defined( __BYTE_ORDER__ ) && ( __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ )
1437 // XRGB
1438 di.Red() = src[1];
1439 di.Green() = src[2];
1440 di.Blue() = src[3];
1441#else
1442 // BGRX
1443 di.Red() = src[2];
1444 di.Green() = src[1];
1445 di.Blue() = src[0];
1446#endif
1447 }
1448
1449 srcRow += stride;
1450
1451 di = rowStart;
1452 di.OffsetY( dstData, 1 );
1453 }
1454
1455 deinitSurface();
1456
1457 Refresh(); // Trigger repaint
1458}
1459
1460
1461void CAIRO_GAL::PostPaint( wxPaintEvent& aEvent )
1462{
1463 // posts an event to m_paint_listener to ask for redraw the canvas.
1464 if( m_paintListener )
1465 wxPostEvent( m_paintListener, aEvent );
1466}
1467
1468
1469void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
1470{
1471 CAIRO_GAL_BASE::ResizeScreen( aWidth, aHeight );
1472
1473 // Recreate the bitmaps
1474 deleteBitmaps();
1476
1477 if( m_validCompositor )
1479
1480 m_validCompositor = false;
1481
1482 SetSize( wxSize( aWidth, aHeight ) );
1483}
1484
1485
1486bool CAIRO_GAL::Show( bool aShow )
1487{
1488 bool s = wxWindow::Show( aShow );
1489
1490 if( aShow )
1491 wxWindow::Raise();
1492
1493 return s;
1494}
1495
1496
1498{
1499 initSurface();
1501}
1502
1503
1505{
1507 deinitSurface();
1508}
1509
1510
1512{
1513 // If the compositor is not set, that means that there is a recaching process going on
1514 // and we do not need the compositor now
1515 if( !m_validCompositor )
1516 return;
1517
1518 // Cairo grouping prevents display of overlapping items on the same layer in the lighter color
1519 if( m_isInitialized )
1520 storePath();
1521
1522 switch( aTarget )
1523 {
1524 default:
1525 case TARGET_CACHED:
1526 case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1527 case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1528 case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
1529 }
1530
1531 m_currentTarget = aTarget;
1532}
1533
1534
1536{
1537 return m_currentTarget;
1538}
1539
1540
1542{
1543 // Save the current state
1544 unsigned int currentBuffer = m_compositor->GetBuffer();
1545
1546 switch( aTarget )
1547 {
1548 // Cached and noncached items are rendered to the same buffer
1549 default:
1550 case TARGET_CACHED:
1551 case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
1552 case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
1553 case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
1554 }
1555
1556 m_compositor->ClearBuffer( COLOR4D::BLACK );
1557
1558 // Restore the previous state
1559 m_compositor->SetBuffer( currentBuffer );
1560}
1561
1562
1564{
1565 if( m_isInitialized )
1566 return;
1567
1568 m_surface = cairo_image_surface_create_for_data( m_bitmapBuffer, GAL_FORMAT, m_wxBufferWidth,
1570
1571 m_context = cairo_create( m_surface );
1572
1573#ifdef DEBUG
1574 cairo_status_t status = cairo_status( m_context );
1575 wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, wxT( "Cairo context creation error" ) );
1576#endif /* DEBUG */
1577
1579
1580 m_isInitialized = true;
1581}
1582
1583
1585{
1586 if( !m_isInitialized )
1587 return;
1588
1589 cairo_destroy( m_context );
1590 m_context = nullptr;
1591 cairo_surface_destroy( m_surface );
1592 m_surface = nullptr;
1593
1594 m_isInitialized = false;
1595}
1596
1597
1599{
1601
1602 // Create buffer, use the system independent Cairo context backend
1603 m_stride = cairo_format_stride_for_width( GAL_FORMAT, m_wxBufferWidth );
1605
1606 wxASSERT( m_bitmapBuffer == nullptr );
1607 m_bitmapBuffer = new unsigned char[m_bufferSize];
1608
1609 wxASSERT( m_wxBitmap == nullptr );
1610 m_wxBitmap = new wxBitmap( m_wxBufferWidth, m_bitmapSize.y, 24 );
1611 m_wxBitmap->SetScaleFactor( getScalingFactor() );
1612}
1613
1614
1616{
1617 delete[] m_bitmapBuffer;
1618 m_bitmapBuffer = nullptr;
1619
1620 delete m_wxBitmap;
1621 m_wxBitmap = nullptr;
1622}
1623
1624
1626{
1627 // Recreate the compositor with the new Cairo context
1630 m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
1631
1632 // Prepare buffers
1633 m_mainBuffer = m_compositor->CreateBuffer();
1634 m_overlayBuffer = m_compositor->CreateBuffer();
1635 m_tempBuffer = m_compositor->CreateBuffer();
1636
1637 m_validCompositor = true;
1638}
1639
1640
1641void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
1642{
1643 // We should have the rendered image in m_wxBitmap after EDA_DRAW_PANEL_GAL::onPaint
1644
1645 if( !m_wxBitmap )
1646 {
1647 wxLogDebug( "CAIRO_GAL::onPaint null output bitmap buffer" );
1648 return;
1649 }
1650
1651 {
1652 // Now it is the time to blit the mouse cursor
1653 wxMemoryDC mdc( *m_wxBitmap );
1654 blitCursor( mdc );
1655 }
1656
1657 wxPaintDC paintDC( this );
1658 paintDC.DrawBitmap( *m_wxBitmap, 0, 0 );
1659}
1660
1661
1662void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1663{
1664 // Post the mouse event to the event listener registered in constructor, if any
1665 if( m_mouseListener )
1666 wxPostEvent( m_mouseListener, aEvent );
1667}
1668
1669
1670void CAIRO_GAL::skipGestureEvent( wxGestureEvent& aEvent )
1671{
1672 // Post the gesture event to the event listener registered in constructor, if any
1673 if( m_mouseListener )
1674 wxPostEvent( m_mouseListener, aEvent );
1675}
1676
1677
1679{
1680 bool refresh = false;
1681
1682 if( m_validCompositor &&
1683 aOptions.cairo_antialiasing_mode != m_compositor->GetAntialiasingMode() )
1684 {
1685 m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
1686 m_validCompositor = false;
1687 deinitSurface();
1688
1689 refresh = true;
1690 }
1691
1692 if( super::updatedGalDisplayOptions( aOptions ) )
1693 {
1694 Refresh();
1695 refresh = true;
1696 }
1697
1698 return refresh;
1699}
1700
1701
1703{
1704 return GetContentScaleFactor();
1705};
1706
1707
1708bool CAIRO_GAL::SetNativeCursorStyle( KICURSOR aCursor, bool aHiDPI )
1709{
1710 // Store the current cursor type and get the wxCursor for it
1711 if( !GAL::SetNativeCursorStyle( aCursor, aHiDPI ) )
1712 return false;
1713
1714 if( aHiDPI )
1716 else
1718
1719 // Update the cursor in the wx control
1720 wxWindow::SetCursor( m_currentwxCursor );
1721
1722 return true;
1723}
1724
1725
1726void CAIRO_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
1727{
1728 aEvent.SetCursor( m_currentwxCursor );
1729}
1730
1731
1733{
1735
1736 // Draw the grid
1737 // For the drawing the start points, end points and increments have
1738 // to be calculated in world coordinates
1739 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1741
1742 // Compute the line marker or point radius of the grid
1743 // Note: generic grids can't handle sub-pixel lines without
1744 // either losing fine/course distinction or having some dots
1745 // fail to render
1746 float marker = std::fmax( 1.0f, m_gridLineWidth ) / m_worldScale;
1747 float doubleMarker = 2.0f * marker;
1748
1749 // Draw axes if desired
1750 if( m_axesEnabled )
1751 {
1752 SetLineWidth( marker );
1753 drawAxes( worldStartPoint, worldEndPoint );
1754 }
1755
1756 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1757 return;
1758
1759 VECTOR2D gridScreenSize( m_gridSize );
1760
1761 double gridThreshold = KiROUND( computeMinGridSpacing() / m_worldScale );
1762
1764 gridThreshold *= 2.0;
1765
1766 // If we cannot display the grid density, scale down by a tick size and
1767 // try again. Eventually, we get some representation of the grid
1768 while( std::min( gridScreenSize.x, gridScreenSize.y ) <= gridThreshold )
1769 {
1770 gridScreenSize = gridScreenSize * static_cast<double>( m_gridTick );
1771 }
1772
1773 // Compute grid starting and ending indexes to draw grid points on the
1774 // visible screen area
1775 // Note: later any point coordinate will be offsetted by m_gridOrigin
1776 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1777 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1778 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1779 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1780
1781 // Ensure start coordinate < end coordinate
1782 normalize( gridStartX, gridEndX );
1783 normalize( gridStartY, gridEndY );
1784
1785 // Ensure the grid fills the screen
1786 --gridStartX;
1787 ++gridEndX;
1788 --gridStartY;
1789 ++gridEndY;
1790
1791 // Draw the grid behind all other layers
1792 SetLayerDepth( m_depthRange.y * 0.75 );
1793
1795 {
1796 // Now draw the grid, every coarse grid line gets the double width
1797
1798 // Vertical lines
1799 for( int j = gridStartY; j <= gridEndY; j++ )
1800 {
1801 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1802
1803 if( m_axesEnabled && y == 0.0 )
1804 continue;
1805
1806 SetLineWidth( ( j % m_gridTick ) ? marker : doubleMarker );
1807 drawGridLine( VECTOR2D( gridStartX * gridScreenSize.x + m_gridOrigin.x, y ),
1808 VECTOR2D( gridEndX * gridScreenSize.x + m_gridOrigin.x, y ) );
1809 }
1810
1811 // Horizontal lines
1812 for( int i = gridStartX; i <= gridEndX; i++ )
1813 {
1814 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1815
1816 if( m_axesEnabled && x == 0.0 )
1817 continue;
1818
1819 SetLineWidth( ( i % m_gridTick ) ? marker : doubleMarker );
1820 drawGridLine( VECTOR2D( x, gridStartY * gridScreenSize.y + m_gridOrigin.y ),
1821 VECTOR2D( x, gridEndY * gridScreenSize.y + m_gridOrigin.y ) );
1822 }
1823 }
1824 else // Dots or Crosses grid
1825 {
1826 m_lineWidthIsOdd = true;
1827 m_isStrokeEnabled = true;
1828
1829 for( int j = gridStartY; j <= gridEndY; j++ )
1830 {
1831 bool tickY = ( j % m_gridTick == 0 );
1832
1833 for( int i = gridStartX; i <= gridEndX; i++ )
1834 {
1835 bool tickX = ( i % m_gridTick == 0 );
1836 VECTOR2D pos{ i * gridScreenSize.x + m_gridOrigin.x,
1837 j * gridScreenSize.y + m_gridOrigin.y };
1838
1840 {
1841 SetLineWidth( ( tickX && tickY ) ? doubleMarker : marker );
1842 drawGridCross( pos );
1843 }
1844 else if( m_gridStyle == GRID_STYLE::DOTS )
1845 {
1846 double doubleGridLineWidth = m_gridLineWidth * 2.0f;
1847 drawGridPoint( pos, ( tickX ) ? doubleGridLineWidth : m_gridLineWidth,
1848 ( tickY ) ? doubleGridLineWidth : m_gridLineWidth );
1849 }
1850 }
1851 }
1852 }
1853}
1854
1855
1856void CAIRO_GAL_BASE::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
1857{
1858 if( aGlyph.IsStroke() )
1859 {
1860 const KIFONT::STROKE_GLYPH& glyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
1861
1862 for( const std::vector<VECTOR2D>& pointList : glyph )
1863 drawPoly( pointList );
1864 }
1865else if( aGlyph.IsOutline() )
1866 {
1867 const KIFONT::OUTLINE_GLYPH& glyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
1868
1869 if( aNth == 0 )
1870 {
1871 cairo_close_path( m_currentContext );
1872 flushPath();
1873
1874 cairo_new_path( m_currentContext );
1875 SetIsFill( true );
1876 SetIsStroke( false );
1877 }
1878
1879 // eventually glyphs should not be drawn as polygons at all,
1880 // but as bitmaps with antialiasing, this is just a stopgap measure
1881 // of getting some form of outline font display
1882
1883 glyph.Triangulate(
1884 [&]( const VECTOR2D& aVertex1, const VECTOR2D& aVertex2, const VECTOR2D& aVertex3 )
1885 {
1886 syncLineWidth();
1887
1888 const VECTOR2D p0 = roundp( xform( aVertex1 ) );
1889 const VECTOR2D p1 = roundp( xform( aVertex2 ) );
1890 const VECTOR2D p2 = roundp( xform( aVertex3 ) );
1891
1892 cairo_move_to( m_currentContext, p0.x, p0.y );
1893 cairo_line_to( m_currentContext, p1.x, p1.y );
1894 cairo_line_to( m_currentContext, p2.x, p2.y );
1895 cairo_close_path( m_currentContext );
1896 cairo_set_fill_rule( m_currentContext, CAIRO_FILL_RULE_EVEN_ODD );
1897 flushPath();
1898 cairo_fill( m_currentContext );
1899 } );
1900
1901 if( aNth == aTotal - 1 )
1902 {
1903 flushPath();
1904 SetIsFill( false );
1905 SetIsStroke( true );
1906 m_isElementAdded = true;
1907 }
1908 }
1909}
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:186
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:1856
void blitCursor(wxMemoryDC &clientDC)
Blit cursor into the current screen.
Definition: cairo_gal.cpp:1198
void drawGridLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a grid line (usually a simplified line function).
Definition: cairo_gal.cpp:1070
cairo_surface_t * m_surface
Cairo surface.
Definition: cairo_gal.h:368
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:386
cairo_matrix_t m_cairoWorldScreenMatrix
Cairo world to screen transform matrix.
Definition: cairo_gal.h:363
void Flush() override
Force all remaining objects to be drawn.
Definition: cairo_gal.cpp:592
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)
Definition: cairo_gal.cpp:178
void Restore() override
Restore the context.
Definition: cairo_gal.cpp:790
unsigned int m_groupCounter
Counter used for generating group keys.
Definition: cairo_gal.h:357
virtual double getScalingFactor()
Definition: cairo_gal.h:256
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
Definition: cairo_gal.cpp:732
void Save() override
Save the context.
Definition: cairo_gal.cpp:772
void ClearCache() override
Delete all data created during caching of graphic items.
Definition: cairo_gal.cpp:970
bool m_isElementAdded
Was an graphic element added ?
Definition: cairo_gal.h:355
void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
Definition: cairo_gal.cpp:952
std::deque< GROUP_ELEMENT > GROUP
A graphic group type definition.
Definition: cairo_gal.h:351
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: cairo_gal.cpp:201
void ClearScreen() override
Clear the screen.
Definition: cairo_gal.cpp:598
void storePath()
Store the actual path.
Definition: cairo_gal.cpp:1149
void DrawGroup(int aGroupNumber) override
Draw the stored group.
Definition: cairo_gal.cpp:835
void drawAxes(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Definition: cairo_gal.cpp:1052
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:371
void SetFillColor(const COLOR4D &aColor) override
Set the fill color.
Definition: cairo_gal.cpp:654
GROUP * m_currentGroup
Currently used group.
Definition: cairo_gal.h:358
void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
Definition: cairo_gal.cpp:1009
const VECTOR2D roundp(const VECTOR2D &v)
Definition: cairo_gal.cpp:192
bool m_isGrouping
Is grouping enabled ?
Definition: cairo_gal.h:354
static constexpr cairo_format_t GAL_FORMAT
Format used to store pixels.
Definition: cairo_gal.h:375
void SetNegativeDrawMode(bool aSetting) override
Set negative draw mode in the renderer.
Definition: cairo_gal.cpp:977
void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Change the depth (Z-axis position) of the group.
Definition: cairo_gal.cpp:945
int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:812
void SetLayerDepth(double aLayerDepth) override
Set the depth of the layer (position on the z-axis)
Definition: cairo_gal.cpp:691
unsigned int getNewGroupNumber()
Return a valid key that can be used as a new group number.
Definition: cairo_gal.cpp:1316
cairo_matrix_t m_currentXform
Definition: cairo_gal.h:364
void SetIsStroke(bool aIsStrokeEnabled) override
Enable/disable stroked outlines.
Definition: cairo_gal.cpp:621
void EndGroup() override
End the group.
Definition: cairo_gal.cpp:828
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:1215
void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
Definition: cairo_gal.cpp:698
void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
Definition: cairo_gal.cpp:446
void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Change the color used to draw the group.
Definition: cairo_gal.cpp:928
std::map< int, GROUP > m_groups
List of graphic groups.
Definition: cairo_gal.h:356
void SetStrokeColor(const COLOR4D &aColor) override
Set the stroke color.
Definition: cairo_gal.cpp:636
cairo_t * m_context
Cairo image.
Definition: cairo_gal.h:367
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition: cairo_gal.cpp:101
void updateWorldScreenMatrix()
Definition: cairo_gal.cpp:108
void EnableDepthTest(bool aEnabled=false) override
Definition: cairo_gal.cpp:1015
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:481
void Scale(const VECTOR2D &aScale) override
Scale the context.
Definition: cairo_gal.cpp:752
std::vector< cairo_matrix_t > m_xformStack
Definition: cairo_gal.h:373
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:365
void drawGridCross(const VECTOR2D &aPoint)
Definition: cairo_gal.cpp:1084
void SetLineWidth(float aLineWidth) override
Set the line width.
Definition: cairo_gal.cpp:672
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:144
double angle_xform(double aAngle)
Transform according to the rotation from m_currentWorld2Screen transform matrix.
Definition: cairo_gal.cpp:130
void syncLineWidth(bool aForceWidth=false, double aWidth=0.0)
Definition: cairo_gal.cpp:215
cairo_t * m_currentContext
Currently used Cairo context for drawing.
Definition: cairo_gal.h:366
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: cairo_gal.cpp:326
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: cairo_gal.cpp:258
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle) override
Draw an arc.
Definition: cairo_gal.cpp:342
void drawGridPoint(const VECTOR2D &aPoint, double aWidth, double aHeight)
Definition: cairo_gal.cpp:1105
void SetIsFill(bool aIsFillEnabled) override
Enable/disable fill.
Definition: cairo_gal.cpp:606
void DrawBitmap(const BITMAP_BASE &aBitmap, double alphaBlend=1.0) override
Draw a bitmap image.
Definition: cairo_gal.cpp:503
@ CMD_SET_STROKE
Enable/disable stroking.
Definition: cairo_gal.h:324
@ CMD_SAVE
Save the transformation matrix.
Definition: cairo_gal.h:334
@ CMD_SCALE
Scale the context.
Definition: cairo_gal.h:333
@ CMD_SET_LINE_WIDTH
Set the line width.
Definition: cairo_gal.h:327
@ CMD_SET_FILL
Enable/disable filling.
Definition: cairo_gal.h:323
@ CMD_CALL_GROUP
Call a group.
Definition: cairo_gal.h:336
@ CMD_ROTATE
Rotate the context.
Definition: cairo_gal.h:331
@ CMD_STROKE_PATH
Set the stroke path.
Definition: cairo_gal.h:328
@ CMD_TRANSLATE
Translate the context.
Definition: cairo_gal.h:332
@ CMD_SET_FILLCOLOR
Set the fill color.
Definition: cairo_gal.h:325
@ CMD_FILL_PATH
Set the fill path.
Definition: cairo_gal.h:329
@ CMD_RESTORE
Restore the transformation matrix.
Definition: cairo_gal.h:335
@ CMD_SET_STROKECOLOR
Set the stroke color.
Definition: cairo_gal.h:326
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:583
void Rotate(double aAngle) override
Rotate the context.
Definition: cairo_gal.cpp:713
void DrawSegmentChain(const std::vector< VECTOR2D > &aPointList, double aWidth) override
Draw a chain of rounded segments.
Definition: cairo_gal.cpp:239
double m_lineWidthInPixels
Definition: cairo_gal.h:360
void DrawGrid() override
Definition: cairo_gal.cpp:1732
void EndGroup() override
End the group.
Definition: cairo_gal.cpp:1504
void deinitSurface()
Destroy Cairo surfaces when are not needed anymore.
Definition: cairo_gal.cpp:1584
void PostPaint(wxPaintEvent &aEvent)
Post an event to m_paint_listener.
Definition: cairo_gal.cpp:1461
bool Show(bool aShow) override
Show/hide the GAL canvas.
Definition: cairo_gal.cpp:1486
void SetTarget(RENDER_TARGET aTarget) override
Set the target for rendering.
Definition: cairo_gal.cpp:1511
wxCursor m_currentwxCursor
wxCursor showing the current native cursor
Definition: cairo_gal.h:533
void skipMouseEvent(wxMouseEvent &aEvent)
Mouse event handler, forwards the event to the child.
Definition: cairo_gal.cpp:1662
unsigned int m_overlayBuffer
Handle to the overlay buffer.
Definition: cairo_gal.h:514
unsigned int m_bufferSize
Size of buffers cairoOutput, bitmapBuffers.
Definition: cairo_gal.h:524
CAIRO_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("CairoCanvas"))
Definition: cairo_gal.cpp:1328
void initSurface()
Prepare Cairo surfaces for drawing.
Definition: cairo_gal.cpp:1563
int BeginGroup() override
Begin a group.
Definition: cairo_gal.cpp:1497
double getScalingFactor() override
Definition: cairo_gal.cpp:1702
bool m_isInitialized
Are Cairo image & surface ready to use.
Definition: cairo_gal.h:531
bool m_validCompositor
Compositor initialization flag.
Definition: cairo_gal.h:518
unsigned int m_mainBuffer
Handle to the main buffer.
Definition: cairo_gal.h:513
int m_stride
Stride value for Cairo.
Definition: cairo_gal.h:529
void onSetNativeCursor(wxSetCursorEvent &aEvent)
Give the correct cursor image when the native widget asks for it.
Definition: cairo_gal.cpp:1726
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition: cairo_gal.cpp:1410
void skipGestureEvent(wxGestureEvent &aEvent)
Skip the gesture event to the parent.
Definition: cairo_gal.cpp:1670
void allocateBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1598
void onPaint(wxPaintEvent &aEvent)
Paint event handler.
Definition: cairo_gal.cpp:1641
void StartNegativesLayer() override
Begins rendering in a new layer that will be copied to the main layer in EndNegativesLayer().
Definition: cairo_gal.cpp:996
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
Definition: cairo_gal.cpp:1469
RENDER_TARGET GetTarget() const override
Get the currently used target for rendering.
Definition: cairo_gal.cpp:1535
void setCompositor()
Prepare the compositor.
Definition: cairo_gal.cpp:1625
bool SetNativeCursorStyle(KICURSOR aCursor, bool aHiDPI) override
Set the cursor in the native panel.
Definition: cairo_gal.cpp:1708
unsigned char * m_bitmapBuffer
Storage of the Cairo image.
Definition: cairo_gal.h:528
wxEvtHandler * m_mouseListener
Mouse listener.
Definition: cairo_gal.h:522
void ClearTarget(RENDER_TARGET aTarget) override
Clear the target for rendering.
Definition: cairo_gal.cpp:1541
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
For HiDPI support.
Definition: cairo_gal.cpp:1678
~CAIRO_GAL()
Return true if the GAL canvas is visible on the screen.
Definition: cairo_gal.cpp:1390
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: cairo_gal.cpp:1396
wxEvtHandler * m_paintListener
Paint listener.
Definition: cairo_gal.h:523
unsigned int m_tempBuffer
Handle to the temp buffer.
Definition: cairo_gal.h:515
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: cairo_gal.h:517
wxWindow * m_parentWindow
Parent window.
Definition: cairo_gal.h:521
wxBitmap * m_wxBitmap
Output buffer bitmap.
Definition: cairo_gal.h:525
void EndDiffLayer() override
Ends rendering of a differential layer.
Definition: cairo_gal.cpp:990
std::shared_ptr< CAIRO_COMPOSITOR > m_compositor
Object for layers compositing.
Definition: cairo_gal.h:512
void deleteBitmaps()
Allocate the bitmaps for drawing.
Definition: cairo_gal.cpp:1615
void EndNegativesLayer() override
Ends rendering of a negatives layer and draws it to the main layer.
Definition: cairo_gal.cpp:1003
void StartDiffLayer() override
Begins rendering of a differential layer.
Definition: cairo_gal.cpp:983
unsigned int m_savedBuffer
Handle to buffer to restore after rendering to temp buffer.
Definition: cairo_gal.h:516
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 ?
VECTOR2I m_bitmapSize
Bitmap size, in physical pixels.
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:341
GRAPHICS_COMMAND m_Command
Command to execute.
Definition: cairo_gal.h:342
cairo_path_t * m_CairoPath
Pointer to a Cairo path.
Definition: cairo_gal.h:348
double DblArg[MAX_CAIRO_ARGUMENTS]
Arguments for Cairo commands.
Definition: cairo_gal.h:344
union KIGFX::CAIRO_GAL_BASE::GROUP_ELEMENT::@29 m_Argument
bool BoolArg
A bool argument.
Definition: cairo_gal.h:345
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