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