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