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