KiCad PCB EDA Suite
opengl_gal.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5 * Copyright (C) 2012-2021 Kicad Developers, see AUTHORS.txt for contributors.
6 * Copyright (C) 2013-2017 CERN
7 * @author Maciej Suminski <[email protected]>
8 *
9 * Graphics Abstraction Layer (GAL) for OpenGL
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, you may find one here:
23 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24 * or you may search the http://www.gnu.org website for the version 2 license,
25 * or you may write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29// Apple, in their infinite wisdom, has decided to mark OpenGL as deprecated.
30// Luckily we can silence warnings about its deprecation.
31#ifdef __APPLE__
32#define GL_SILENCE_DEPRECATION 1
33#endif
34
35#include <advanced_config.h>
37#include <gal/opengl/utils.h>
38#include <gal/definitions.h>
39#include <gl_context_mgr.h>
41#include <bitmap_base.h>
42#include <bezier_curves.h>
43#include <math/util.h> // for KiROUND
44#include <trace_helpers.h>
45
46#include <wx/frame.h>
47
48#include <macros.h>
50
51#include <profile.h>
52#include <trace_helpers.h>
53
54#include <gl_utils.h>
55
56#include <functional>
57#include <limits>
58#include <memory>
59using namespace std::placeholders;
60using namespace KIGFX;
61
62// Currently the bitmap cache is disabled because the code has serious issues and
63// work fine does not work fine:
64// created GL textures are never deleted when a bitmap is deleted
65// the key to retrieve a CACHED_BITMAP is the BITMAP_BASE items* pointer.
66// Because in code many BITMAP_BASE items are temporary cloned for drawing purposes,
67// it creates a lot of CACHED_BITMAP never deleted thus created memory leak
68// So to reenable the bitmaps cache, serious changes in code are needed:
69// At least:
70// - use a key that works on cloned BITMAP_BASE items
71// - handle rotated bitmaps without create a new cached item
72// - add a "garbage collector" to delete not existing BITMAP_BASE items
73// After tests, caching bitmaps do not speedup significantly drawings.
74#define DISABLE_BITMAP_CACHE
75
76// The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
77// (see ubuntu-font-licence-1.0.txt for details)
78#include "gl_resources.h"
79#include <glsl_kicad_frag.h>
80#include <glsl_kicad_vert.h>
81using namespace KIGFX::BUILTIN_FONT;
82
83static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
84static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
85
86wxGLContext* OPENGL_GAL::m_glMainContext = nullptr;
87int OPENGL_GAL::m_instanceCounter = 0;
88GLuint OPENGL_GAL::g_fontTexture = 0;
89bool OPENGL_GAL::m_isBitmapFontLoaded = false;
90
91namespace KIGFX
92{
94{
95public:
97
99
100 GLuint RequestBitmap( const BITMAP_BASE* aBitmap );
101
102private:
104 {
105 GLuint id;
106 int w, h;
107 };
108
109 GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
110
111 std::map< const BITMAP_BASE*, CACHED_BITMAP> m_bitmaps;
112};
113
114}; // namespace KIGFX
115
116
117GL_BITMAP_CACHE::~GL_BITMAP_CACHE()
118{
119 for( auto& bitmap : m_bitmaps )
120 glDeleteTextures( 1, &bitmap.second.id );
121}
122
123
125{
126#ifndef DISABLE_BITMAP_CACHE
127 auto it = m_bitmaps.find( aBitmap );
128
129 if( it != m_bitmaps.end() )
130 {
131 // A bitmap is found in cache bitmap. Ensure the associated texture
132 // is still valid.
133 // It can be destroyed somewhere or the corresponding bitmap can be
134 // modifed (rotated)
135 if( ( it->second.w == aBitmap->GetSizePixels().x ) &&
136 ( it->second.h == aBitmap->GetSizePixels().y ) &&
137 glIsTexture( it->second.id ) )
138 {
139 return it->second.id;
140 }
141 else
142 {
143 // Delete the invalid bitmap cache and its data
144 glDeleteTextures( 1, &it->second.id );
145 m_bitmaps.erase( it );
146 }
147
148 // the cached bitmap is not valid and deleted, it will be recreated.
149 }
150
151#endif
152 return cacheBitmap( aBitmap );
153}
154
155
157{
158 CACHED_BITMAP bmp;
159
160 bmp.w = aBitmap->GetSizePixels().x;
161 bmp.h = aBitmap->GetSizePixels().y;
162
163 // The bitmap size needs to be a multiple of 4.
164 // This is easiest to achieve by ensuring that each row
165 // has a multiple of 4 pixels
166 int extra_w = bmp.w % 4;
167
168 if( extra_w )
169 extra_w = 4 - extra_w;
170
171 GLuint textureID;
172 glGenTextures( 1, &textureID );
173
174 // make_unique initializes this to 0, so extra pixels are transparent
175 auto buf = std::make_unique<uint8_t[]>( ( bmp.w + extra_w ) * bmp.h * 4 );
176 const wxImage& imgData = *aBitmap->GetImageData();
177
178 for( int y = 0; y < bmp.h; y++ )
179 {
180 for( int x = 0; x < bmp.w; x++ )
181 {
182 uint8_t* p = buf.get() + ( ( bmp.w + extra_w ) * y + x ) * 4;
183
184 p[0] = imgData.GetRed( x, y );
185 p[1] = imgData.GetGreen( x, y );
186 p[2] = imgData.GetBlue( x, y );
187
188 if( imgData.HasAlpha() )
189 p[3] = imgData.GetAlpha( x, y );
190 else if( imgData.HasMask() && p[0] == imgData.GetMaskRed()
191 && p[1] == imgData.GetMaskGreen() && p[2] == imgData.GetMaskBlue() )
192 p[3] = wxALPHA_TRANSPARENT;
193 else
194 p[3] = wxALPHA_OPAQUE;
195 }
196 }
197
198 glBindTexture( GL_TEXTURE_2D, textureID );
199 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w + extra_w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
200 buf.get() );
201
202 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
203 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
204
205 bmp.id = textureID;
206
207#ifndef DISABLE_BITMAP_CACHE
208 m_bitmaps[aBitmap] = bmp;
209#endif
210
211 return textureID;
212}
213
214OPENGL_GAL::OPENGL_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
215 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
216 const wxString& aName ) :
217 GAL( aDisplayOptions ),
218 HIDPI_GL_CANVAS( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize,
219 wxEXPAND, aName ),
220 m_mouseListener( aMouseListener ),
221 m_paintListener( aPaintListener ),
222 m_currentManager( nullptr ),
223 m_cachedManager( nullptr ),
224 m_nonCachedManager( nullptr ),
225 m_overlayManager( nullptr ),
226 m_tempManager( nullptr ),
227 m_mainBuffer( 0 ),
228 m_overlayBuffer( 0 ),
229 m_tempBuffer( 0 ),
230 m_isContextLocked( false ),
231 m_lockClientCookie( 0 )
232{
233 if( m_glMainContext == nullptr )
234 {
236
238 }
239 else
240 {
242 }
243
244 m_shader = new SHADER();
246
247 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
248
251
252 // Initialize the flags
255 m_isInitialized = false;
256 m_isGrouping = false;
257 m_groupCounter = 0;
258
259 // Connect the native cursor handler
260 Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( OPENGL_GAL::onSetNativeCursor ), nullptr,
261 this );
262
263 // Connecting the event handlers
264 Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) );
265
266 // Mouse events are skipped to the parent
267 Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
268 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
269 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
270 Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
271 Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
272 Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
273 Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
274 Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
275 Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
276 Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
277 Connect( wxEVT_AUX1_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
278 Connect( wxEVT_AUX1_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
279 Connect( wxEVT_AUX1_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
280 Connect( wxEVT_AUX2_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
281 Connect( wxEVT_AUX2_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
282 Connect( wxEVT_AUX2_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
283 Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
284#if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
285 Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
286#endif
287#if defined _WIN32 || defined _WIN64
288 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
289#endif
290
291 SetSize( aParent->GetClientSize() );
293
294 // Grid color settings are different in Cairo and OpenGL
295 SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
297
298 // Tesselator initialization
299 m_tesselator = gluNewTess();
301
302 gluTessProperty( m_tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
303
305
306 // Avoid uninitialized variables:
311}
312
313
315{
317
319 glFlush();
320 gluDeleteTess( m_tesselator );
321 ClearCache();
322
323 delete m_compositor;
324
325 if( m_isInitialized )
326 {
327 delete m_cachedManager;
328 delete m_nonCachedManager;
329 delete m_overlayManager;
330 delete m_tempManager;
331 }
332
334
335 // If it was the main context, then it will be deleted
336 // when the last OpenGL GAL instance is destroyed (a few lines below)
339
340 delete m_shader;
341
342 // Are we destroying the last GAL instance?
343 if( m_instanceCounter == 0 )
344 {
346
348 {
349 glDeleteTextures( 1, &g_fontTexture );
350 m_isBitmapFontLoaded = false;
351 }
352
355 m_glMainContext = nullptr;
356 }
357}
358
359
361{
362 wxString retVal = wxEmptyString;
363
364 wxFrame* testFrame = new wxFrame( nullptr, wxID_ANY, wxT( "" ), wxDefaultPosition,
365 wxSize( 1, 1 ), wxFRAME_TOOL_WINDOW | wxNO_BORDER );
366
367 KIGFX::OPENGL_GAL* opengl_gal = nullptr;
368
369 try
370 {
371 opengl_gal = new KIGFX::OPENGL_GAL( aOptions, testFrame );
372
373 testFrame->Raise();
374 testFrame->Show();
375
376 GAL_CONTEXT_LOCKER lock( opengl_gal );
377 opengl_gal->init();
378 }
379 catch( std::runtime_error& err )
380 {
381 //Test failed
382 retVal = wxString( err.what() );
383 }
384
385 delete opengl_gal;
386 delete testFrame;
387
388 return retVal;
389}
390
391
392void OPENGL_GAL::PostPaint( wxPaintEvent& aEvent )
393{
394 // posts an event to m_paint_listener to ask for redraw the canvas.
395 if( m_paintListener )
396 wxPostEvent( m_paintListener, aEvent );
397}
398
399
401{
402 GAL_CONTEXT_LOCKER lock( this );
403
404 bool refresh = false;
405
407 {
410 refresh = true;
411 }
412
414 {
416 refresh = true;
417 }
418
419 if( super::updatedGalDisplayOptions( aOptions ) || refresh )
420 {
421 Refresh();
422 refresh = true;
423 }
424
425 return refresh;
426}
427
428
430{
432 return std::min( std::abs( matrix.GetScale().x ), std::abs( matrix.GetScale().y ) );
433}
434
435
437{
438 double sf = GetScaleFactor();
439 return VECTOR2D( 2.0 / (double) ( m_screenSize.x * sf ), 2.0 /
440 (double) ( m_screenSize.y * sf ) );
441}
442
443
445{
446#ifdef KICAD_GAL_PROFILE
447 PROF_TIMER totalRealTime( "OPENGL_GAL::beginDrawing()", true );
448#endif /* KICAD_GAL_PROFILE */
449
450 wxASSERT_MSG( m_isContextLocked, "GAL_DRAWING_CONTEXT RAII object should have locked context. "
451 "Calling GAL::beginDrawing() directly is not allowed." );
452
453 wxASSERT_MSG( IsVisible(), "GAL::beginDrawing() must not be entered when GAL is not visible. "
454 "Other drawing routines will expect everything to be initialized "
455 "which will not be the case." );
456
457 if( !m_isInitialized )
458 init();
459
460 // Set up the view port
461 glMatrixMode( GL_PROJECTION );
462 glLoadIdentity();
463
464 // Create the screen transformation (Do the RH-LH conversion here)
465 glOrtho( 0, (GLint) m_screenSize.x, (GLsizei) m_screenSize.y, 0,
467
469 {
470 // Prepare rendering target buffers
474 try
475 {
477 }
478 catch( const std::runtime_error& )
479 {
480 wxLogVerbose( "Could not create a framebuffer for overlays.\n" );
481 m_overlayBuffer = 0;
482 }
483
485 }
486
488
489 // Disable 2D Textures
490 glDisable( GL_TEXTURE_2D );
491
492 glShadeModel( GL_FLAT );
493
494 // Enable the depth buffer
495 glEnable( GL_DEPTH_TEST );
496 glDepthFunc( GL_LESS );
497
498 // Setup blending, required for transparent objects
499 glEnable( GL_BLEND );
500 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
501
502 glMatrixMode( GL_MODELVIEW );
503
504 // Set up the world <-> screen transformation
506 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
507 matrixData[0] = m_worldScreenMatrix.m_data[0][0];
508 matrixData[1] = m_worldScreenMatrix.m_data[1][0];
509 matrixData[2] = m_worldScreenMatrix.m_data[2][0];
510 matrixData[4] = m_worldScreenMatrix.m_data[0][1];
511 matrixData[5] = m_worldScreenMatrix.m_data[1][1];
512 matrixData[6] = m_worldScreenMatrix.m_data[2][1];
513 matrixData[12] = m_worldScreenMatrix.m_data[0][2];
514 matrixData[13] = m_worldScreenMatrix.m_data[1][2];
515 matrixData[14] = m_worldScreenMatrix.m_data[2][2];
516 glLoadMatrixd( matrixData );
517
518 // Set defaults
521
522 // Remove all previously stored items
526
531
533 {
534 // Keep bitmap font texture always bound to the second texturing unit
535 const GLint FONT_TEXTURE_UNIT = 2;
536
537 // Either load the font atlas to video memory, or simply bind it to a texture unit
539 {
540 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
541 glGenTextures( 1, &g_fontTexture );
542 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
543 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, font_image.width, font_image.height, 0, GL_RGB,
544 GL_UNSIGNED_BYTE, font_image.pixels );
545 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
546 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
547 checkGlError( "loading bitmap font", __FILE__, __LINE__ );
548
549 glActiveTexture( GL_TEXTURE0 );
550
552 }
553 else
554 {
555 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
556 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
557 glActiveTexture( GL_TEXTURE0 );
558 }
559
560 // Set shader parameter
561 GLint ufm_fontTexture = m_shader->AddParameter( "u_fontTexture" );
562 GLint ufm_fontTextureWidth = m_shader->AddParameter( "u_fontTextureWidth" );
563 ufm_worldPixelSize = m_shader->AddParameter( "u_worldPixelSize" );
564 ufm_screenPixelSize = m_shader->AddParameter( "u_screenPixelSize" );
565 ufm_pixelSizeMultiplier = m_shader->AddParameter( "u_pixelSizeMultiplier" );
566 ufm_antialiasingOffset = m_shader->AddParameter( "u_antialiasingOffset" );
567
568 m_shader->Use();
569 m_shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT );
570 m_shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width );
572 checkGlError( "setting bitmap font sampler as shader parameter", __FILE__, __LINE__ );
573
575 }
576
577 m_shader->Use();
579 (float) ( getWorldPixelSize() / GetScaleFactor() ) );
580 const VECTOR2D& screenPixelSize = getScreenPixelSize();
581 m_shader->SetParameter( ufm_screenPixelSize, screenPixelSize );
582 double pixelSizeMultiplier = m_compositor->GetAntialiasSupersamplingFactor();
583 m_shader->SetParameter( ufm_pixelSizeMultiplier, (float) pixelSizeMultiplier );
585 renderingOffset.x *= screenPixelSize.x;
586 renderingOffset.y *= screenPixelSize.y;
587 m_shader->SetParameter( ufm_antialiasingOffset, renderingOffset );
589
590 // Something betreen BeginDrawing and EndDrawing seems to depend on
591 // this texture unit being active, but it does not assure it itself.
592 glActiveTexture( GL_TEXTURE0 );
593
594 // Unbind buffers - set compositor for direct drawing
596
597#ifdef KICAD_GAL_PROFILE
598 totalRealTime.Stop();
599 wxLogTrace( traceGalProfile, wxT( "OPENGL_GAL::beginDrawing(): %.1f ms" ),
600 totalRealTime.msecs() );
601#endif /* KICAD_GAL_PROFILE */
602}
603
604
606{
607 wxASSERT_MSG( m_isContextLocked, "What happened to the context lock?" );
608
609 PROF_TIMER cntTotal("gl-end-total");
610 PROF_TIMER cntEndCached("gl-end-cached");
611 PROF_TIMER cntEndNoncached("gl-end-noncached");
612 PROF_TIMER cntEndOverlay("gl-end-overlay");
613 PROF_TIMER cntComposite("gl-composite");
614 PROF_TIMER cntSwap("gl-swap");
615
616 cntTotal.Start();
617 // Cached & non-cached containers are rendered to the same buffer
619
620 cntEndNoncached.Start();
622 cntEndNoncached.Stop();
623
624 cntEndCached.Start();
626 cntEndCached.Stop();
627
628 cntEndOverlay.Start();
629 // Overlay container is rendered to a different buffer
630 if( m_overlayBuffer )
632
634 cntEndOverlay.Stop();
635
636 cntComposite.Start();
637 // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
638 glColor4d( 1.0, 1.0, 1.0, 1.0 );
639
640 // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
642
643 if( m_overlayBuffer )
645
647 blitCursor();
648
649 cntComposite.Stop();
650
651 cntSwap.Start();
652 SwapBuffers();
653 cntSwap.Stop();
654
655 cntTotal.Stop();
656
657 KI_TRACE( traceGalProfile, "Timing: %s %s %s %s %s %s\n", cntTotal.to_string(),
658 cntEndCached.to_string(), cntEndNoncached.to_string(), cntEndOverlay.to_string(),
659 cntComposite.to_string(), cntSwap.to_string() );
660}
661
662
663void OPENGL_GAL::LockContext( int aClientCookie )
664{
665 wxASSERT_MSG( !m_isContextLocked, "Context already locked." );
666 m_isContextLocked = true;
667 m_lockClientCookie = aClientCookie;
668
670}
671
672
673void OPENGL_GAL::UnlockContext( int aClientCookie )
674{
675 wxASSERT_MSG( m_isContextLocked, "Context not locked. A GAL_CONTEXT_LOCKER RAII object must "
676 "be stacked rather than making separate lock/unlock calls." );
677
678 wxASSERT_MSG( m_lockClientCookie == aClientCookie, "Context was locked by a different client. "
679 "Should not be possible with RAII objects." );
680
681 m_isContextLocked = false;
682
684}
685
686
688{
689 wxASSERT_MSG( m_isContextLocked, "GAL_UPDATE_CONTEXT RAII object should have locked context. "
690 "Calling this from anywhere else is not allowed." );
691
692 wxASSERT_MSG( IsVisible(), "GAL::beginUpdate() must not be entered when GAL is not visible. "
693 "Other update routines will expect everything to be initialized "
694 "which will not be the case." );
695
696 if( !m_isInitialized )
697 init();
698
700}
701
702
704{
705 if( !m_isInitialized )
706 return;
707
709}
710
711
712void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
713{
715
716 drawLineQuad( aStartPoint, aEndPoint );
717}
718
719
720void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
721 double aWidth )
722{
723 drawSegment( aStartPoint, aEndPoint, aWidth );
724}
725
726
727void OPENGL_GAL::drawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth,
728 bool aReserve )
729{
730 VECTOR2D startEndVector = aEndPoint - aStartPoint;
731 double lineLength = startEndVector.EuclideanNorm();
732
733 float startx = aStartPoint.x;
734 float starty = aStartPoint.y;
735 float endx = aStartPoint.x + lineLength;
736 float endy = aStartPoint.y + lineLength;
737
738 // Be careful about floating point rounding. As we draw segments in larger and larger
739 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
740 // segments. In this case, we need to draw a circle for the minimal segment.
741 if( startx == endx || starty == endy )
742 {
743 drawCircle( aStartPoint, aWidth / 2, aReserve );
744 return;
745 }
746
747 if( m_isFillEnabled || aWidth == 1.0 )
748 {
750
751 SetLineWidth( aWidth );
752 drawLineQuad( aStartPoint, aEndPoint, aReserve );
753 }
754 else
755 {
756 EDA_ANGLE lineAngle( startEndVector );
757 // Outlined tracks
758
759 SetLineWidth( 1.0 );
762
763 Save();
764
765 if( aReserve )
766 m_currentManager->Reserve( 6 + 6 + 3 + 3 ); // Two line quads and two semicircles
767
768 m_currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
769 m_currentManager->Rotate( lineAngle.AsRadians(), 0.0f, 0.0f, 1.0f );
770
771 drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ), VECTOR2D( lineLength, aWidth / 2.0 ), false );
772
773 drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ), VECTOR2D( lineLength, -aWidth / 2.0 ),
774 false );
775
776 // Draw line caps
777 drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2, false );
778 drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2, false );
779
780 Restore();
781 }
782}
783
784
785void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
786{
787 drawCircle( aCenterPoint, aRadius );
788}
789
790
791void OPENGL_GAL::drawCircle( const VECTOR2D& aCenterPoint, double aRadius, bool aReserve )
792{
793 if( m_isFillEnabled )
794 {
795 if( aReserve )
797
799
800 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
801 * Parameters given to Shader() are indices of the triangle's vertices
802 * (if you want to understand more, check the vertex shader source [shader.vert]).
803 * Shader uses this coordinates to determine if fragments are inside the circle or not.
804 * Does the calculations in the vertex shader now (pixel alignment)
805 * v2
806 * /\
807 * //\\
808 * v0 /_\/_\ v1
809 */
811 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
812
814 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
815
817 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
818 }
820 {
821 if( aReserve )
823
826
827 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
828 * Parameters given to Shader() are indices of the triangle's vertices
829 * (if you want to understand more, check the vertex shader source [shader.vert]).
830 * and the line width. Shader uses this coordinates to determine if fragments are
831 * inside the circle or not.
832 * v2
833 * /\
834 * //\\
835 * v0 /_\/_\ v1
836 */
838 m_currentManager->Vertex( aCenterPoint.x, // v0
839 aCenterPoint.y, m_layerDepth );
840
842 m_currentManager->Vertex( aCenterPoint.x, // v1
843 aCenterPoint.y, m_layerDepth );
844
846 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, // v2
847 m_layerDepth );
848 }
849}
850
851
852void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
853 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle )
854{
855 if( aRadius <= 0 )
856 return;
857
858 double startAngle = aStartAngle.AsRadians();
859 double endAngle = aEndAngle.AsRadians();
860
861 // Swap the angles, if start angle is greater than end angle
862 SWAP( startAngle, >, endAngle );
863
864 const double alphaIncrement = calcAngleStep( aRadius );
865
866 Save();
867 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
868
869 if( m_isFillEnabled )
870 {
871 double alpha;
874
875 // Triangle fan
876 for( alpha = startAngle; ( alpha + alphaIncrement ) < endAngle; )
877 {
880 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
881 m_layerDepth );
882 alpha += alphaIncrement;
883 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
884 m_layerDepth );
885 }
886
887 // The last missing triangle
888 const VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
889
892 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, m_layerDepth );
893 m_currentManager->Vertex( endPoint.x, endPoint.y, m_layerDepth );
894 }
895
897 {
900
901 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
902 double alpha;
903
904 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
905 {
906 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
907 DrawLine( p, p_next );
908
909 p = p_next;
910 }
911
912 // Draw the last missing part
913 if( alpha != endAngle )
914 {
915 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
916 DrawLine( p, p_last );
917 }
918 }
919
920 Restore();
921}
922
923
924void OPENGL_GAL::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
925 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aEndAngle,
926 double aWidth, double aMaxError )
927{
928 if( aRadius <= 0 )
929 {
930 // Arcs of zero radius are a circle of aWidth diameter
931 if( aWidth > 0 )
932 DrawCircle( aCenterPoint, aWidth / 2.0 );
933
934 return;
935 }
936
937 double startAngle = aStartAngle.AsRadians();
938 double endAngle = aEndAngle.AsRadians();
939
940 // Swap the angles, if start angle is greater than end angle
941 SWAP( startAngle, >, endAngle );
942
943 // Calculate the seg count to approximate the arc with aMaxError or less
944 int segCount360 = GetArcToSegmentCount( aRadius, aMaxError, FULL_CIRCLE );
945 segCount360 = std::max( SEG_PER_CIRCLE_COUNT, segCount360 );
946 double alphaIncrement = 2.0 * M_PI / segCount360;
947
948 // Refinement: Use a segment count multiple of 2, because we have a control point
949 // on the middle of the arc, and the look is better if it is on a segment junction
950 // because there is no approx error
951 int seg_count = KiROUND( ( endAngle - startAngle ) / alphaIncrement );
952
953 if( seg_count % 2 != 0 )
954 seg_count += 1;
955
956 // Recalculate alphaIncrement with a even integer number of segment
957 if( seg_count )
958 alphaIncrement = ( endAngle - startAngle ) / seg_count;
959
960 Save();
961 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
962
964 {
967
968 double width = aWidth / 2.0;
969 VECTOR2D startPoint( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
970 VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
971
972 drawStrokedSemiCircle( startPoint, width, startAngle + M_PI );
973 drawStrokedSemiCircle( endPoint, width, endAngle );
974
975 VECTOR2D pOuter( cos( startAngle ) * ( aRadius + width ),
976 sin( startAngle ) * ( aRadius + width ) );
977
978 VECTOR2D pInner( cos( startAngle ) * ( aRadius - width ),
979 sin( startAngle ) * ( aRadius - width ) );
980
981 double alpha;
982
983 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
984 {
985 VECTOR2D pNextOuter( cos( alpha ) * ( aRadius + width ),
986 sin( alpha ) * ( aRadius + width ) );
987 VECTOR2D pNextInner( cos( alpha ) * ( aRadius - width ),
988 sin( alpha ) * ( aRadius - width ) );
989
990 DrawLine( pOuter, pNextOuter );
991 DrawLine( pInner, pNextInner );
992
993 pOuter = pNextOuter;
994 pInner = pNextInner;
995 }
996
997 // Draw the last missing part
998 if( alpha != endAngle )
999 {
1000 VECTOR2D pLastOuter( cos( endAngle ) * ( aRadius + width ),
1001 sin( endAngle ) * ( aRadius + width ) );
1002 VECTOR2D pLastInner( cos( endAngle ) * ( aRadius - width ),
1003 sin( endAngle ) * ( aRadius - width ) );
1004
1005 DrawLine( pOuter, pLastOuter );
1006 DrawLine( pInner, pLastInner );
1007 }
1008 }
1009
1010 if( m_isFillEnabled )
1011 {
1013 SetLineWidth( aWidth );
1014
1015 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1016 double alpha;
1017
1018 int lineCount = 0;
1019
1020 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1021 {
1022 lineCount++;
1023 }
1024
1025 // The last missing part
1026 if( alpha != endAngle )
1027 {
1028 lineCount++;
1029 }
1030
1031 reserveLineQuads( lineCount );
1032
1033 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1034 {
1035 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
1036 drawLineQuad( p, p_next, false );
1037
1038 p = p_next;
1039 }
1040
1041 // Draw the last missing part
1042 if( alpha != endAngle )
1043 {
1044 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1045 drawLineQuad( p, p_last, false );
1046 }
1047 }
1048
1049 Restore();
1050}
1051
1052
1053void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1054{
1055 // Compute the diagonal points of the rectangle
1056 VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
1057 VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
1058
1059 // Fill the rectangle
1060 if( m_isFillEnabled )
1061 {
1065
1066 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1067 m_currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, m_layerDepth );
1068 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1069
1070 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1071 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1072 m_currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, m_layerDepth );
1073 }
1074
1075 // Stroke the outline
1076 if( m_isStrokeEnabled )
1077 {
1079 m_strokeColor.a );
1080
1081 std::deque<VECTOR2D> pointList;
1082 pointList.push_back( aStartPoint );
1083 pointList.push_back( diagonalPointA );
1084 pointList.push_back( aEndPoint );
1085 pointList.push_back( diagonalPointB );
1086 pointList.push_back( aStartPoint );
1087 DrawPolyline( pointList );
1088 }
1089}
1090
1091
1092void OPENGL_GAL::DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth )
1093{
1095 [&]( int idx )
1096 {
1097 return aPointList[idx];
1098 },
1099 aPointList.size(), aWidth );
1100}
1101
1102
1103void OPENGL_GAL::DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth )
1104{
1105 auto numPoints = aLineChain.PointCount();
1106
1107 if( aLineChain.IsClosed() )
1108 numPoints += 1;
1109
1111 [&]( int idx )
1112 {
1113 return aLineChain.CPoint( idx );
1114 },
1115 numPoints, aWidth );
1116}
1117
1118
1119void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
1120{
1122 [&]( int idx )
1123 {
1124 return aPointList[idx];
1125 },
1126 aPointList.size() );
1127}
1128
1129
1130void OPENGL_GAL::DrawPolyline( const std::vector<VECTOR2D>& aPointList )
1131{
1133 [&]( int idx )
1134 {
1135 return aPointList[idx];
1136 },
1137 aPointList.size() );
1138}
1139
1140
1141void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
1142{
1144 [&]( int idx )
1145 {
1146 return aPointList[idx];
1147 },
1148 aListSize );
1149}
1150
1151
1153{
1154 auto numPoints = aLineChain.PointCount();
1155
1156 if( aLineChain.IsClosed() )
1157 numPoints += 1;
1158
1160 [&]( int idx )
1161 {
1162 return aLineChain.CPoint( idx );
1163 },
1164 numPoints );
1165}
1166
1167
1168void OPENGL_GAL::DrawPolylines( const std::vector<std::vector<VECTOR2D>>& aPointList )
1169{
1170 int lineQuadCount = 0;
1171
1172 for( const std::vector<VECTOR2D>& points : aPointList )
1173 lineQuadCount += points.size() - 1;
1174
1175 reserveLineQuads( lineQuadCount );
1176
1177 for( const std::vector<VECTOR2D>& points : aPointList )
1178 {
1180 [&]( int idx )
1181 {
1182 return points[idx];
1183 },
1184 points.size(), false );
1185 }
1186}
1187
1188
1189void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
1190{
1191 wxCHECK( aPointList.size() >= 2, /* void */ );
1192 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aPointList.size()] );
1193 GLdouble* ptr = points.get();
1194
1195 for( const VECTOR2D& p : aPointList )
1196 {
1197 *ptr++ = p.x;
1198 *ptr++ = p.y;
1199 *ptr++ = m_layerDepth;
1200 }
1201
1202 drawPolygon( points.get(), aPointList.size() );
1203}
1204
1205
1206void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
1207{
1208 wxCHECK( aListSize >= 2, /* void */ );
1209 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aListSize] );
1210 GLdouble* target = points.get();
1211 const VECTOR2D* src = aPointList;
1212
1213 for( int i = 0; i < aListSize; ++i )
1214 {
1215 *target++ = src->x;
1216 *target++ = src->y;
1217 *target++ = m_layerDepth;
1218 ++src;
1219 }
1220
1221 drawPolygon( points.get(), aListSize );
1222}
1223
1224
1226 bool aStrokeTriangulation )
1227{
1230
1231 if( m_isFillEnabled )
1232 {
1233 int totalTriangleCount = 0;
1234
1235 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1236 {
1237 auto triPoly = aPolySet.TriangulatedPolygon( j );
1238
1239 totalTriangleCount += triPoly->GetTriangleCount();
1240 }
1241
1242 m_currentManager->Reserve( 3 * totalTriangleCount );
1243
1244 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1245 {
1246 auto triPoly = aPolySet.TriangulatedPolygon( j );
1247
1248 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1249 {
1250 VECTOR2I a, b, c;
1251 triPoly->GetTriangle( i, a, b, c );
1255 }
1256 }
1257 }
1258
1259 if( m_isStrokeEnabled )
1260 {
1261 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1262 {
1263 const auto& poly = aPolySet.Polygon( j );
1264
1265 for( const auto& lc : poly )
1266 {
1267 DrawPolyline( lc );
1268 }
1269 }
1270 }
1271
1272 if( ADVANCED_CFG::GetCfg().m_DrawTriangulationOutlines )
1273 {
1274 aStrokeTriangulation = true;
1275 SetStrokeColor( COLOR4D( 0.0, 1.0, 0.2, 1.0 ) );
1276 }
1277
1278 if( aStrokeTriangulation )
1279 {
1280 COLOR4D oldStrokeColor = m_strokeColor;
1281 double oldLayerDepth = m_layerDepth;
1282
1284
1285 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1286 {
1287 auto triPoly = aPolySet.TriangulatedPolygon( j );
1288
1289 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1290 {
1291 VECTOR2I a, b, c;
1292 triPoly->GetTriangle( i, a, b, c );
1293 DrawLine( a, b );
1294 DrawLine( b, c );
1295 DrawLine( c, a );
1296 }
1297 }
1298
1299 SetStrokeColor( oldStrokeColor );
1300 SetLayerDepth( oldLayerDepth );
1301 }
1302}
1303
1304
1305void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
1306{
1307 if( aPolySet.IsTriangulationUpToDate() )
1308 {
1309 drawTriangulatedPolyset( aPolySet, aStrokeTriangulation );
1310 return;
1311 }
1312
1313 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1314 {
1315 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
1316 DrawPolygon( outline );
1317 }
1318}
1319
1320
1322{
1323 wxCHECK( aPolygon.PointCount() >= 2, /* void */ );
1324
1325 const int pointCount = aPolygon.SegmentCount() + 1;
1326 std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1327 GLdouble* ptr = points.get();
1328
1329 for( int i = 0; i < pointCount; ++i )
1330 {
1331 const VECTOR2I& p = aPolygon.CPoint( i );
1332 *ptr++ = p.x;
1333 *ptr++ = p.y;
1334 *ptr++ = m_layerDepth;
1335 }
1336
1337 drawPolygon( points.get(), pointCount );
1338}
1339
1340
1341void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1342 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
1343 double aFilterValue )
1344{
1345 std::vector<VECTOR2D> output;
1346 std::vector<VECTOR2D> pointCtrl;
1347
1348 pointCtrl.push_back( aStartPoint );
1349 pointCtrl.push_back( aControlPointA );
1350 pointCtrl.push_back( aControlPointB );
1351 pointCtrl.push_back( aEndPoint );
1352
1353 BEZIER_POLY converter( pointCtrl );
1354 converter.GetPoly( output, aFilterValue );
1355
1356 DrawPolyline( &output[0], output.size() );
1357}
1358
1359
1360void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
1361{
1362 GLfloat alpha = std::clamp( alphaBlend, 0.0, 1.0 );
1363
1364 // We have to calculate the pixel size in users units to draw the image.
1365 // m_worldUnitLength is a factor used for converting IU to inches
1366 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
1367 double w = (double) aBitmap.GetSizePixels().x * scale;
1368 double h = (double) aBitmap.GetSizePixels().y * scale;
1369
1370 auto xform = m_currentManager->GetTransformation();
1371
1372 glm::vec4 v0 = xform * glm::vec4( -w / 2, -h / 2, 0.0, 0.0 );
1373 glm::vec4 v1 = xform * glm::vec4( w / 2, h / 2, 0.0, 0.0 );
1374 glm::vec4 trans = xform[3];
1375
1376 auto texture_id = m_bitmapCache->RequestBitmap( &aBitmap );
1377
1378 if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1379 return;
1380
1381 glPushMatrix();
1382 glTranslated( trans.x, trans.y, trans.z );
1383
1384 glEnable( GL_TEXTURE_2D );
1385 glActiveTexture( GL_TEXTURE0 );
1386 glBindTexture( GL_TEXTURE_2D, texture_id );
1387
1388 glBegin( GL_QUADS );
1389 glColor4f( 1.0, 1.0, 1.0, alpha );
1390 glTexCoord2f( 0.0, 0.0 );
1391 glVertex3f( v0.x, v0.y, m_layerDepth );
1392 glColor4f( 1.0, 1.0, 1.0, alpha );
1393 glTexCoord2f( 1.0, 0.0 );
1394 glVertex3f( v1.x, v0.y, m_layerDepth );
1395 glColor4f( 1.0, 1.0, 1.0, alpha );
1396 glTexCoord2f( 1.0, 1.0 );
1397 glVertex3f( v1.x, v1.y, m_layerDepth );
1398 glColor4f( 1.0, 1.0, 1.0, alpha );
1399 glTexCoord2f( 0.0, 1.0 );
1400 glVertex3f( v0.x, v1.y, m_layerDepth );
1401 glEnd();
1402
1403 glBindTexture( GL_TEXTURE_2D, 0 );
1404
1405#ifdef DISABLE_BITMAP_CACHE
1406 glDeleteTextures( 1, &texture_id );
1407#endif
1408
1409 glPopMatrix();
1410}
1411
1412
1413void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2I& aPosition,
1414 const EDA_ANGLE& aAngle )
1415{
1416 // Fallback to generic impl (which uses the stroke font) on cases we don't handle
1417 if( IsTextMirrored()
1418 || aText.Contains( wxT( "^{" ) )
1419 || aText.Contains( wxT( "_{" ) )
1420 || aText.Contains( wxT( "\n" ) ) )
1421 {
1422 return GAL::BitmapText( aText, aPosition, aAngle );
1423 }
1424
1425 const UTF8 text( aText );
1426 VECTOR2D textSize;
1427 float commonOffset;
1428 std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1429
1430 const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1431 double overbarHeight = textSize.y;
1432
1433 Save();
1434
1436 m_currentManager->Translate( aPosition.x, aPosition.y, m_layerDepth );
1437 m_currentManager->Rotate( aAngle.AsRadians(), 0.0f, 0.0f, -1.0f );
1438
1439 double sx = SCALE * ( m_globalFlipX ? -1.0 : 1.0 );
1440 double sy = SCALE * ( m_globalFlipY ? -1.0 : 1.0 );
1441
1442 m_currentManager->Scale( sx, sy, 0 );
1443 m_currentManager->Translate( 0, -commonOffset, 0 );
1444
1445 switch( GetHorizontalJustify() )
1446 {
1448 Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1449 break;
1450
1452 //if( !IsTextMirrored() )
1453 Translate( VECTOR2D( -textSize.x, 0 ) );
1454 break;
1455
1457 //if( IsTextMirrored() )
1458 //Translate( VECTOR2D( -textSize.x, 0 ) );
1459 break;
1460 }
1461
1462 switch( GetVerticalJustify() )
1463 {
1465 break;
1466
1468 Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1469 overbarHeight = 0;
1470 break;
1471
1473 Translate( VECTOR2D( 0, -textSize.y ) );
1474 overbarHeight = -textSize.y / 2.0;
1475 break;
1476 }
1477
1478 int overbarLength = 0;
1479 int overbarDepth = -1;
1480 int braceNesting = 0;
1481
1482 auto iterateString =
1483 [&]( std::function<void( int aOverbarLength, int aOverbarHeight )> overbarFn,
1484 std::function<int( unsigned long aChar )> bitmapCharFn )
1485 {
1486 for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1487 {
1488 wxASSERT_MSG( *chIt != '\n' && *chIt != '\r',
1489 "No support for multiline bitmap text yet" );
1490
1491 if( *chIt == '~' && overbarDepth == -1 )
1492 {
1493 UTF8::uni_iter lookahead = chIt;
1494
1495 if( ++lookahead != end && *lookahead == '{' )
1496 {
1497 chIt = lookahead;
1498 overbarDepth = braceNesting;
1499 braceNesting++;
1500 continue;
1501 }
1502 }
1503 else if( *chIt == '{' )
1504 {
1505 braceNesting++;
1506 }
1507 else if( *chIt == '}' )
1508 {
1509 if( braceNesting > 0 )
1510 braceNesting--;
1511
1512 if( braceNesting == overbarDepth )
1513 {
1514 overbarFn( overbarLength, overbarHeight );
1515 overbarLength = 0;
1516
1517 overbarDepth = -1;
1518 continue;
1519 }
1520 }
1521
1522 if( overbarDepth != -1 )
1523 overbarLength += bitmapCharFn( *chIt );
1524 else
1525 bitmapCharFn( *chIt );
1526 }
1527 };
1528
1529 // First, calculate the amount of characters and overbars to reserve
1530
1531 int charsCount = 0;
1532 int overbarsCount = 0;
1533
1534 iterateString(
1535 [&overbarsCount]( int aOverbarLength, int aOverbarHeight )
1536 {
1537 overbarsCount++;
1538 },
1539 [&charsCount]( unsigned long aChar ) -> int
1540 {
1541 if( aChar != ' ' )
1542 charsCount++;
1543
1544 return 0;
1545 } );
1546
1547 m_currentManager->Reserve( 6 * charsCount + 6 * overbarsCount );
1548
1549 // Now reset the state and actually draw the characters and overbars
1550 overbarLength = 0;
1551 overbarDepth = -1;
1552 braceNesting = 0;
1553
1554 iterateString(
1555 [&]( int aOverbarLength, int aOverbarHeight )
1556 {
1557 drawBitmapOverbar( aOverbarLength, aOverbarHeight, false );
1558 },
1559 [&]( unsigned long aChar ) -> int
1560 {
1561 return drawBitmapChar( aChar, false );
1562 } );
1563
1564 // Handle the case when overbar is active till the end of the drawn text
1565 m_currentManager->Translate( 0, commonOffset, 0 );
1566
1567 if( overbarDepth != -1 && overbarLength > 0 )
1568 drawBitmapOverbar( overbarLength, overbarHeight );
1569
1570 Restore();
1571}
1572
1573
1575{
1578
1580
1581 // sub-pixel lines all render the same
1582 float minorLineWidth = std::fmax( 1.0f,
1584 float majorLineWidth = minorLineWidth * 2.0f;
1585
1586 // Draw the axis and grid
1587 // For the drawing the start points, end points and increments have
1588 // to be calculated in world coordinates
1589 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1591
1592 // Draw axes if desired
1593 if( m_axesEnabled )
1594 {
1595 SetLineWidth( minorLineWidth );
1597
1598 DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1599 DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1600 }
1601
1602 // force flush
1604
1605 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1606 return;
1607
1608 VECTOR2D gridScreenSize( m_gridSize );
1609
1610 double gridThreshold = computeMinGridSpacing() / m_worldScale;
1611
1613 gridThreshold *= 2.0;
1614
1615 // If we cannot display the grid density, scale down by a tick size and
1616 // try again. Eventually, we get some representation of the grid
1617 while( std::min( gridScreenSize.x, gridScreenSize.y ) <= gridThreshold )
1618 {
1619 gridScreenSize = gridScreenSize * static_cast<double>( m_gridTick );
1620 }
1621
1622 // Compute grid starting and ending indexes to draw grid points on the
1623 // visible screen area
1624 // Note: later any point coordinate will be offsetted by m_gridOrigin
1625 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1626 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1627 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1628 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1629
1630 // Ensure start coordinate > end coordinate
1631 SWAP( gridStartX, >, gridEndX );
1632 SWAP( gridStartY, >, gridEndY );
1633
1634 // Ensure the grid fills the screen
1635 --gridStartX;
1636 ++gridEndX;
1637 --gridStartY;
1638 ++gridEndY;
1639
1640 glDisable( GL_DEPTH_TEST );
1641 glDisable( GL_TEXTURE_2D );
1642
1644 {
1645 glEnable( GL_STENCIL_TEST );
1646 glStencilFunc( GL_ALWAYS, 1, 1 );
1647 glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1648 glColor4d( 0.0, 0.0, 0.0, 0.0 );
1649 SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1650 }
1651 else
1652 {
1655 }
1656
1658 {
1659 // Vertical positions
1660 for( int j = gridStartY; j <= gridEndY; j++ )
1661 {
1662 bool tickY = ( j % m_gridTick == 0 );
1663 const double posY = j * gridScreenSize.y + m_gridOrigin.y;
1664
1665 // Horizontal positions
1666 for( int i = gridStartX; i <= gridEndX; i++ )
1667 {
1668 bool tickX = ( i % m_gridTick == 0 );
1669 SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1670 auto lineLen = 2.0 * GetLineWidth();
1671 auto posX = i * gridScreenSize.x + m_gridOrigin.x;
1672
1673 DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1674 DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1675 }
1676 }
1677
1679 }
1680 else
1681 {
1682 // Vertical lines
1683 for( int j = gridStartY; j <= gridEndY; j++ )
1684 {
1685 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1686
1687 // If axes are drawn, skip the lines that would cover them
1688 if( m_axesEnabled && y == 0.0 )
1689 continue;
1690
1691 SetLineWidth( ( j % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1692 VECTOR2D a( gridStartX * gridScreenSize.x + m_gridOrigin.x, y );
1693 VECTOR2D b( gridEndX * gridScreenSize.x + m_gridOrigin.x, y );
1694
1695 DrawLine( a, b );
1696 }
1697
1699
1701 {
1702 glStencilFunc( GL_NOTEQUAL, 0, 1 );
1705 }
1706
1707 // Horizontal lines
1708 for( int i = gridStartX; i <= gridEndX; i++ )
1709 {
1710 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1711
1712 // If axes are drawn, skip the lines that would cover them
1713 if( m_axesEnabled && x == 0.0 )
1714 continue;
1715
1716 SetLineWidth( ( i % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1717 VECTOR2D a( x, gridStartY * gridScreenSize.y + m_gridOrigin.y );
1718 VECTOR2D b( x, gridEndY * gridScreenSize.y + m_gridOrigin.y );
1719 DrawLine( a, b );
1720 }
1721
1723
1725 glDisable( GL_STENCIL_TEST );
1726 }
1727
1728 glEnable( GL_DEPTH_TEST );
1729 glEnable( GL_TEXTURE_2D );
1730}
1731
1732
1733void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1734{
1735 m_screenSize = VECTOR2I( aWidth, aHeight );
1736
1737 // Resize framebuffers
1738 const float scaleFactor = GetScaleFactor();
1739 m_compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1741
1742 wxGLCanvas::SetSize( aWidth, aHeight );
1743}
1744
1745
1746bool OPENGL_GAL::Show( bool aShow )
1747{
1748 bool s = wxGLCanvas::Show( aShow );
1749
1750 if( aShow )
1751 wxGLCanvas::Raise();
1752
1753 return s;
1754}
1755
1756
1758{
1759 glFlush();
1760}
1761
1762
1764{
1765 // Clear screen
1767
1768 // NOTE: Black used here instead of m_clearColor; it will be composited later
1769 glClearColor( 0, 0, 0, 1 );
1770 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1771}
1772
1773
1774void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1775{
1776 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1777
1778 matrixData[0] = aTransformation.m_data[0][0];
1779 matrixData[1] = aTransformation.m_data[1][0];
1780 matrixData[2] = aTransformation.m_data[2][0];
1781 matrixData[4] = aTransformation.m_data[0][1];
1782 matrixData[5] = aTransformation.m_data[1][1];
1783 matrixData[6] = aTransformation.m_data[2][1];
1784 matrixData[12] = aTransformation.m_data[0][2];
1785 matrixData[13] = aTransformation.m_data[1][2];
1786 matrixData[14] = aTransformation.m_data[2][2];
1787
1788 glMultMatrixd( matrixData );
1789}
1790
1791
1792void OPENGL_GAL::Rotate( double aAngle )
1793{
1794 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1795}
1796
1797
1798void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1799{
1800 m_currentManager->Translate( aVector.x, aVector.y, 0.0f );
1801}
1802
1803
1804void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1805{
1806 m_currentManager->Scale( aScale.x, aScale.y, 0.0f );
1807}
1808
1809
1811{
1813}
1814
1815
1817{
1819}
1820
1821
1823{
1824 m_isGrouping = true;
1825
1826 std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *m_cachedManager );
1827 int groupNumber = getNewGroupNumber();
1828 m_groups.insert( std::make_pair( groupNumber, newItem ) );
1829
1830 return groupNumber;
1831}
1832
1833
1835{
1837 m_isGrouping = false;
1838}
1839
1840
1841void OPENGL_GAL::DrawGroup( int aGroupNumber )
1842{
1843 auto group = m_groups.find( aGroupNumber );
1844
1845 if( group != m_groups.end() )
1846 m_cachedManager->DrawItem( *group->second );
1847}
1848
1849
1850void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1851{
1852 auto group = m_groups.find( aGroupNumber );
1853
1854 if( group != m_groups.end() )
1855 m_cachedManager->ChangeItemColor( *group->second, aNewColor );
1856}
1857
1858
1859void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
1860{
1861 auto group = m_groups.find( aGroupNumber );
1862
1863 if( group != m_groups.end() )
1864 m_cachedManager->ChangeItemDepth( *group->second, aDepth );
1865}
1866
1867
1868void OPENGL_GAL::DeleteGroup( int aGroupNumber )
1869{
1870 // Frees memory in the container as well
1871 m_groups.erase( aGroupNumber );
1872}
1873
1874
1876{
1877 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
1878
1879 m_groups.clear();
1880
1881 if( m_isInitialized )
1883}
1884
1885
1887{
1888 switch( aTarget )
1889 {
1890 default:
1895 }
1896
1897 m_currentTarget = aTarget;
1898}
1899
1900
1902{
1903 return m_currentTarget;
1904}
1905
1906
1908{
1909 // Save the current state
1910 unsigned int oldTarget = m_compositor->GetBuffer();
1911
1912 switch( aTarget )
1913 {
1914 // Cached and noncached items are rendered to the same buffer
1915 default:
1916 case TARGET_CACHED:
1917 case TARGET_NONCACHED:
1919 break;
1920
1921 case TARGET_TEMP:
1923 break;
1924
1925 case TARGET_OVERLAY:
1926 if( m_overlayBuffer )
1928 break;
1929 }
1930
1931 if( aTarget != TARGET_OVERLAY )
1933 else if( m_overlayBuffer )
1935
1936 // Restore the previous state
1937 m_compositor->SetBuffer( oldTarget );
1938}
1939
1940
1942{
1943 switch( aTarget )
1944 {
1945 default:
1946 case TARGET_TEMP:
1947 case TARGET_CACHED:
1948 case TARGET_NONCACHED: return true;
1949 case TARGET_OVERLAY: return ( m_overlayBuffer != 0 );
1950 }
1951}
1952
1953
1955{
1959}
1960
1961
1963{
1964 glBlendEquation( GL_MAX );
1966 glBlendEquation( GL_FUNC_ADD );
1967
1969}
1970
1971
1973{
1974 // Store the current cursor type and get the wxCursor for it
1975 if( !GAL::SetNativeCursorStyle( aCursor ) )
1976 return false;
1977
1979
1980 // Update the cursor in the wx control
1981 HIDPI_GL_CANVAS::SetCursor( m_currentwxCursor );
1982
1983 return true;
1984}
1985
1986
1987void OPENGL_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
1988{
1989 aEvent.SetCursor( m_currentwxCursor );
1990}
1991
1992
1993void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
1994{
1995 // Now we should only store the position of the mouse cursor
1996 // The real drawing routines are in blitCursor()
1997 //VECTOR2D screenCursor = m_worldScreenMatrix * aCursorPosition;
1998 //m_cursorPosition = m_screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
1999 m_cursorPosition = aCursorPosition;
2000}
2001
2002
2003void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
2004 const bool aReserve )
2005{
2006 /* Helper drawing: ____--- v3 ^
2007 * ____---- ... \ \
2008 * ____---- ... \ end \
2009 * v1 ____---- ... ____---- \ width
2010 * ---- ...___---- \ \
2011 * \ ___...-- \ v
2012 * \ ____----... ____---- v2
2013 * ---- ... ____----
2014 * start \ ... ____----
2015 * \... ____----
2016 * ----
2017 * v0
2018 * dots mark triangles' hypotenuses
2019 */
2020
2022 * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
2024 * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
2025
2026 VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
2027
2028 if( aReserve )
2029 reserveLineQuads( 1 );
2030
2031 // Line width is maintained by the vertex shader
2033 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2034
2036 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2037
2039 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2040
2042 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2043
2045 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2046
2048 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2049}
2050
2051
2052void OPENGL_GAL::reserveLineQuads( const int aLineCount )
2053{
2054 m_currentManager->Reserve( 6 * aLineCount );
2055}
2056
2057
2058void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2059{
2060 if( m_isFillEnabled )
2061 {
2063 drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
2064 }
2065
2066 if( m_isStrokeEnabled )
2067 {
2069 m_strokeColor.a );
2070 drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
2071 }
2072}
2073
2074
2075void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2076{
2077 Save();
2078
2080 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2081 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2082
2083 /* Draw a triangle that contains the semicircle, then shade it to leave only
2084 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2085 * (if you want to understand more, check the vertex shader source [shader.vert]).
2086 * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
2087 * v2
2088 * /\
2089 * /__\
2090 * v0 //__\\ v1
2091 */
2093 m_currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2094
2096 m_currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2097
2099 m_currentManager->Vertex( 0.0f, aRadius * 2.0f, m_layerDepth ); // v2
2100
2101 Restore();
2102}
2103
2104
2105void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle,
2106 bool aReserve )
2107{
2108 double outerRadius = aRadius + ( m_lineWidth / 2 );
2109
2110 Save();
2111
2112 if( aReserve )
2114
2115 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2116 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2117
2118 /* Draw a triangle that contains the semicircle, then shade it to leave only
2119 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2120 * (if you want to understand more, check the vertex shader source [shader.vert]), the
2121 * radius and the line width. Shader uses these coordinates to determine if fragments are
2122 * inside the semicircle or not.
2123 * v2
2124 * /\
2125 * /__\
2126 * v0 //__\\ v1
2127 */
2129 m_currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2130
2132 m_currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2133
2135 m_currentManager->Vertex( 0.0f, outerRadius * 2.0f, m_layerDepth ); // v2
2136
2137 Restore();
2138}
2139
2140
2141void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
2142{
2143 if( m_isFillEnabled )
2144 {
2147
2148 // Any non convex polygon needs to be tesselated
2149 // for this purpose the GLU standard functions are used
2151 gluTessBeginPolygon( m_tesselator, &params );
2152 gluTessBeginContour( m_tesselator );
2153
2154 GLdouble* point = aPoints;
2155
2156 for( int i = 0; i < aPointCount; ++i )
2157 {
2158 gluTessVertex( m_tesselator, point, point );
2159 point += 3; // 3 coordinates
2160 }
2161
2162 gluTessEndContour( m_tesselator );
2163 gluTessEndPolygon( m_tesselator );
2164
2165 // Free allocated intersecting points
2166 m_tessIntersects.clear();
2167 }
2168
2169 if( m_isStrokeEnabled )
2170 {
2172 [&]( int idx )
2173 {
2174 return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] );
2175 },
2176 aPointCount );
2177 }
2178}
2179
2180
2181void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
2182 bool aReserve )
2183{
2184 wxCHECK( aPointCount > 0, /* return */ );
2185
2187
2188 if( aPointCount == 1 )
2189 {
2190 drawLineQuad( aPointGetter( 0 ), aPointGetter( 0 ), aReserve );
2191 return;
2192 }
2193
2194 if( aReserve )
2195 {
2196 reserveLineQuads( aPointCount - 1 );
2197 }
2198
2199 for( int i = 1; i < aPointCount; ++i )
2200 {
2201 auto start = aPointGetter( i - 1 );
2202 auto end = aPointGetter( i );
2203
2204 drawLineQuad( start, end, false );
2205 }
2206}
2207
2208
2209void OPENGL_GAL::drawSegmentChain( const std::function<VECTOR2D( int )>& aPointGetter,
2210 int aPointCount, double aWidth, bool aReserve )
2211{
2212 wxCHECK( aPointCount >= 2, /* return */ );
2213
2215
2216 int vertices = 0;
2217
2218 for( int i = 1; i < aPointCount; ++i )
2219 {
2220 auto start = aPointGetter( i - 1 );
2221 auto end = aPointGetter( i );
2222
2223 VECTOR2D startEndVector = start - end;
2224 double lineLength = startEndVector.EuclideanNorm();
2225
2226 float startx = start.x;
2227 float starty = end.y;
2228 float endx = start.x + lineLength;
2229 float endy = end.y + lineLength;
2230
2231 // Be careful about floating point rounding. As we draw segments in larger and larger
2232 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
2233 // segments. In this case, we need to draw a circle for the minimal segment.
2234 if( startx == endx || starty == endy )
2235 {
2236 vertices += 3; // One circle
2237 continue;
2238 }
2239
2240 if( m_isFillEnabled || aWidth == 1.0 )
2241 {
2242 vertices += 6; // One line
2243 }
2244 else
2245 {
2246 vertices += 6 + 6 + 3 + 3; // Two lines and two half-circles
2247 }
2248 }
2249
2250 m_currentManager->Reserve( vertices );
2251
2252 for( int i = 1; i < aPointCount; ++i )
2253 {
2254 auto start = aPointGetter( i - 1 );
2255 auto end = aPointGetter( i );
2256
2257 drawSegment( start, end, aWidth, false );
2258 }
2259}
2260
2261
2262int OPENGL_GAL::drawBitmapChar( unsigned long aChar, bool aReserve )
2263{
2264 const float TEX_X = font_image.width;
2265 const float TEX_Y = font_image.height;
2266
2267 // handle space
2268 if( aChar == ' ' )
2269 {
2270 const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
2271 wxCHECK( g, 0 );
2272
2273 // Match stroke font as well as possible
2274 double spaceWidth = g->advance * 0.74;
2275
2276 Translate( VECTOR2D( spaceWidth, 0 ) );
2277 return KiROUND( spaceWidth );
2278 }
2279
2280 const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
2281
2282 // If the glyph is not found (happens for many esoteric unicode chars)
2283 // shows a '?' instead.
2284 if( !glyph )
2285 glyph = LookupGlyph( '?' );
2286
2287 if( !glyph ) // Should not happen.
2288 return 0;
2289
2290 const float X = glyph->atlas_x + font_information.smooth_pixels;
2291 const float Y = glyph->atlas_y + font_information.smooth_pixels;
2292 const float XOFF = glyph->minx;
2293
2294 // adjust for height rounding
2295 const float round_adjust = ( glyph->maxy - glyph->miny )
2296 - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
2297 const float top_adjust = font_information.max_y - glyph->maxy;
2298 const float YOFF = round_adjust + top_adjust;
2299 const float W = glyph->atlas_w - font_information.smooth_pixels * 2;
2300 const float H = glyph->atlas_h - font_information.smooth_pixels * 2;
2301 const float B = 0;
2302
2303 if( aReserve )
2305
2306 Translate( VECTOR2D( XOFF, YOFF ) );
2307
2308 /* Glyph:
2309 * v0 v1
2310 * +--+
2311 * | /|
2312 * |/ |
2313 * +--+
2314 * v2 v3
2315 */
2316 m_currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
2317 m_currentManager->Vertex( -B, -B, 0 ); // v0
2318
2319 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2320 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2321
2322 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2323 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2324
2325
2326 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2327 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2328
2329 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2330 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2331
2332 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
2333 m_currentManager->Vertex( W + B, H + B, 0 ); // v3
2334
2335 Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
2336
2337 return glyph->advance;
2338}
2339
2340
2341void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight, bool aReserve )
2342{
2343 // To draw an overbar, simply draw an overbar
2344 const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
2345 wxCHECK( glyph, /* void */ );
2346
2347 const float H = glyph->maxy - glyph->miny;
2348
2349 Save();
2350
2351 Translate( VECTOR2D( -aLength, -aHeight ) );
2352
2353 if( aReserve )
2355
2357
2359
2360 m_currentManager->Vertex( 0, 0, 0 ); // v0
2361 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2362 m_currentManager->Vertex( 0, H, 0 ); // v2
2363
2364 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2365 m_currentManager->Vertex( 0, H, 0 ); // v2
2366 m_currentManager->Vertex( aLength, H, 0 ); // v3
2367
2368 Restore();
2369}
2370
2371
2372std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
2373{
2374 static const FONT_GLYPH_TYPE* defaultGlyph = LookupGlyph( '(' ); // for strange chars
2375
2376 VECTOR2D textSize( 0, 0 );
2377 float commonOffset = std::numeric_limits<float>::max();
2378 float charHeight = font_information.max_y - defaultGlyph->miny;
2379 int overbarDepth = -1;
2380 int braceNesting = 0;
2381
2382 for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
2383 {
2384 if( *chIt == '~' && overbarDepth == -1 )
2385 {
2386 UTF8::uni_iter lookahead = chIt;
2387
2388 if( ++lookahead != end && *lookahead == '{' )
2389 {
2390 chIt = lookahead;
2391 overbarDepth = braceNesting;
2392 braceNesting++;
2393 continue;
2394 }
2395 }
2396 else if( *chIt == '{' )
2397 {
2398 braceNesting++;
2399 }
2400 else if( *chIt == '}' )
2401 {
2402 if( braceNesting > 0 )
2403 braceNesting--;
2404
2405 if( braceNesting == overbarDepth )
2406 {
2407 overbarDepth = -1;
2408 continue;
2409 }
2410 }
2411
2412 const FONT_GLYPH_TYPE* glyph = LookupGlyph( *chIt );
2413
2414 if( !glyph // Not coded in font
2415 || *chIt == '-' || *chIt == '_' ) // Strange size of these 2 chars
2416 {
2417 glyph = defaultGlyph;
2418 }
2419
2420 if( glyph )
2421 textSize.x += glyph->advance;
2422 }
2423
2424 textSize.y = std::max<float>( textSize.y, charHeight );
2425 commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
2426 textSize.y -= commonOffset;
2427
2428 return std::make_pair( textSize, commonOffset );
2429}
2430
2431
2432void OPENGL_GAL::onPaint( wxPaintEvent& aEvent )
2433{
2434 PostPaint( aEvent );
2435}
2436
2437
2438void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
2439{
2440 // Post the mouse event to the event listener registered in constructor, if any
2441 if( m_mouseListener )
2442 wxPostEvent( m_mouseListener, aEvent );
2443}
2444
2445
2447{
2448 if( !IsCursorEnabled() )
2449 return;
2450
2452
2453 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
2454
2455 VECTOR2D cursorBegin = m_cursorPosition - cursorSize / ( 2 * m_worldScale );
2456 VECTOR2D cursorEnd = m_cursorPosition + cursorSize / ( 2 * m_worldScale );
2457 VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
2458
2459 const COLOR4D cColor = getCursorColor();
2460 const COLOR4D color( cColor.r * cColor.a, cColor.g * cColor.a, cColor.b * cColor.a, 1.0 );
2461
2462 glActiveTexture( GL_TEXTURE0 );
2463 glDisable( GL_TEXTURE_2D );
2464 glLineWidth( 1.0 );
2465 glColor4d( color.r, color.g, color.b, color.a );
2466
2467 glBegin( GL_LINES );
2468 glVertex2d( cursorCenter.x, cursorBegin.y );
2469 glVertex2d( cursorCenter.x, cursorEnd.y );
2470
2471 glVertex2d( cursorBegin.x, cursorCenter.y );
2472 glVertex2d( cursorEnd.x, cursorCenter.y );
2473 glEnd();
2474}
2475
2476
2478{
2479 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
2480 wxT( "There are no free slots to store a group" ) );
2481
2482 while( m_groups.find( m_groupCounter ) != m_groups.end() )
2484
2485 return m_groupCounter++;
2486}
2487
2488
2490{
2491 wxASSERT( IsShownOnScreen() );
2492
2493 wxASSERT_MSG( m_isContextLocked, "This should only be called from within a locked context." );
2494
2495// IsDisplayAttr() handles WX_GL_{MAJOR,MINOR}_VERSION correctly only in 3.0.4
2496// starting with 3.1.0 one should use wxGLContext::IsOk() (done by GL_CONTEXT_MANAGER)
2497#if wxCHECK_VERSION( 3, 0, 3 ) and !wxCHECK_VERSION( 3, 1, 0 )
2498 const int attr[] = { WX_GL_MAJOR_VERSION, 2, WX_GL_MINOR_VERSION, 1, 0 };
2499
2500 if( !IsDisplaySupported( attr ) )
2501 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2502#endif /* wxCHECK_VERSION( 3, 0, 3 ) */
2503
2504 // Check correct initialization from the constructor
2505 if( !m_glMainContext )
2506 throw std::runtime_error( "Could not create the main OpenGL context" );
2507
2508 if( !m_glPrivContext )
2509 throw std::runtime_error( "Could not create a private OpenGL context" );
2510
2511 if( m_tesselator == nullptr )
2512 throw std::runtime_error( "Could not create the m_tesselator" );
2513 // End initialization checks
2514
2515 GLenum err = glewInit();
2516
2517 if( GLEW_OK != err )
2518 throw std::runtime_error( (const char*) glewGetErrorString( err ) );
2519
2520 // Check the OpenGL version (minimum 2.1 is required)
2521 if( !GLEW_VERSION_2_1 )
2522 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2523
2524#if defined( __LINUX__ ) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2525#ifdef DEBUG
2526 if( GLEW_ARB_debug_output )
2527 enableGlDebug( true );
2528#endif
2529#endif
2530
2531 // Framebuffers have to be supported
2532 if( !GLEW_EXT_framebuffer_object )
2533 throw std::runtime_error( "Framebuffer objects are not supported!" );
2534
2535 // Vertex buffer has to be supported
2536 if( !GLEW_ARB_vertex_buffer_object )
2537 throw std::runtime_error( "Vertex buffer objects are not supported!" );
2538
2539 // Prepare shaders
2540 if( !m_shader->IsLinked()
2542 BUILTIN_SHADERS::glsl_kicad_vert ) )
2543 {
2544 throw std::runtime_error( "Cannot compile vertex shader!" );
2545 }
2546
2547 if( !m_shader->IsLinked()
2549 BUILTIN_SHADERS::glsl_kicad_frag ) )
2550 {
2551 throw std::runtime_error( "Cannot compile fragment shader!" );
2552 }
2553
2554 if( !m_shader->IsLinked() && !m_shader->Link() )
2555 throw std::runtime_error( "Cannot link the shaders!" );
2556
2557 // Check if video card supports textures big enough to fit the font atlas
2558 int maxTextureSize;
2559 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2560
2561 if( maxTextureSize < (int) font_image.width || maxTextureSize < (int) font_image.height )
2562 {
2563 // TODO implement software texture scaling
2564 // for bitmap fonts and use a higher resolution texture?
2565 throw std::runtime_error( "Requested texture size is not supported" );
2566 }
2567
2569
2570 m_cachedManager = new VERTEX_MANAGER( true );
2571 m_nonCachedManager = new VERTEX_MANAGER( false );
2572 m_overlayManager = new VERTEX_MANAGER( false );
2573 m_tempManager = new VERTEX_MANAGER( false );
2574
2575 // Make VBOs use shaders
2580
2581 m_isInitialized = true;
2582}
2583
2584
2585// Callback functions for the tesselator. Compare Redbook Chapter 11.
2586void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2587{
2588 GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2589 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2590 VERTEX_MANAGER* vboManager = param->vboManager;
2591
2592 assert( vboManager );
2593 vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2594}
2595
2596
2597void CALLBACK CombineCallback( GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4],
2598 GLdouble** dataOut, void* aData )
2599{
2600 GLdouble* vertex = new GLdouble[3];
2601 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2602
2603 // Save the pointer so we can delete it later
2604 // Note, we use the default_delete for an array because macOS
2605 // decides to bundle an ancient libc++ that mismatches the C++17 support of clang
2606 param->intersectPoints.emplace_back( vertex, std::default_delete<GLdouble[]>() );
2607
2608 memcpy( vertex, coords, 3 * sizeof( GLdouble ) );
2609
2610 *dataOut = vertex;
2611}
2612
2613
2614void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2615{
2616 // This callback is needed to force GLU tesselator to use triangles only
2617}
2618
2619
2620void CALLBACK ErrorCallback( GLenum aErrorCode )
2621{
2622 //throw std::runtime_error( std::string( "Tessellation error: " ) +
2623 //std::string( (const char*) gluErrorString( aErrorCode ) );
2624}
2625
2626
2627static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2628{
2629 gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, (void( CALLBACK* )()) VertexCallback );
2630 gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, (void( CALLBACK* )()) CombineCallback );
2631 gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, (void( CALLBACK* )()) EdgeCallback );
2632 gluTessCallback( aTesselator, GLU_TESS_ERROR, (void( CALLBACK* )()) ErrorCallback );
2633}
2634
2635void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2636{
2637 m_cachedManager->EnableDepthTest( aEnabled );
2639 m_overlayManager->EnableDepthTest( aEnabled );
2640}
2641
2642
2643inline double round_to_half_pixel( double f, double r )
2644{
2645 return ( ceil( f / r ) - 0.5 ) * r;
2646}
2647
2648
2650{
2652 auto pixelSize = m_worldScale;
2653
2654 // we need -m_lookAtPoint == -k * pixelSize + 0.5 * pixelSize for OpenGL
2655 // meaning m_lookAtPoint = (k-0.5)*pixelSize with integer k
2658
2660}
2661
2662
2663void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
2664{
2665 if( aGlyph.IsStroke() )
2666 {
2667 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
2668
2669 DrawPolylines( strokeGlyph );
2670 }
2671 else if( aGlyph.IsOutline() )
2672 {
2673 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
2674
2677
2678 outlineGlyph.Triangulate(
2679 [&]( const VECTOR2D& aPt1, const VECTOR2D& aPt2, const VECTOR2D& aPt3 )
2680 {
2682
2683 m_currentManager->Vertex( aPt1.x, aPt1.y, m_layerDepth );
2684 m_currentManager->Vertex( aPt2.x, aPt2.y, m_layerDepth );
2685 m_currentManager->Vertex( aPt3.x, aPt3.y, m_layerDepth );
2686 } );
2687 }
2688}
2689
2690
2691void OPENGL_GAL::DrawGlyphs( const std::vector<std::unique_ptr<KIFONT::GLYPH>>& aGlyphs )
2692{
2693 if( aGlyphs.empty() )
2694 return;
2695
2696 bool allGlyphsAreStroke = true;
2697 bool allGlyphsAreOutline = true;
2698
2699 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2700 {
2701 if( !glyph->IsStroke() )
2702 {
2703 allGlyphsAreStroke = false;
2704 break;
2705 }
2706 }
2707
2708 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2709 {
2710 if( !glyph->IsOutline() )
2711 {
2712 allGlyphsAreOutline = false;
2713 break;
2714 }
2715 }
2716
2717 if( allGlyphsAreStroke )
2718 {
2719 // Optimized path for stroke fonts that pre-reserves line quads.
2720 int lineQuadCount = 0;
2721
2722 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2723 {
2724 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2725
2726 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2727 lineQuadCount += points.size() - 1;
2728 }
2729
2730 reserveLineQuads( lineQuadCount );
2731
2732 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2733 {
2734 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2735
2736 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2737 {
2739 [&]( int idx )
2740 {
2741 return points[idx];
2742 },
2743 points.size(), false );
2744 }
2745 }
2746
2747 return;
2748 }
2749 else if( allGlyphsAreOutline )
2750 {
2751 // Optimized path for stroke fonts that pre-reserves glyph triangles.
2752 int triangleCount = 0;
2753
2754 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2755 {
2756 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2757
2758 // Only call CacheTriangulation if it has never been done before. Otherwise we'll hash
2759 // the triangulation to see if it has been edited, and glyphs after creation are read-only.
2760 if( outlineGlyph.TriangulatedPolyCount() == 0 )
2761 const_cast<KIFONT::OUTLINE_GLYPH&>( outlineGlyph ).CacheTriangulation( false );
2762
2763 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2764 {
2766 outlineGlyph.TriangulatedPolygon( i );
2767
2768 triangleCount += polygon->GetTriangleCount();
2769 }
2770 }
2771
2774
2775 m_currentManager->Reserve( 3 * triangleCount );
2776
2777 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2778 {
2779 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2780
2781 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2782 {
2784 outlineGlyph.TriangulatedPolygon( i );
2785
2786 for( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
2787 {
2788 VECTOR2I a, b, c;
2789 polygon->GetTriangle( j, a, b, c );
2790
2794 }
2795 }
2796 }
2797 }
2798 else
2799 {
2800 // Regular path
2801 for( size_t i = 0; i < aGlyphs.size(); i++ )
2802 DrawGlyph( *aGlyphs[i], i, aGlyphs.size() );
2803 }
2804}
int color
Definition: DXF_plotter.cpp:57
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Bezier curves to polygon converter.
Definition: bezier_curves.h:36
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMinSegLen=0, int aMaxSegCount=32)
Convert a Bezier curve to a polygon.
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:51
wxSize GetSizePixels() const
Definition: bitmap_base.h:123
int GetPPI() const
Definition: bitmap_base.h:134
wxImage * GetImageData()
Definition: bitmap_base.h:69
static const wxCursor GetCursor(KICURSOR aCursorType)
Definition: cursors.cpp:394
double AsRadians() const
Definition: eda_angle.h:153
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
static GL_CONTEXT_MANAGER & Get()
Return the GL_CONTEXT_MANAGER instance (singleton).
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:42
wxGLCanvas wrapper for HiDPI/Retina support.
void SetScaleFactor(double aFactor)
Set the canvas scale factor, probably for a hi-DPI display.
virtual wxSize GetNativePixelSize() const
double GetScaleFactor() const
Get the current scale factor.
virtual bool IsStroke() const
Definition: glyph.h:52
virtual bool IsOutline() const
Definition: glyph.h:51
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:384
double g
Green component.
Definition: color4d.h:385
double a
Alpha component.
Definition: color4d.h:387
static const COLOR4D BLACK
Definition: color4d.h:394
double b
Blue component.
Definition: color4d.h:386
OPENGL_ANTIALIASING_MODE gl_antialiasing_mode
Abstract interface for drawing on a 2D-surface.
bool IsTextMirrored() const
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.
double m_layerDepth
The actual layer depth.
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 coordinates.
GR_TEXT_H_ALIGN_T GetHorizontalJustify() const
VECTOR2D m_depthRange
Range of the depth.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
GRID_STYLE m_gridStyle
Grid display style.
COLOR4D m_axesColor
Color of the axes.
const MATRIX3x3D & GetScreenWorldMatrix() const
Get the screen <-> world transformation matrix.
float m_lineWidth
The line width.
void computeWorldScale()
Compute the scaling factor for the world->screen matrix.
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.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
VECTOR2D m_cursorPosition
Current cursor position (world coordinates)
const VECTOR2I & GetGlyphSize() const
int m_gridTick
Every tick line gets the double width.
double m_worldScale
The scale factor world->screen.
virtual bool SetNativeCursorStyle(KICURSOR aCursor)
Set the cursor in the native panel.
VECTOR2D m_gridOrigin
The grid origin.
KICURSOR m_currentNativeCursor
Current cursor.
bool m_globalFlipY
Flag for Y axis flipping.
bool m_fullscreenCursor
Shape of the cursor (fullscreen or small cross)
bool m_isFillEnabled
Is filling of graphic objects enabled ?
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
COLOR4D m_gridColor
Color of the grid.
COLOR4D m_strokeColor
The color of the outlines.
double computeMinGridSpacing() const
compute minimum grid spacing from the grid settings
bool m_isStrokeEnabled
Are the outlines stroked ?
GAL_DISPLAY_OPTIONS & m_options
bool m_gridVisibility
Should the grid be shown.
virtual void BitmapText(const wxString &aText, const VECTOR2I &aPosition, const EDA_ANGLE &aAngle)
Draw a text using a bitmap font.
float GetLineWidth() const
Get the line width.
bool m_globalFlipX
Flag for X axis flipping.
GR_TEXT_V_ALIGN_T GetVerticalJustify() const
VECTOR2D m_lookAtPoint
Point to be looked at in world space.
GLuint cacheBitmap(const BITMAP_BASE *aBitmap)
Definition: opengl_gal.cpp:156
std::map< const BITMAP_BASE *, CACHED_BITMAP > m_bitmaps
Definition: opengl_gal.cpp:111
GLuint RequestBitmap(const BITMAP_BASE *aBitmap)
Definition: opengl_gal.cpp:124
VECTOR2D GetAntialiasRenderingOffset() const
virtual void Begin() override
Call this at the beginning of each frame.
static const unsigned int DIRECT_RENDERING
virtual unsigned int GetBuffer() const override
Return currently used buffer handle.
OPENGL_ANTIALIASING_MODE GetAntialiasingMode() const
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
virtual void Present() override
Call this to present the output buffer to the screen.
virtual void Initialize() override
Perform primary initialization, necessary to use the object.
virtual void ClearBuffer(const COLOR4D &aColor) override
Clear the selected buffer (set by the SetBuffer() function).
virtual void Resize(unsigned int aWidth, unsigned int aHeight) override
Clear the state of COMPOSITOR, so it has to be reinitialized again with the new dimensions.
virtual void DrawBuffer(unsigned int aBufferHandle) override
Draw the selected buffer to the output buffer.
int GetAntialiasSupersamplingFactor() const
virtual void SetBuffer(unsigned int aBufferHandle) override
Set the selected buffer as the rendering target.
virtual unsigned int CreateBuffer() override
Prepare a new buffer that may be used as a rendering target.
OpenGL implementation of the Graphics Abstraction Layer.
Definition: opengl_gal.h:70
void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
void drawPolygon(GLdouble *aPoints, int aPointCount)
Draw a filled polygon.
void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Change the depth (Z-axis position) of the group.
void skipMouseEvent(wxMouseEvent &aEvent)
Skip the mouse event to the parent.
void drawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth, bool aReserve=true)
Internal method for segment drawing.
Definition: opengl_gal.cpp:727
unsigned int m_groupCounter
Counter used for generating keys for groups.
Definition: opengl_gal.h:340
void EndDiffLayer() override
Ends rendering of a differential layer.
VERTEX_MANAGER * m_overlayManager
Container for storing overlaid VERTEX_ITEMs.
Definition: opengl_gal.h:345
void Scale(const VECTOR2D &aScale) override
Scale the context.
bool m_isInitialized
Basic initialization flag, has to be done when the window is visible.
Definition: opengl_gal.h:363
VERTEX_MANAGER * m_currentManager
Currently used VERTEX_MANAGER (for storing VERTEX_ITEMs).
Definition: opengl_gal.h:341
void drawCircle(const VECTOR2D &aCenterPoint, double aRadius, bool aReserve=true)
Internal method for circle drawing.
Definition: opengl_gal.cpp:791
std::deque< std::shared_ptr< GLdouble > > m_tessIntersects
Definition: opengl_gal.h:379
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: opengl_gal.cpp:785
void LockContext(int aClientCookie) override
Use GAL_CONTEXT_LOCKER RAII object unless you know what you're doing.
Definition: opengl_gal.cpp:663
unsigned int m_mainBuffer
Main rendering target.
Definition: opengl_gal.h:350
std::unique_ptr< GL_BITMAP_CACHE > m_bitmapCache
Definition: opengl_gal.h:375
std::pair< VECTOR2D, float > computeBitmapTextSize(const UTF8 &aText) const
Compute a size of text drawn using bitmap font with current text setting applied.
void SetTarget(RENDER_TARGET aTarget) override
Set the target for rendering.
void blitCursor()
Blit cursor into the current screen.
static wxString CheckFeatures(GAL_DISPLAY_OPTIONS &aOptions)
Checks OpenGL features.
Definition: opengl_gal.cpp:360
void ClearTarget(RENDER_TARGET aTarget) override
Clear the target for rendering.
void drawBitmapOverbar(double aLength, double aHeight, bool aReserve=true)
Draw an overbar over the currently drawn text.
void EndGroup() override
End the group.
bool m_isBitmapFontInitialized
Is the shader set to use bitmap fonts?
Definition: opengl_gal.h:362
void drawSegmentChain(const std::function< VECTOR2D(int)> &aPointGetter, int aPointCount, double aWidth, bool aReserve=true)
Generic way of drawing a chain of segments stored in different containers.
void BitmapText(const wxString &aText, const VECTOR2I &aPosition, const EDA_ANGLE &aAngle) override
Draw a text using a bitmap font.
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Handle updating display options.
Definition: opengl_gal.cpp:400
unsigned int m_overlayBuffer
Auxiliary rendering target (for menus etc.)
Definition: opengl_gal.h:351
void PostPaint(wxPaintEvent &aEvent)
Function PostPaint posts an event to m_paint_listener.
Definition: opengl_gal.cpp:392
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle) override
Draw an arc.
Definition: opengl_gal.cpp:852
OPENGL_COMPOSITOR * m_compositor
Handles multiple rendering targets.
Definition: opengl_gal.h:349
VERTEX_MANAGER * m_cachedManager
Container for storing cached VERTEX_ITEMs.
Definition: opengl_gal.h:343
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
void DrawPolyline(const std::deque< VECTOR2D > &aPointList) override
Draw a polyline.
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 drawFilledSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a filled semicircle.
void onPaint(wxPaintEvent &aEvent)
This is the OnPaint event handler.
void DrawGroup(int aGroupNumber) override
Draw the stored group.
void Restore() override
Restore the context.
GLint ufm_worldPixelSize
Definition: opengl_gal.h:368
void DrawGrid() override
void DrawSegmentChain(const std::vector< VECTOR2D > &aPointList, double aWidth) override
Draw a chain of rounded segments.
void drawStrokedSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle, bool aReserve=true)
Draw a stroked semicircle.
unsigned int getNewGroupNumber()
Return a valid key that can be used as a new group number.
void Flush() override
Force all remaining objects to be drawn.
GLint ufm_antialiasingOffset
Definition: opengl_gal.h:371
void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
bool IsVisible() const override
Return true if the GAL canvas is visible on the screen.
Definition: opengl_gal.h:109
void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aEndAngle, double aWidth, double aMaxError) override
Draw an arc segment.
Definition: opengl_gal.cpp:924
wxEvtHandler * m_mouseListener
Definition: opengl_gal.h:331
void ClearCache() override
Delete all data created during caching of graphic items.
double getWorldPixelSize() const
Definition: opengl_gal.cpp:429
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: opengl_gal.cpp:720
GROUPS_MAP m_groups
Stores information about VBO objects (groups)
Definition: opengl_gal.h:339
void endUpdate() override
Definition: opengl_gal.cpp:703
void ClearScreen() override
Clear the screen.
void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
void drawTriangulatedPolyset(const SHAPE_POLY_SET &aPoly, bool aStrokeTriangulation)
Draw a set of polygons with a cached triangulation.
void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
VERTEX_MANAGER * m_nonCachedManager
Container for storing non-cached VERTEX_ITEMs.
Definition: opengl_gal.h:344
int drawBitmapChar(unsigned long aChar, bool aReserve=true)
Draw a single character using bitmap font.
GLUtesselator * m_tesselator
Definition: opengl_gal.h:378
wxEvtHandler * m_paintListener
Definition: opengl_gal.h:332
void StartDiffLayer() override
Begins rendering of a differential layer.
bool m_isContextLocked
Used for assertion checking.
Definition: opengl_gal.h:366
void Save() override
Save the context.
wxCursor m_currentwxCursor
wxCursor showing the current native cursor
Definition: opengl_gal.h:373
bool m_isFramebufferInitialized
Are the framebuffers initialized?
Definition: opengl_gal.h:360
void ComputeWorldScreenMatrix() override
Compute the world <-> screen transformation matrix.
bool Show(bool aShow) override
Shows/hides the GAL canvas.
static GLuint g_fontTexture
Bitmap font texture handle (shared)
Definition: opengl_gal.h:334
virtual bool HasTarget(RENDER_TARGET aTarget) override
Return true if the target exists.
void reserveLineQuads(const int aLineCount)
Reserves specified number of line quads.
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: opengl_gal.cpp:712
void beginUpdate() override
Definition: opengl_gal.cpp:687
double calcAngleStep(double aRadius) const
Compute the angle step when drawing arcs/circles approximated with lines.
Definition: opengl_gal.h:563
virtual void DrawGlyph(const KIFONT::GLYPH &aGlyph, int aNth, int aTotal) override
Draw a polygon representing a font glyph.
bool m_isGrouping
Was a group started?
Definition: opengl_gal.h:365
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: opengl_gal.cpp:444
GLint ufm_screenPixelSize
Definition: opengl_gal.h:369
void Rotate(double aAngle) override
Rotate the context.
int BeginGroup() override
Begin a group.
GLint ufm_pixelSizeMultiplier
Definition: opengl_gal.h:370
OPENGL_GAL(GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("GLCanvas"))
Definition: opengl_gal.cpp:214
VECTOR2D getScreenPixelSize() const
Definition: opengl_gal.cpp:436
bool SetNativeCursorStyle(KICURSOR aCursor) override
Set the cursor in the native panel.
void DrawBitmap(const BITMAP_BASE &aBitmap, double alphaBlend=1.0) override
Draw a bitmap image.
void drawPolyline(const std::function< VECTOR2D(int)> &aPointGetter, int aPointCount, bool aReserve=true)
Generic way of drawing a polyline stored in different containers.
RENDER_TARGET GetTarget() const override
Get the currently used target for rendering.
void UnlockContext(int aClientCookie) override
Definition: opengl_gal.cpp:673
void drawSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a semicircle.
void drawLineQuad(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, bool aReserve=true)
Draw a quad for the line.
VERTEX_MANAGER * m_tempManager
Container for storing temp (diff mode) VERTEX_ITEMs.
Definition: opengl_gal.h:346
virtual void DrawGlyphs(const std::vector< std::unique_ptr< KIFONT::GLYPH > > &aGlyphs) override
Draw polygons representing font glyphs.
void onSetNativeCursor(wxSetCursorEvent &aEvent)
Give the correct cursor image when the native widget asks for it.
void EnableDepthTest(bool aEnabled=false) override
void EndDrawing() override
End the drawing, needs to be called for every new frame.
Definition: opengl_gal.cpp:605
SHADER * m_shader
There is only one shader used for different objects.
Definition: opengl_gal.h:356
void DrawPolylines(const std::vector< std::vector< VECTOR2D > > &aPointLists) override
Draw multiple polylines.
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: opengl_gal.h:353
wxGLContext * m_glPrivContext
Canvas-specific OpenGL context.
Definition: opengl_gal.h:329
void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Change the color used to draw the group.
static bool m_isBitmapFontLoaded
Is the bitmap font texture loaded?
Definition: opengl_gal.h:361
static wxGLContext * m_glMainContext
Parent OpenGL context.
Definition: opengl_gal.h:328
unsigned int m_tempBuffer
Temporary rendering target (for diffing etc.)
Definition: opengl_gal.h:352
void init()
Basic OpenGL initialization and feature checks.
static int m_instanceCounter
GL GAL instance counter.
Definition: opengl_gal.h:330
Provide the access to the OpenGL shaders.
Definition: shader.h:77
bool IsLinked() const
Return true if shaders are linked correctly.
Definition: shader.h:118
void SetParameter(int aParameterNumber, float aValue) const
Set a parameter of the shader.
Definition: shader.cpp:143
bool Link()
Link the shaders.
Definition: shader.cpp:101
void Use()
Use the shader.
Definition: shader.h:126
bool LoadShaderFromStrings(SHADER_TYPE aShaderType, Args &&... aArgs)
Add a shader and compile the shader sources.
Definition: shader.h:93
int AddParameter(const std::string &aParameterName)
Add a parameter to the parameter queue.
Definition: shader.cpp:130
void Deactivate()
Deactivate the shader and use the default OpenGL program.
Definition: shader.h:135
Class to control vertex container and GPU with possibility of emulating old-style OpenGL 1....
void EndDrawing() const
Finish drawing operations.
bool Vertex(const VERTEX &aVertex)
Add a vertex with the given coordinates to the currently set item.
const glm::mat4 & GetTransformation() const
void Map()
Map vertex buffer.
void Clear() const
Remove all the stored vertices from the container.
void BeginDrawing() const
Prepare buffers and items to start drawing.
void ChangeItemColor(const VERTEX_ITEM &aItem, const COLOR4D &aColor) const
Change the color of all vertices owned by an item.
void Color(const COLOR4D &aColor)
Changes currently used color that will be applied to newly added vertices.
bool Reserve(unsigned int aSize)
Allocate space for vertices, so it will be used with subsequent Vertex() calls.
void FinishItem() const
Clean after adding an item.
void ChangeItemDepth(const VERTEX_ITEM &aItem, GLfloat aDepth) const
Change the depth of all vertices owned by an item.
void Rotate(GLfloat aAngle, GLfloat aX, GLfloat aY, GLfloat aZ)
Multiply the current matrix by a rotation matrix, so the newly vertices will be rotated by the given ...
void EnableDepthTest(bool aEnabled)
Enable/disable Z buffer depth test.
void PopMatrix()
Pop the current transformation matrix stack.
void Shader(GLfloat aShaderType, GLfloat aParam1=0.0f, GLfloat aParam2=0.0f, GLfloat aParam3=0.0f)
Change currently used shader and its parameters that will be applied to newly added vertices.
void PushMatrix()
Push the current transformation matrix stack.
void Scale(GLfloat aX, GLfloat aY, GLfloat aZ)
Multiply the current matrix by a scaling matrix, so the newly vertices will be scaled by the given fa...
void Unmap()
Unmap vertex buffer.
void SetShader(SHADER &aShader) const
Set a shader program that is going to be used during rendering.
void Translate(GLfloat aX, GLfloat aY, GLfloat aZ)
Multiply the current matrix by a translation matrix, so newly vertices will be translated by the give...
void DrawItem(const VERTEX_ITEM &aItem) const
Draw an item to the buffer.
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition: matrix3x3.h:265
T m_data[3][3]
Definition: matrix3x3.h:64
A small class to help profiling.
Definition: profile.h:47
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition: profile.h:86
void Start()
Start or restart the counter.
Definition: profile.h:75
std::string to_string()
Definition: profile.h:153
double msecs(bool aSinceLast=false)
Definition: profile.h:147
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.
int SegmentCount() const
Return the number of segments in this line chain.
void GetTriangle(int index, VECTOR2I &a, VECTOR2I &b, VECTOR2I &c) const
Represent a set of closed polygons.
bool IsTriangulationUpToDate() const
POLYGON & Polygon(int aIndex)
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
unsigned int TriangulatedPolyCount() const
Return the number of outlines in the set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
uni_iter is a non-mutating iterator that walks through unicode code points in the UTF8 encoded string...
Definition: utf8.h:204
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:71
uni_iter uend() const
Return a uni_iter initialized to the end of "this" UTF8 byte sequence.
Definition: utf8.h:287
uni_iter ubegin() const
Returns a uni_iter initialized to the start of "this" UTF8 byte sequence.
Definition: utf8.h:279
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
@ BLUE
Definition: color4d.h:56
KICURSOR
Definition: cursors.h:34
#define SWAP(varA, condition, varB)
Swap the variables if a condition is met.
Definition: definitions.h:31
static constexpr EDA_ANGLE & FULL_CIRCLE
Definition: eda_angle.h:410
E_SERIE r
Definition: eserie.cpp:41
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
const wxChar *const traceGalProfile
Flag to enable debug output of GAL performance profiling.
This file contains miscellaneous commonly used macros and functions.
#define H(x, y, z)
Definition: md5_hash.cpp:17
FONT_IMAGE_TYPE font_image
const FONT_GLYPH_TYPE * LookupGlyph(unsigned int aCodepoint)
FONT_INFO_TYPE font_information
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:266
@ SMALL_CROSS
Use small cross instead of dots for the grid.
@ DOTS
Use dots for the grid.
@ SHADER_NONE
Definition: vertex_common.h:47
@ SHADER_LINE_C
Definition: vertex_common.h:53
@ SHADER_LINE_B
Definition: vertex_common.h:52
@ SHADER_FONT
Definition: vertex_common.h:50
@ SHADER_LINE_F
Definition: vertex_common.h:56
@ SHADER_LINE_E
Definition: vertex_common.h:55
@ SHADER_STROKED_CIRCLE
Definition: vertex_common.h:49
@ SHADER_LINE_A
Definition: vertex_common.h:51
@ SHADER_LINE_D
Definition: vertex_common.h:54
@ SHADER_FILLED_CIRCLE
Definition: vertex_common.h:48
@ SHADER_TYPE_VERTEX
Vertex shader.
Definition: shader.h:46
@ SHADER_TYPE_FRAGMENT
Fragment shader.
Definition: shader.h:47
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:47
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition: definitions.h:49
@ TARGET_TEMP
Temporary target for drawing in separate layer.
Definition: definitions.h:51
@ TARGET_CACHED
Main rendering target (cached)
Definition: definitions.h:48
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:50
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
static void InitTesselatorCallbacks(GLUtesselator *aTesselator)
void CALLBACK CombineCallback(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **dataOut, void *aData)
void CALLBACK VertexCallback(GLvoid *aVertexPtr, void *aData)
void CALLBACK EdgeCallback(GLboolean aEdgeFlag)
void CALLBACK ErrorCallback(GLenum aErrorCode)
double round_to_half_pixel(double f, double r)
static const int glAttributes[]
Definition: opengl_gal.cpp:84
#define SEG_PER_CIRCLE_COUNT
Definition: opengl_gal.h:52
#define CALLBACK
The default number of points for circle approximation.
Definition: opengl_gal.h:48
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
#define X()
const int scale
unsigned char pixels[1024 *1024 *3]
Definition: gl_resources.h:38
VERTEX_MANAGER * vboManager
Manager used for storing new vertices.
Definition: opengl_gal.h:318
std::deque< std::shared_ptr< GLdouble > > & intersectPoints
Intersect points, that have to be freed after tessellation.
Definition: opengl_gal.h:321
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
v1
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
wxLogTrace helper definitions.
#define KI_TRACE(aWhat,...)
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
void enableGlDebug(bool aEnable)
Enable or disable OpenGL driver messages output.
Definition: utils.cpp:183
int checkGlError(const std::string &aInfo, const char *aFile, int aLine, bool aThrow)
Check if a recent OpenGL operation has failed.
Definition: utils.cpp:45
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618