KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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>
36#include <build_version.h>
38#include <gal/opengl/utils.h>
39#include <gal/definitions.h>
42#include <math/vector2wx.h>
43#include <bitmap_base.h>
44#include <bezier_curves.h>
45#include <math/util.h> // for KiROUND
46#include <pgm_base.h>
47#include <trace_helpers.h>
48
49#include <wx/frame.h>
50
51#include <macros.h>
53#include <thread_pool.h>
54
55#include <core/profile.h>
56#include <trace_helpers.h>
57
58#include <gal/opengl/gl_utils.h>
59
60#include <functional>
61#include <limits>
62#include <memory>
63#include <list>
64using namespace std::placeholders;
65using namespace KIGFX;
66
67//#define DISABLE_BITMAP_CACHE
68
69// The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
70// (see ubuntu-font-licence-1.0.txt for details)
71#include "gl_resources.h"
72#include <glsl_kicad_frag.h>
73#include <glsl_kicad_vert.h>
74using namespace KIGFX::BUILTIN_FONT;
75
76static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
77
78static wxGLAttributes getGLAttribs()
79{
80 wxGLAttributes attribs;
81 attribs.RGBA().DoubleBuffer().Depth( 8 ).EndList();
82
83 return attribs;
84}
85
86wxGLContext* OPENGL_GAL::m_glMainContext = nullptr;
90
91namespace KIGFX
92{
94{
95public:
97 m_cacheSize( 0 )
98 {}
99
101
102 GLuint RequestBitmap( const BITMAP_BASE* aBitmap );
103
104private:
106 {
107 GLuint id;
108 int w, h;
109 size_t size;
110 long long int accessTime;
111 };
112
113 GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
114
115 const size_t m_cacheMaxElements = 50;
116 const size_t m_cacheMaxSize = 256 * 1024 * 1024;
117
118 std::map<const KIID, CACHED_BITMAP> m_bitmaps;
119 std::list<KIID> m_cacheLru;
121 std::list<GLuint> m_freedTextureIds;
122};
123
124}; // namespace KIGFX
125
126
128{
129 for( auto& bitmap : m_bitmaps )
130 glDeleteTextures( 1, &bitmap.second.id );
131}
132
133
135{
136#ifndef DISABLE_BITMAP_CACHE
137 auto it = m_bitmaps.find( aBitmap->GetImageID() );
138
139 if( it != m_bitmaps.end() )
140 {
141 // A bitmap is found in cache bitmap. Ensure the associated texture is still valid.
142 if( glIsTexture( it->second.id ) )
143 {
144 it->second.accessTime = wxGetUTCTimeMillis().GetValue();
145 return it->second.id;
146 }
147 else
148 {
149 // Delete the invalid bitmap cache and its data
150 glDeleteTextures( 1, &it->second.id );
151 m_freedTextureIds.emplace_back( it->second.id );
152
153 auto listIt = std::find( m_cacheLru.begin(), m_cacheLru.end(), it->first );
154
155 if( listIt != m_cacheLru.end() )
156 m_cacheLru.erase( listIt );
157
158 m_cacheSize -= it->second.size;
159
160 m_bitmaps.erase( it );
161 }
162
163 // the cached bitmap is not valid and deleted, it will be recreated.
164 }
165
166#endif
167 return cacheBitmap( aBitmap );
168}
169
170
172{
173 CACHED_BITMAP bmp;
174
175 const wxImage* imgPtr = aBitmap->GetOriginalImageData();
176
177 if( !imgPtr )
178 return std::numeric_limits< GLuint >::max();
179
180 const wxImage& imgData = *imgPtr;
181
182 bmp.w = imgData.GetSize().x;
183 bmp.h = imgData.GetSize().y;
184
185 GLuint textureID;
186
187 if( m_freedTextureIds.empty() )
188 {
189 glGenTextures( 1, &textureID );
190 }
191 else
192 {
193 textureID = m_freedTextureIds.front();
194 m_freedTextureIds.pop_front();
195 }
196
197 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
198
199 if( imgData.HasAlpha() || imgData.HasMask() )
200 {
201 bmp.size = bmp.w * bmp.h * 4;
202 auto buf = std::make_unique<uint8_t[]>( bmp.size );
203
204 uint8_t* dstP = buf.get();
205 uint8_t* srcP = imgData.GetData();
206
207 long long pxCount = static_cast<long long>( bmp.w ) * bmp.h;
208
209 if( imgData.HasAlpha() )
210 {
211 uint8_t* srcAlpha = imgData.GetAlpha();
212
213 for( long long px = 0; px < pxCount; px++ )
214 {
215 memcpy( dstP, srcP, 3 );
216 dstP[3] = *srcAlpha;
217
218 srcAlpha += 1;
219 srcP += 3;
220 dstP += 4;
221 }
222 }
223 else if( imgData.HasMask() )
224 {
225 uint8_t maskRed = imgData.GetMaskRed();
226 uint8_t maskGreen = imgData.GetMaskGreen();
227 uint8_t maskBlue = imgData.GetMaskBlue();
228
229 for( long long px = 0; px < pxCount; px++ )
230 {
231 memcpy( dstP, srcP, 3 );
232
233 if( srcP[0] == maskRed && srcP[1] == maskGreen && srcP[2] == maskBlue )
234 dstP[3] = wxALPHA_TRANSPARENT;
235 else
236 dstP[3] = wxALPHA_OPAQUE;
237
238 srcP += 3;
239 dstP += 4;
240 }
241 }
242
243 glBindTexture( GL_TEXTURE_2D, textureID );
244 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
245 buf.get() );
246 }
247 else
248 {
249 bmp.size = bmp.w * bmp.h * 3;
250
251 uint8_t* srcP = imgData.GetData();
252
253 glBindTexture( GL_TEXTURE_2D, textureID );
254 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, bmp.w, bmp.h, 0, GL_RGB, GL_UNSIGNED_BYTE, srcP );
255 }
256
257 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
258 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
259
260 long long currentTime = wxGetUTCTimeMillis().GetValue();
261
262 bmp.id = textureID;
263 bmp.accessTime = currentTime;
264
265#ifndef DISABLE_BITMAP_CACHE
266 if( ( m_cacheLru.size() + 1 > m_cacheMaxElements || m_cacheSize + bmp.size > m_cacheMaxSize )
267 && !m_cacheLru.empty() )
268 {
269 KIID toRemove( 0 );
270 auto toRemoveLru = m_cacheLru.end();
271
272 // Remove entries accessed > 1s ago first
273 for( const auto& [kiid, cachedBmp] : m_bitmaps )
274 {
275 const int cacheTimeoutMillis = 1000L;
276
277 if( currentTime - cachedBmp.accessTime > cacheTimeoutMillis )
278 {
279 toRemove = kiid;
280 toRemoveLru = std::find( m_cacheLru.begin(), m_cacheLru.end(), toRemove );
281 break;
282 }
283 }
284
285 // Otherwise, remove the latest entry (it's less likely to be needed soon)
286 if( toRemove == niluuid )
287 {
288 toRemoveLru = m_cacheLru.end();
289 toRemoveLru--;
290
291 toRemove = *toRemoveLru;
292 }
293
294 CACHED_BITMAP& cachedBitmap = m_bitmaps[toRemove];
295
296 m_cacheSize -= cachedBitmap.size;
297 glDeleteTextures( 1, &cachedBitmap.id );
298 m_freedTextureIds.emplace_back( cachedBitmap.id );
299
300 m_bitmaps.erase( toRemove );
301 m_cacheLru.erase( toRemoveLru );
302 }
303
304 m_cacheLru.emplace_back( aBitmap->GetImageID() );
305 m_cacheSize += bmp.size;
306 m_bitmaps.emplace( aBitmap->GetImageID(), std::move( bmp ) );
307#endif
308
309 return textureID;
310}
311
312
314 wxWindow* aParent,
315 wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
316 const wxString& aName ) :
317 GAL( aDisplayOptions ),
318 HIDPI_GL_CANVAS( aVcSettings, aParent, getGLAttribs(), wxID_ANY, wxDefaultPosition,
319 wxDefaultSize,
320 wxEXPAND, aName ),
321 m_mouseListener( aMouseListener ),
322 m_paintListener( aPaintListener ),
323 m_currentManager( nullptr ),
324 m_cachedManager( nullptr ),
325 m_nonCachedManager( nullptr ),
326 m_overlayManager( nullptr ),
327 m_tempManager( nullptr ),
328 m_mainBuffer( 0 ),
329 m_overlayBuffer( 0 ),
330 m_tempBuffer( 0 ),
331 m_isContextLocked( false ),
332 m_lockClientCookie( 0 )
333{
334 if( m_glMainContext == nullptr )
335 {
337
338 if( !m_glMainContext )
339 throw std::runtime_error( "Could not create the main OpenGL context" );
340
342 }
343 else
344 {
346
347 if( !m_glPrivContext )
348 throw std::runtime_error( "Could not create a private OpenGL context" );
349 }
350
351 m_shader = new SHADER();
353
354 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
355
358
359 // Initialize the flags
362 m_isInitialized = false;
363 m_isGrouping = false;
364 m_groupCounter = 0;
365
366 // Connect the native cursor handler
367 Connect( wxEVT_SET_CURSOR, wxSetCursorEventHandler( OPENGL_GAL::onSetNativeCursor ), nullptr,
368 this );
369
370 // Connecting the event handlers
371 Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) );
372
373 // Mouse events are skipped to the parent
374 Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
375 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
376 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
377 Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
378 Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
379 Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
380 Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
381 Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
382 Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
383 Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
384 Connect( wxEVT_AUX1_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
385 Connect( wxEVT_AUX1_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
386 Connect( wxEVT_AUX1_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
387 Connect( wxEVT_AUX2_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
388 Connect( wxEVT_AUX2_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
389 Connect( wxEVT_AUX2_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
390 Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
391 Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
392
393#if defined _WIN32 || defined _WIN64
394 Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
395#endif
396
397 Bind( wxEVT_GESTURE_ZOOM, &OPENGL_GAL::skipGestureEvent, this );
398 Bind( wxEVT_GESTURE_PAN, &OPENGL_GAL::skipGestureEvent, this );
399
400 SetSize( aParent->GetClientSize() );
402
403 // Grid color settings are different in Cairo and OpenGL
404 SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
406
407 // Tesselator initialization
408 m_tesselator = gluNewTess();
410
411 gluTessProperty( m_tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
412
414
415 // Avoid uninitialized variables:
420 m_swapInterval = 0;
421}
422
423
425{
426
428 gl_mgr->LockCtx( m_glPrivContext, this );
429
431 glFlush();
432 gluDeleteTess( m_tesselator );
433 ClearCache();
434
435 delete m_compositor;
436
437 if( m_isInitialized )
438 {
439 delete m_cachedManager;
440 delete m_nonCachedManager;
441 delete m_overlayManager;
442 delete m_tempManager;
443 }
444
445 gl_mgr->UnlockCtx( m_glPrivContext );
446
447 // If it was the main context, then it will be deleted
448 // when the last OpenGL GAL instance is destroyed (a few lines below)
450 gl_mgr->DestroyCtx( m_glPrivContext );
451
452 delete m_shader;
453
454 // Are we destroying the last GAL instance?
455 if( m_instanceCounter == 0 )
456 {
457 gl_mgr->LockCtx( m_glMainContext, this );
458
460 {
461 glDeleteTextures( 1, &g_fontTexture );
462 m_isBitmapFontLoaded = false;
463 }
464
465 gl_mgr->UnlockCtx( m_glMainContext );
466 gl_mgr->DestroyCtx( m_glMainContext );
467 m_glMainContext = nullptr;
468 }
469}
470
471
473{
474 wxString retVal = wxEmptyString;
475
476 wxFrame* testFrame = new wxFrame( nullptr, wxID_ANY, wxT( "" ), wxDefaultPosition,
477 wxSize( 1, 1 ), wxFRAME_TOOL_WINDOW | wxNO_BORDER );
478
479 KIGFX::OPENGL_GAL* opengl_gal = nullptr;
480
481 try
482 {
484 opengl_gal = new KIGFX::OPENGL_GAL( dummy, aOptions, testFrame );
485
486 testFrame->Raise();
487 testFrame->Show();
488
489 GAL_CONTEXT_LOCKER lock( opengl_gal );
490 opengl_gal->init();
491 }
492 catch( std::runtime_error& err )
493 {
494 //Test failed
495 retVal = wxString( err.what() );
496 }
497
498 delete opengl_gal;
499 delete testFrame;
500
501 return retVal;
502}
503
504
505void OPENGL_GAL::PostPaint( wxPaintEvent& aEvent )
506{
507 // posts an event to m_paint_listener to ask for redraw the canvas.
508 if( m_paintListener )
509 wxPostEvent( m_paintListener, aEvent );
510}
511
512
514{
515 GAL_CONTEXT_LOCKER lock( this );
516
517 bool refresh = false;
518
520 {
523 refresh = true;
524 }
525
526 if( super::updatedGalDisplayOptions( aOptions ) || refresh )
527 {
528 Refresh();
529 refresh = true;
530 }
531
532 return refresh;
533}
534
535
537{
539 return std::min( std::abs( matrix.GetScale().x ), std::abs( matrix.GetScale().y ) );
540}
541
542
544{
545 double sf = GetScaleFactor();
546 return VECTOR2D( 2.0 / (double) ( m_screenSize.x * sf ), 2.0 /
547 (double) ( m_screenSize.y * sf ) );
548}
549
550
552{
553#ifdef KICAD_GAL_PROFILE
554 PROF_TIMER totalRealTime( "OPENGL_GAL::beginDrawing()", true );
555#endif /* KICAD_GAL_PROFILE */
556
557 wxASSERT_MSG( m_isContextLocked, "GAL_DRAWING_CONTEXT RAII object should have locked context. "
558 "Calling GAL::beginDrawing() directly is not allowed." );
559
560 wxASSERT_MSG( IsVisible(), "GAL::beginDrawing() must not be entered when GAL is not visible. "
561 "Other drawing routines will expect everything to be initialized "
562 "which will not be the case." );
563
564 if( !m_isInitialized )
565 init();
566
567 // Set up the view port
568 glMatrixMode( GL_PROJECTION );
569 glLoadIdentity();
570
571 // Create the screen transformation (Do the RH-LH conversion here)
572 glOrtho( 0, (GLint) m_screenSize.x, (GLsizei) m_screenSize.y, 0,
574
576 {
577 // Prepare rendering target buffers
580 try
581 {
583 }
584 catch( const std::runtime_error& )
585 {
586 wxLogVerbose( "Could not create a framebuffer for diff mode blending.\n" );
587 m_tempBuffer = 0;
588 }
589 try
590 {
592 }
593 catch( const std::runtime_error& )
594 {
595 wxLogVerbose( "Could not create a framebuffer for overlays.\n" );
596 m_overlayBuffer = 0;
597 }
598
600 }
601
603
604 // Disable 2D Textures
605 glDisable( GL_TEXTURE_2D );
606
607 glShadeModel( GL_FLAT );
608
609 // Enable the depth buffer
610 glEnable( GL_DEPTH_TEST );
611 glDepthFunc( GL_LESS );
612
613 // Setup blending, required for transparent objects
614 glEnable( GL_BLEND );
615 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
616
617 glMatrixMode( GL_MODELVIEW );
618
619 // Set up the world <-> screen transformation
621 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
622 matrixData[0] = m_worldScreenMatrix.m_data[0][0];
623 matrixData[1] = m_worldScreenMatrix.m_data[1][0];
624 matrixData[2] = m_worldScreenMatrix.m_data[2][0];
625 matrixData[4] = m_worldScreenMatrix.m_data[0][1];
626 matrixData[5] = m_worldScreenMatrix.m_data[1][1];
627 matrixData[6] = m_worldScreenMatrix.m_data[2][1];
628 matrixData[12] = m_worldScreenMatrix.m_data[0][2];
629 matrixData[13] = m_worldScreenMatrix.m_data[1][2];
630 matrixData[14] = m_worldScreenMatrix.m_data[2][2];
631 glLoadMatrixd( matrixData );
632
633 // Set defaults
636
637 // Remove all previously stored items
641
646
648 {
649 // Keep bitmap font texture always bound to the second texturing unit
650 const GLint FONT_TEXTURE_UNIT = 2;
651
652 // Either load the font atlas to video memory, or simply bind it to a texture unit
654 {
655 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
656 glGenTextures( 1, &g_fontTexture );
657 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
658 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, font_image.width, font_image.height, 0, GL_RGB,
659 GL_UNSIGNED_BYTE, font_image.pixels );
660 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
661 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
662 checkGlError( "loading bitmap font", __FILE__, __LINE__ );
663
664 glActiveTexture( GL_TEXTURE0 );
665
667 }
668 else
669 {
670 glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
671 glBindTexture( GL_TEXTURE_2D, g_fontTexture );
672 glActiveTexture( GL_TEXTURE0 );
673 }
674
675 // Set shader parameter
676 GLint ufm_fontTexture = m_shader->AddParameter( "u_fontTexture" );
677 GLint ufm_fontTextureWidth = m_shader->AddParameter( "u_fontTextureWidth" );
678 ufm_worldPixelSize = m_shader->AddParameter( "u_worldPixelSize" );
679 ufm_screenPixelSize = m_shader->AddParameter( "u_screenPixelSize" );
680 ufm_pixelSizeMultiplier = m_shader->AddParameter( "u_pixelSizeMultiplier" );
681 ufm_antialiasingOffset = m_shader->AddParameter( "u_antialiasingOffset" );
682
683 m_shader->Use();
684 m_shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT );
685 m_shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width );
687 checkGlError( "setting bitmap font sampler as shader parameter", __FILE__, __LINE__ );
688
690 }
691
692 m_shader->Use();
694 (float) ( getWorldPixelSize() / GetScaleFactor() ) );
695 const VECTOR2D& screenPixelSize = getScreenPixelSize();
696 m_shader->SetParameter( ufm_screenPixelSize, screenPixelSize );
697 double pixelSizeMultiplier = m_compositor->GetAntialiasSupersamplingFactor();
698 m_shader->SetParameter( ufm_pixelSizeMultiplier, (float) pixelSizeMultiplier );
700 renderingOffset.x *= screenPixelSize.x;
701 renderingOffset.y *= screenPixelSize.y;
702 m_shader->SetParameter( ufm_antialiasingOffset, renderingOffset );
704
705 // Something between BeginDrawing and EndDrawing seems to depend on
706 // this texture unit being active, but it does not assure it itself.
707 glActiveTexture( GL_TEXTURE0 );
708
709 // Unbind buffers - set compositor for direct drawing
711
712#ifdef KICAD_GAL_PROFILE
713 totalRealTime.Stop();
714 wxLogTrace( traceGalProfile, wxT( "OPENGL_GAL::beginDrawing(): %.1f ms" ),
715 totalRealTime.msecs() );
716#endif /* KICAD_GAL_PROFILE */
717}
718
719
721{
722 wxASSERT_MSG( m_isContextLocked, "What happened to the context lock?" );
723
724 PROF_TIMER cntTotal( "gl-end-total" );
725 PROF_TIMER cntEndCached( "gl-end-cached" );
726 PROF_TIMER cntEndNoncached( "gl-end-noncached" );
727 PROF_TIMER cntEndOverlay( "gl-end-overlay" );
728 PROF_TIMER cntComposite( "gl-composite" );
729 PROF_TIMER cntSwap( "gl-swap" );
730
731 cntTotal.Start();
732
733 // Cached & non-cached containers are rendered to the same buffer
735
736 cntEndNoncached.Start();
738 cntEndNoncached.Stop();
739
740 cntEndCached.Start();
742 cntEndCached.Stop();
743
744 cntEndOverlay.Start();
745 // Overlay container is rendered to a different buffer
746 if( m_overlayBuffer )
748
750 cntEndOverlay.Stop();
751
752 cntComposite.Start();
753
754 // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
755 glColor4d( 1.0, 1.0, 1.0, 1.0 );
756
757 // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
759
760 if( m_overlayBuffer )
762
764 blitCursor();
765
766 cntComposite.Stop();
767
768 cntSwap.Start();
769 SwapBuffers();
770 cntSwap.Stop();
771
772 cntTotal.Stop();
773
774 KI_TRACE( traceGalProfile, "Timing: %s %s %s %s %s %s\n", cntTotal.to_string(),
775 cntEndCached.to_string(), cntEndNoncached.to_string(), cntEndOverlay.to_string(),
776 cntComposite.to_string(), cntSwap.to_string() );
777}
778
779
780void OPENGL_GAL::LockContext( int aClientCookie )
781{
782 wxASSERT_MSG( !m_isContextLocked, "Context already locked." );
783 m_isContextLocked = true;
784 m_lockClientCookie = aClientCookie;
785
787}
788
789
790void OPENGL_GAL::UnlockContext( int aClientCookie )
791{
792 wxASSERT_MSG( m_isContextLocked, "Context not locked. A GAL_CONTEXT_LOCKER RAII object must "
793 "be stacked rather than making separate lock/unlock calls." );
794
795 wxASSERT_MSG( m_lockClientCookie == aClientCookie,
796 "Context was locked by a different client. "
797 "Should not be possible with RAII objects." );
798
799 m_isContextLocked = false;
800
802}
803
804
806{
807 wxASSERT_MSG( m_isContextLocked, "GAL_UPDATE_CONTEXT RAII object should have locked context. "
808 "Calling this from anywhere else is not allowed." );
809
810 wxASSERT_MSG( IsVisible(), "GAL::beginUpdate() must not be entered when GAL is not visible. "
811 "Other update routines will expect everything to be initialized "
812 "which will not be the case." );
813
814 if( !m_isInitialized )
815 init();
816
818}
819
820
822{
823 if( !m_isInitialized )
824 return;
825
827}
828
829
830void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
831{
833
834 drawLineQuad( aStartPoint, aEndPoint );
835}
836
837
838void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
839 double aWidth )
840{
841 drawSegment( aStartPoint, aEndPoint, aWidth );
842}
843
844
845void OPENGL_GAL::drawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth,
846 bool aReserve )
847{
848 VECTOR2D startEndVector = aEndPoint - aStartPoint;
849 double lineLength = startEndVector.EuclideanNorm();
850
851 float startx = aStartPoint.x;
852 float starty = aStartPoint.y;
853 float endx = aStartPoint.x + lineLength;
854 float endy = aStartPoint.y + lineLength;
855
856 // Be careful about floating point rounding. As we draw segments in larger and larger
857 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
858 // segments. In this case, we need to draw a circle for the minimal segment.
859 if( startx == endx || starty == endy )
860 {
861 drawCircle( aStartPoint, aWidth / 2, aReserve );
862 return;
863 }
864
865 if( m_isFillEnabled || aWidth == 1.0 )
866 {
868
869 SetLineWidth( aWidth );
870 drawLineQuad( aStartPoint, aEndPoint, aReserve );
871 }
872 else
873 {
874 EDA_ANGLE lineAngle( startEndVector );
875
876 // Outlined tracks
877 SetLineWidth( 1.0 );
880
881 Save();
882
883 if( aReserve )
884 m_currentManager->Reserve( 6 + 6 + 3 + 3 ); // Two line quads and two semicircles
885
886 m_currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
887 m_currentManager->Rotate( lineAngle.AsRadians(), 0.0f, 0.0f, 1.0f );
888
889 drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ), VECTOR2D( lineLength, aWidth / 2.0 ), false );
890
891 drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ), VECTOR2D( lineLength, -aWidth / 2.0 ),
892 false );
893
894 // Draw line caps
895 drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2, false );
896 drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2, false );
897
898 Restore();
899 }
900}
901
902
903void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
904{
905 drawCircle( aCenterPoint, aRadius );
906}
907
908
909void OPENGL_GAL::drawCircle( const VECTOR2D& aCenterPoint, double aRadius, bool aReserve )
910{
911 if( m_isFillEnabled )
912 {
913 if( aReserve )
915
917
918 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
919 * Parameters given to Shader() are indices of the triangle's vertices
920 * (if you want to understand more, check the vertex shader source [shader.vert]).
921 * Shader uses this coordinates to determine if fragments are inside the circle or not.
922 * Does the calculations in the vertex shader now (pixel alignment)
923 * v2
924 * /\
925 * //\\
926 * v0 /_\/_\ v1
927 */
929 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
930
932 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
933
935 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth );
936 }
937
939 {
940 if( aReserve )
942
945
946 /* Draw a triangle that contains the circle, then shade it leaving only the circle.
947 * Parameters given to Shader() are indices of the triangle's vertices
948 * (if you want to understand more, check the vertex shader source [shader.vert]).
949 * and the line width. Shader uses this coordinates to determine if fragments are
950 * inside the circle or not.
951 * v2
952 * /\
953 * //\\
954 * v0 /_\/_\ v1
955 */
957 m_currentManager->Vertex( aCenterPoint.x, // v0
958 aCenterPoint.y, m_layerDepth );
959
961 m_currentManager->Vertex( aCenterPoint.x, // v1
962 aCenterPoint.y, m_layerDepth );
963
965 m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, // v2
966 m_layerDepth );
967 }
968}
969
970
971void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius,
972 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle )
973{
974 if( aRadius <= 0 )
975 return;
976
977 double startAngle = aStartAngle.AsRadians();
978 double endAngle = startAngle + aAngle.AsRadians();
979
980 // Normalize arc angles
981 normalize( startAngle, endAngle );
982
983 const double alphaIncrement = calcAngleStep( aRadius );
984
985 Save();
986 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
987
988 if( m_isFillEnabled )
989 {
990 double alpha;
993
994 // Triangle fan
995 for( alpha = startAngle; ( alpha + alphaIncrement ) < endAngle; )
996 {
999 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
1000 m_layerDepth );
1001 alpha += alphaIncrement;
1002 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius,
1003 m_layerDepth );
1004 }
1005
1006 // The last missing triangle
1007 const VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1008
1010 m_currentManager->Vertex( 0.0, 0.0, m_layerDepth );
1011 m_currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, m_layerDepth );
1012 m_currentManager->Vertex( endPoint.x, endPoint.y, m_layerDepth );
1013 }
1014
1015 if( m_isStrokeEnabled )
1016 {
1018 m_strokeColor.a );
1019
1020 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1021 double alpha;
1022 unsigned int lineCount = 0;
1023
1024 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1025 lineCount++;
1026
1027 if( alpha != endAngle )
1028 lineCount++;
1029
1030 reserveLineQuads( lineCount );
1031
1032 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1033 {
1034 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
1035 drawLineQuad( p, p_next, false );
1036
1037 p = p_next;
1038 }
1039
1040 // Draw the last missing part
1041 if( alpha != endAngle )
1042 {
1043 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1044 drawLineQuad( p, p_last, false );
1045 }
1046 }
1047
1048 Restore();
1049}
1050
1051
1052void OPENGL_GAL::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius,
1053 const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle,
1054 double aWidth, double aMaxError )
1055{
1056 if( aRadius <= 0 )
1057 {
1058 // Arcs of zero radius are a circle of aWidth diameter
1059 if( aWidth > 0 )
1060 DrawCircle( aCenterPoint, aWidth / 2.0 );
1061
1062 return;
1063 }
1064
1065 double startAngle = aStartAngle.AsRadians();
1066 double endAngle = startAngle + aAngle.AsRadians();
1067
1068 // Swap the angles, if start angle is greater than end angle
1069 normalize( startAngle, endAngle );
1070
1071 // Calculate the seg count to approximate the arc with aMaxError or less
1072 int segCount360 = GetArcToSegmentCount( aRadius, aMaxError, FULL_CIRCLE );
1073 segCount360 = std::max( SEG_PER_CIRCLE_COUNT, segCount360 );
1074 double alphaIncrement = 2.0 * M_PI / segCount360;
1075
1076 // Refinement: Use a segment count multiple of 2, because we have a control point
1077 // on the middle of the arc, and the look is better if it is on a segment junction
1078 // because there is no approx error
1079 int seg_count = KiROUND( ( endAngle - startAngle ) / alphaIncrement );
1080
1081 if( seg_count % 2 != 0 )
1082 seg_count += 1;
1083
1084 // Our shaders have trouble rendering null line quads, so delegate this task to DrawSegment.
1085 if( seg_count == 0 )
1086 {
1087 VECTOR2D p_start( aCenterPoint.x + cos( startAngle ) * aRadius,
1088 aCenterPoint.y + sin( startAngle ) * aRadius );
1089
1090 VECTOR2D p_end( aCenterPoint.x + cos( endAngle ) * aRadius,
1091 aCenterPoint.y + sin( endAngle ) * aRadius );
1092
1093 DrawSegment( p_start, p_end, aWidth );
1094 return;
1095 }
1096
1097 // Recalculate alphaIncrement with a even integer number of segment
1098 alphaIncrement = ( endAngle - startAngle ) / seg_count;
1099
1100 Save();
1101 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
1102
1103 if( m_isStrokeEnabled )
1104 {
1106 m_strokeColor.a );
1107
1108 double width = aWidth / 2.0;
1109 VECTOR2D startPoint( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1110 VECTOR2D endPoint( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1111
1112 drawStrokedSemiCircle( startPoint, width, startAngle + M_PI );
1113 drawStrokedSemiCircle( endPoint, width, endAngle );
1114
1115 VECTOR2D pOuter( cos( startAngle ) * ( aRadius + width ),
1116 sin( startAngle ) * ( aRadius + width ) );
1117
1118 VECTOR2D pInner( cos( startAngle ) * ( aRadius - width ),
1119 sin( startAngle ) * ( aRadius - width ) );
1120
1121 double alpha;
1122
1123 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1124 {
1125 VECTOR2D pNextOuter( cos( alpha ) * ( aRadius + width ),
1126 sin( alpha ) * ( aRadius + width ) );
1127 VECTOR2D pNextInner( cos( alpha ) * ( aRadius - width ),
1128 sin( alpha ) * ( aRadius - width ) );
1129
1130 DrawLine( pOuter, pNextOuter );
1131 DrawLine( pInner, pNextInner );
1132
1133 pOuter = pNextOuter;
1134 pInner = pNextInner;
1135 }
1136
1137 // Draw the last missing part
1138 if( alpha != endAngle )
1139 {
1140 VECTOR2D pLastOuter( cos( endAngle ) * ( aRadius + width ),
1141 sin( endAngle ) * ( aRadius + width ) );
1142 VECTOR2D pLastInner( cos( endAngle ) * ( aRadius - width ),
1143 sin( endAngle ) * ( aRadius - width ) );
1144
1145 DrawLine( pOuter, pLastOuter );
1146 DrawLine( pInner, pLastInner );
1147 }
1148 }
1149
1150 if( m_isFillEnabled )
1151 {
1153 SetLineWidth( aWidth );
1154
1155 VECTOR2D p( cos( startAngle ) * aRadius, sin( startAngle ) * aRadius );
1156 double alpha;
1157
1158 int lineCount = 0;
1159
1160 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1161 {
1162 lineCount++;
1163 }
1164
1165 // The last missing part
1166 if( alpha != endAngle )
1167 {
1168 lineCount++;
1169 }
1170
1171 reserveLineQuads( lineCount );
1172
1173 for( alpha = startAngle + alphaIncrement; alpha <= endAngle; alpha += alphaIncrement )
1174 {
1175 VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
1176 drawLineQuad( p, p_next, false );
1177
1178 p = p_next;
1179 }
1180
1181 // Draw the last missing part
1182 if( alpha != endAngle )
1183 {
1184 VECTOR2D p_last( cos( endAngle ) * aRadius, sin( endAngle ) * aRadius );
1185 drawLineQuad( p, p_last, false );
1186 }
1187 }
1188
1189 Restore();
1190}
1191
1192
1193void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1194{
1195 // Compute the diagonal points of the rectangle
1196 VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
1197 VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
1198
1199 // Fill the rectangle
1200 if( m_isFillEnabled )
1201 {
1205
1206 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1207 m_currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, m_layerDepth );
1208 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1209
1210 m_currentManager->Vertex( aStartPoint.x, aStartPoint.y, m_layerDepth );
1211 m_currentManager->Vertex( aEndPoint.x, aEndPoint.y, m_layerDepth );
1212 m_currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, m_layerDepth );
1213 }
1214
1215 // Stroke the outline
1216 if( m_isStrokeEnabled )
1217 {
1219 m_strokeColor.a );
1220
1221 // DrawLine (and DrawPolyline )
1222 // has problem with 0 length lines so enforce minimum
1223 if( aStartPoint == aEndPoint )
1224 {
1225 DrawLine( aStartPoint + VECTOR2D( 1.0, 0.0 ), aEndPoint );
1226 }
1227 else
1228 {
1229 std::deque<VECTOR2D> pointList;
1230
1231 pointList.push_back( aStartPoint );
1232 pointList.push_back( diagonalPointA );
1233 pointList.push_back( aEndPoint );
1234 pointList.push_back( diagonalPointB );
1235 pointList.push_back( aStartPoint );
1236 DrawPolyline( pointList );
1237 }
1238 }
1239}
1240
1241
1242void OPENGL_GAL::DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth )
1243{
1245 [&]( int idx )
1246 {
1247 return aPointList[idx];
1248 },
1249 aPointList.size(), aWidth );
1250}
1251
1252
1253void OPENGL_GAL::DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth )
1254{
1255 auto numPoints = aLineChain.PointCount();
1256
1257 if( aLineChain.IsClosed() )
1258 numPoints += 1;
1259
1261 [&]( int idx )
1262 {
1263 return aLineChain.CPoint( idx );
1264 },
1265 numPoints, aWidth );
1266}
1267
1268
1269void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
1270{
1272 [&]( int idx )
1273 {
1274 return aPointList[idx];
1275 },
1276 aPointList.size() );
1277}
1278
1279
1280void OPENGL_GAL::DrawPolyline( const std::vector<VECTOR2D>& aPointList )
1281{
1283 [&]( int idx )
1284 {
1285 return aPointList[idx];
1286 },
1287 aPointList.size() );
1288}
1289
1290
1291void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
1292{
1294 [&]( int idx )
1295 {
1296 return aPointList[idx];
1297 },
1298 aListSize );
1299}
1300
1301
1303{
1304 auto numPoints = aLineChain.PointCount();
1305
1306 if( aLineChain.IsClosed() )
1307 numPoints += 1;
1308
1310 [&]( int idx )
1311 {
1312 return aLineChain.CPoint( idx );
1313 },
1314 numPoints );
1315}
1316
1317
1318void OPENGL_GAL::DrawPolylines( const std::vector<std::vector<VECTOR2D>>& aPointList )
1319{
1320 int lineQuadCount = 0;
1321
1322 for( const std::vector<VECTOR2D>& points : aPointList )
1323 lineQuadCount += points.size() - 1;
1324
1325 reserveLineQuads( lineQuadCount );
1326
1327 for( const std::vector<VECTOR2D>& points : aPointList )
1328 {
1330 [&]( int idx )
1331 {
1332 return points[idx];
1333 },
1334 points.size(), false );
1335 }
1336}
1337
1338
1339void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
1340{
1341 wxCHECK( aPointList.size() >= 2, /* void */ );
1342 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aPointList.size()] );
1343 GLdouble* ptr = points.get();
1344
1345 for( const VECTOR2D& p : aPointList )
1346 {
1347 *ptr++ = p.x;
1348 *ptr++ = p.y;
1349 *ptr++ = m_layerDepth;
1350 }
1351
1352 drawPolygon( points.get(), aPointList.size() );
1353}
1354
1355
1356void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
1357{
1358 wxCHECK( aListSize >= 2, /* void */ );
1359 auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aListSize] );
1360 GLdouble* target = points.get();
1361 const VECTOR2D* src = aPointList;
1362
1363 for( int i = 0; i < aListSize; ++i )
1364 {
1365 *target++ = src->x;
1366 *target++ = src->y;
1367 *target++ = m_layerDepth;
1368 ++src;
1369 }
1370
1371 drawPolygon( points.get(), aListSize );
1372}
1373
1374
1376 bool aStrokeTriangulation )
1377{
1380
1381 if( m_isFillEnabled )
1382 {
1383 int totalTriangleCount = 0;
1384
1385 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1386 {
1387 auto triPoly = aPolySet.TriangulatedPolygon( j );
1388
1389 totalTriangleCount += triPoly->GetTriangleCount();
1390 }
1391
1392 m_currentManager->Reserve( 3 * totalTriangleCount );
1393
1394 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1395 {
1396 auto triPoly = aPolySet.TriangulatedPolygon( j );
1397
1398 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1399 {
1400 VECTOR2I a, b, c;
1401 triPoly->GetTriangle( i, a, b, c );
1405 }
1406 }
1407 }
1408
1409 if( m_isStrokeEnabled )
1410 {
1411 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1412 {
1413 const auto& poly = aPolySet.Polygon( j );
1414
1415 for( const auto& lc : poly )
1416 {
1417 DrawPolyline( lc );
1418 }
1419 }
1420 }
1421
1422 if( ADVANCED_CFG::GetCfg().m_DrawTriangulationOutlines )
1423 {
1424 aStrokeTriangulation = true;
1425 SetStrokeColor( COLOR4D( 0.0, 1.0, 0.2, 1.0 ) );
1426 }
1427
1428 if( aStrokeTriangulation )
1429 {
1432
1433 for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
1434 {
1435 auto triPoly = aPolySet.TriangulatedPolygon( j );
1436
1437 for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
1438 {
1439 VECTOR2I a, b, c;
1440 triPoly->GetTriangle( i, a, b, c );
1441 DrawLine( a, b );
1442 DrawLine( b, c );
1443 DrawLine( c, a );
1444 }
1445 }
1446 }
1447}
1448
1449
1450void OPENGL_GAL::DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation )
1451{
1452 if( aPolySet.IsTriangulationUpToDate() )
1453 {
1454 drawTriangulatedPolyset( aPolySet, aStrokeTriangulation );
1455 return;
1456 }
1457
1458 for( int j = 0; j < aPolySet.OutlineCount(); ++j )
1459 {
1460 const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
1461 DrawPolygon( outline );
1462 }
1463}
1464
1465
1467{
1468 wxCHECK( aPolygon.PointCount() >= 2, /* void */ );
1469
1470 const int pointCount = aPolygon.SegmentCount() + 1;
1471 std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1472 GLdouble* ptr = points.get();
1473
1474 for( int i = 0; i < pointCount; ++i )
1475 {
1476 const VECTOR2I& p = aPolygon.CPoint( i );
1477 *ptr++ = p.x;
1478 *ptr++ = p.y;
1479 *ptr++ = m_layerDepth;
1480 }
1481
1482 drawPolygon( points.get(), pointCount );
1483}
1484
1485
1486void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1487 const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint,
1488 double aFilterValue )
1489{
1490 std::vector<VECTOR2D> output;
1491 std::vector<VECTOR2D> pointCtrl;
1492
1493 pointCtrl.push_back( aStartPoint );
1494 pointCtrl.push_back( aControlPointA );
1495 pointCtrl.push_back( aControlPointB );
1496 pointCtrl.push_back( aEndPoint );
1497
1498 BEZIER_POLY converter( pointCtrl );
1499 converter.GetPoly( output, aFilterValue );
1500
1501 if( output.size() == 1 )
1502 output.push_back( output.front() );
1503
1504 DrawPolygon( &output[0], output.size() );
1505}
1506
1507
1508void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
1509{
1510 GLfloat alpha = std::clamp( alphaBlend, 0.0, 1.0 );
1511
1512 // We have to calculate the pixel size in users units to draw the image.
1513 // m_worldUnitLength is a factor used for converting IU to inches
1514 double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
1515 double w = (double) aBitmap.GetSizePixels().x * scale;
1516 double h = (double) aBitmap.GetSizePixels().y * scale;
1517
1518 auto xform = m_currentManager->GetTransformation();
1519
1520 glm::vec4 v0 = xform * glm::vec4( -w / 2, -h / 2, 0.0, 0.0 );
1521 glm::vec4 v1 = xform * glm::vec4( w / 2, h / 2, 0.0, 0.0 );
1522 glm::vec4 trans = xform[3];
1523
1524 auto texture_id = m_bitmapCache->RequestBitmap( &aBitmap );
1525
1526 if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1527 return;
1528
1529 glDepthFunc( GL_ALWAYS );
1530
1531 glAlphaFunc( GL_GREATER, 0.01f );
1532 glEnable( GL_ALPHA_TEST );
1533
1534 glMatrixMode( GL_TEXTURE );
1535 glPushMatrix();
1536 glTranslated( 0.5, 0.5, 0.5 );
1537 glRotated( aBitmap.Rotation().AsDegrees(), 0, 0, 1 );
1538 glTranslated( -0.5, -0.5, -0.5 );
1539
1540 glMatrixMode( GL_MODELVIEW );
1541 glPushMatrix();
1542 glTranslated( trans.x, trans.y, trans.z );
1543
1544 glEnable( GL_TEXTURE_2D );
1545 glActiveTexture( GL_TEXTURE0 );
1546 glBindTexture( GL_TEXTURE_2D, texture_id );
1547
1548 float texStartX = aBitmap.IsMirroredX() ? 1.0 : 0.0;
1549 float texEndX = aBitmap.IsMirroredX() ? 0.0 : 1.0;
1550 float texStartY = aBitmap.IsMirroredY() ? 1.0 : 0.0;
1551 float texEndY = aBitmap.IsMirroredY() ? 0.0 : 1.0;
1552
1553 glBegin( GL_QUADS );
1554 glColor4f( 1.0, 1.0, 1.0, alpha );
1555 glTexCoord2f( texStartX, texStartY );
1556 glVertex3f( v0.x, v0.y, m_layerDepth );
1557 glColor4f( 1.0, 1.0, 1.0, alpha );
1558 glTexCoord2f( texEndX, texStartY);
1559 glVertex3f( v1.x, v0.y, m_layerDepth );
1560 glColor4f( 1.0, 1.0, 1.0, alpha );
1561 glTexCoord2f( texEndX, texEndY);
1562 glVertex3f( v1.x, v1.y, m_layerDepth );
1563 glColor4f( 1.0, 1.0, 1.0, alpha );
1564 glTexCoord2f( texStartX, texEndY);
1565 glVertex3f( v0.x, v1.y, m_layerDepth );
1566 glEnd();
1567
1568 glBindTexture( GL_TEXTURE_2D, 0 );
1569
1570#ifdef DISABLE_BITMAP_CACHE
1571 glDeleteTextures( 1, &texture_id );
1572#endif
1573
1574 glPopMatrix();
1575
1576 glMatrixMode( GL_TEXTURE );
1577 glPopMatrix();
1578 glMatrixMode( GL_MODELVIEW );
1579
1580 glDisable( GL_ALPHA_TEST );
1581
1582 glDepthFunc( GL_LESS );
1583}
1584
1585
1586void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2I& aPosition,
1587 const EDA_ANGLE& aAngle )
1588{
1589 // Fallback to generic impl (which uses the stroke font) on cases we don't handle
1590 if( IsTextMirrored()
1591 || aText.Contains( wxT( "^{" ) )
1592 || aText.Contains( wxT( "_{" ) )
1593 || aText.Contains( wxT( "\n" ) ) )
1594 {
1595 return GAL::BitmapText( aText, aPosition, aAngle );
1596 }
1597
1598 const UTF8 text( aText );
1599 VECTOR2D textSize;
1600 float commonOffset;
1601 std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1602
1603 const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1604 double overbarHeight = textSize.y;
1605
1606 Save();
1607
1609 m_currentManager->Translate( aPosition.x, aPosition.y, m_layerDepth );
1610 m_currentManager->Rotate( aAngle.AsRadians(), 0.0f, 0.0f, -1.0f );
1611
1612 double sx = SCALE * ( m_globalFlipX ? -1.0 : 1.0 );
1613 double sy = SCALE * ( m_globalFlipY ? -1.0 : 1.0 );
1614
1615 m_currentManager->Scale( sx, sy, 0 );
1616 m_currentManager->Translate( 0, -commonOffset, 0 );
1617
1618 switch( GetHorizontalJustify() )
1619 {
1621 Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1622 break;
1623
1625 //if( !IsTextMirrored() )
1626 Translate( VECTOR2D( -textSize.x, 0 ) );
1627 break;
1628
1630 //if( IsTextMirrored() )
1631 //Translate( VECTOR2D( -textSize.x, 0 ) );
1632 break;
1633
1635 wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
1636 break;
1637 }
1638
1639 switch( GetVerticalJustify() )
1640 {
1642 break;
1643
1645 Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1646 overbarHeight = 0;
1647 break;
1648
1650 Translate( VECTOR2D( 0, -textSize.y ) );
1651 overbarHeight = -textSize.y / 2.0;
1652 break;
1653
1655 wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
1656 break;
1657 }
1658
1659 int overbarLength = 0;
1660 int overbarDepth = -1;
1661 int braceNesting = 0;
1662
1663 auto iterateString =
1664 [&]( const std::function<void( int aOverbarLength, int aOverbarHeight )>& overbarFn,
1665 const std::function<int( unsigned long aChar )>& bitmapCharFn )
1666 {
1667 for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1668 {
1669 wxASSERT_MSG( *chIt != '\n' && *chIt != '\r',
1670 "No support for multiline bitmap text yet" );
1671
1672 if( *chIt == '~' && overbarDepth == -1 )
1673 {
1674 UTF8::uni_iter lookahead = chIt;
1675
1676 if( ++lookahead != end && *lookahead == '{' )
1677 {
1678 chIt = lookahead;
1679 overbarDepth = braceNesting;
1680 braceNesting++;
1681 continue;
1682 }
1683 }
1684 else if( *chIt == '{' )
1685 {
1686 braceNesting++;
1687 }
1688 else if( *chIt == '}' )
1689 {
1690 if( braceNesting > 0 )
1691 braceNesting--;
1692
1693 if( braceNesting == overbarDepth )
1694 {
1695 overbarFn( overbarLength, overbarHeight );
1696 overbarLength = 0;
1697
1698 overbarDepth = -1;
1699 continue;
1700 }
1701 }
1702
1703 if( overbarDepth != -1 )
1704 overbarLength += bitmapCharFn( *chIt );
1705 else
1706 bitmapCharFn( *chIt );
1707 }
1708 };
1709
1710 // First, calculate the amount of characters and overbars to reserve
1711
1712 int charsCount = 0;
1713 int overbarsCount = 0;
1714
1715 iterateString(
1716 [&overbarsCount]( int aOverbarLength, int aOverbarHeight )
1717 {
1718 overbarsCount++;
1719 },
1720 [&charsCount]( unsigned long aChar ) -> int
1721 {
1722 if( aChar != ' ' )
1723 charsCount++;
1724
1725 return 0;
1726 } );
1727
1728 m_currentManager->Reserve( 6 * charsCount + 6 * overbarsCount );
1729
1730 // Now reset the state and actually draw the characters and overbars
1731 overbarLength = 0;
1732 overbarDepth = -1;
1733 braceNesting = 0;
1734
1735 iterateString(
1736 [&]( int aOverbarLength, int aOverbarHeight )
1737 {
1738 drawBitmapOverbar( aOverbarLength, aOverbarHeight, false );
1739 },
1740 [&]( unsigned long aChar ) -> int
1741 {
1742 return drawBitmapChar( aChar, false );
1743 } );
1744
1745 // Handle the case when overbar is active till the end of the drawn text
1746 m_currentManager->Translate( 0, commonOffset, 0 );
1747
1748 if( overbarDepth != -1 && overbarLength > 0 )
1749 drawBitmapOverbar( overbarLength, overbarHeight );
1750
1751 Restore();
1752}
1753
1754
1756{
1759
1761
1762 // sub-pixel lines all render the same
1763 float minorLineWidth = std::fmax( 1.0f,
1765 float majorLineWidth = minorLineWidth * 2.0f;
1766
1767 // Draw the axis and grid
1768 // For the drawing the start points, end points and increments have
1769 // to be calculated in world coordinates
1770 VECTOR2D worldStartPoint = m_screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1772
1773 // Draw axes if desired
1774 if( m_axesEnabled )
1775 {
1776 SetLineWidth( minorLineWidth );
1778
1779 DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1780 DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1781 }
1782
1783 // force flush
1785
1786 if( !m_gridVisibility || m_gridSize.x == 0 || m_gridSize.y == 0 )
1787 return;
1788
1789 VECTOR2D gridScreenSize = GetVisibleGridSize();
1790
1791 // Compute grid starting and ending indexes to draw grid points on the
1792 // visible screen area
1793 // Note: later any point coordinate will be offset by m_gridOrigin
1794 int gridStartX = KiROUND( ( worldStartPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1795 int gridEndX = KiROUND( ( worldEndPoint.x - m_gridOrigin.x ) / gridScreenSize.x );
1796 int gridStartY = KiROUND( ( worldStartPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1797 int gridEndY = KiROUND( ( worldEndPoint.y - m_gridOrigin.y ) / gridScreenSize.y );
1798
1799 // Ensure start coordinate < end coordinate
1800 normalize( gridStartX, gridEndX );
1801 normalize( gridStartY, gridEndY );
1802
1803 // Ensure the grid fills the screen
1804 --gridStartX;
1805 ++gridEndX;
1806 --gridStartY;
1807 ++gridEndY;
1808
1809 glDisable( GL_DEPTH_TEST );
1810 glDisable( GL_TEXTURE_2D );
1811
1813 {
1814 glEnable( GL_STENCIL_TEST );
1815 glStencilFunc( GL_ALWAYS, 1, 1 );
1816 glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1817 glColor4d( 0.0, 0.0, 0.0, 0.0 );
1818 SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1819 }
1820 else
1821 {
1824 }
1825
1827 {
1828 // Vertical positions
1829 for( int j = gridStartY; j <= gridEndY; j++ )
1830 {
1831 bool tickY = ( j % m_gridTick == 0 );
1832 const double posY = j * gridScreenSize.y + m_gridOrigin.y;
1833
1834 // Horizontal positions
1835 for( int i = gridStartX; i <= gridEndX; i++ )
1836 {
1837 bool tickX = ( i % m_gridTick == 0 );
1838 SetLineWidth( ( ( tickX && tickY ) ? majorLineWidth : minorLineWidth ) );
1839 auto lineLen = 2.0 * GetLineWidth();
1840 auto posX = i * gridScreenSize.x + m_gridOrigin.x;
1841
1842 DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1843 DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1844 }
1845 }
1846
1848 }
1849 else
1850 {
1851 // Vertical lines
1852 for( int j = gridStartY; j <= gridEndY; j++ )
1853 {
1854 const double y = j * gridScreenSize.y + m_gridOrigin.y;
1855
1856 // If axes are drawn, skip the lines that would cover them
1857 if( m_axesEnabled && y == 0.0 )
1858 continue;
1859
1860 SetLineWidth( ( j % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1861 VECTOR2D a( gridStartX * gridScreenSize.x + m_gridOrigin.x, y );
1862 VECTOR2D b( gridEndX * gridScreenSize.x + m_gridOrigin.x, y );
1863
1864 DrawLine( a, b );
1865 }
1866
1868
1870 {
1871 glStencilFunc( GL_NOTEQUAL, 0, 1 );
1874 }
1875
1876 // Horizontal lines
1877 for( int i = gridStartX; i <= gridEndX; i++ )
1878 {
1879 const double x = i * gridScreenSize.x + m_gridOrigin.x;
1880
1881 // If axes are drawn, skip the lines that would cover them
1882 if( m_axesEnabled && x == 0.0 )
1883 continue;
1884
1885 SetLineWidth( ( i % m_gridTick == 0 ) ? majorLineWidth : minorLineWidth );
1886 VECTOR2D a( x, gridStartY * gridScreenSize.y + m_gridOrigin.y );
1887 VECTOR2D b( x, gridEndY * gridScreenSize.y + m_gridOrigin.y );
1888 DrawLine( a, b );
1889 }
1890
1892
1894 glDisable( GL_STENCIL_TEST );
1895 }
1896
1897 glEnable( GL_DEPTH_TEST );
1898 glEnable( GL_TEXTURE_2D );
1899}
1900
1901
1902void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1903{
1904 m_screenSize = VECTOR2I( aWidth, aHeight );
1905
1906 // Resize framebuffers
1907 const float scaleFactor = GetScaleFactor();
1908 m_compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1910
1911 wxGLCanvas::SetSize( aWidth, aHeight );
1912}
1913
1914
1915bool OPENGL_GAL::Show( bool aShow )
1916{
1917 bool s = wxGLCanvas::Show( aShow );
1918
1919 if( aShow )
1920 wxGLCanvas::Raise();
1921
1922 return s;
1923}
1924
1925
1927{
1928 glFlush();
1929}
1930
1931
1933{
1934 // Clear screen
1936
1937 // NOTE: Black used here instead of m_clearColor; it will be composited later
1938 glClearColor( 0, 0, 0, 1 );
1939 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1940}
1941
1942
1943void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1944{
1945 GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1946
1947 matrixData[0] = aTransformation.m_data[0][0];
1948 matrixData[1] = aTransformation.m_data[1][0];
1949 matrixData[2] = aTransformation.m_data[2][0];
1950 matrixData[4] = aTransformation.m_data[0][1];
1951 matrixData[5] = aTransformation.m_data[1][1];
1952 matrixData[6] = aTransformation.m_data[2][1];
1953 matrixData[12] = aTransformation.m_data[0][2];
1954 matrixData[13] = aTransformation.m_data[1][2];
1955 matrixData[14] = aTransformation.m_data[2][2];
1956
1957 glMultMatrixd( matrixData );
1958}
1959
1960
1961void OPENGL_GAL::Rotate( double aAngle )
1962{
1963 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1964}
1965
1966
1967void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1968{
1969 m_currentManager->Translate( aVector.x, aVector.y, 0.0f );
1970}
1971
1972
1973void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1974{
1975 m_currentManager->Scale( aScale.x, aScale.y, 1.0f );
1976}
1977
1978
1980{
1982}
1983
1984
1986{
1988}
1989
1990
1992{
1993 m_isGrouping = true;
1994
1995 std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *m_cachedManager );
1996 int groupNumber = getNewGroupNumber();
1997 m_groups.insert( std::make_pair( groupNumber, newItem ) );
1998
1999 return groupNumber;
2000}
2001
2002
2004{
2006 m_isGrouping = false;
2007}
2008
2009
2010void OPENGL_GAL::DrawGroup( int aGroupNumber )
2011{
2012 auto group = m_groups.find( aGroupNumber );
2013
2014 if( group != m_groups.end() )
2015 m_cachedManager->DrawItem( *group->second );
2016}
2017
2018
2019void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
2020{
2021 auto group = m_groups.find( aGroupNumber );
2022
2023 if( group != m_groups.end() )
2024 m_cachedManager->ChangeItemColor( *group->second, aNewColor );
2025}
2026
2027
2028void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
2029{
2030 auto group = m_groups.find( aGroupNumber );
2031
2032 if( group != m_groups.end() )
2033 m_cachedManager->ChangeItemDepth( *group->second, aDepth );
2034}
2035
2036
2037void OPENGL_GAL::DeleteGroup( int aGroupNumber )
2038{
2039 // Frees memory in the container as well
2040 m_groups.erase( aGroupNumber );
2041}
2042
2043
2045{
2046 m_bitmapCache = std::make_unique<GL_BITMAP_CACHE>();
2047
2048 m_groups.clear();
2049
2050 if( m_isInitialized )
2052}
2053
2054
2056{
2057 switch( aTarget )
2058 {
2059 default:
2064 }
2065
2066 m_currentTarget = aTarget;
2067}
2068
2069
2071{
2072 return m_currentTarget;
2073}
2074
2075
2077{
2078 // Save the current state
2079 unsigned int oldTarget = m_compositor->GetBuffer();
2080
2081 switch( aTarget )
2082 {
2083 // Cached and noncached items are rendered to the same buffer
2084 default:
2085 case TARGET_CACHED:
2086 case TARGET_NONCACHED:
2088 break;
2089
2090 case TARGET_TEMP:
2091 if( m_tempBuffer )
2093 break;
2094
2095 case TARGET_OVERLAY:
2096 if( m_overlayBuffer )
2098 break;
2099 }
2100
2101 if( aTarget != TARGET_OVERLAY )
2103 else if( m_overlayBuffer )
2105
2106 // Restore the previous state
2107 m_compositor->SetBuffer( oldTarget );
2108}
2109
2110
2112{
2113 switch( aTarget )
2114 {
2115 default:
2116 case TARGET_CACHED:
2117 case TARGET_NONCACHED: return true;
2118 case TARGET_OVERLAY: return ( m_overlayBuffer != 0 );
2119 case TARGET_TEMP: return ( m_tempBuffer != 0 );
2120 }
2121}
2122
2123
2125{
2127
2128 if( m_tempBuffer )
2129 {
2132 }
2133}
2134
2135
2137{
2138 if( m_tempBuffer )
2139 {
2140 glBlendEquation( GL_MAX );
2142 glBlendEquation( GL_FUNC_ADD );
2143
2145 }
2146 else
2147 {
2148 // Fall back to imperfect alpha blending on single buffer
2149 glBlendFunc( GL_SRC_ALPHA, GL_ONE );
2151 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
2152 }
2153}
2154
2155
2156bool OPENGL_GAL::SetNativeCursorStyle( KICURSOR aCursor, bool aHiDPI )
2157{
2158 // Store the current cursor type and get the wxCursor for it
2159 if( !GAL::SetNativeCursorStyle( aCursor, aHiDPI ) )
2160 return false;
2161
2162 if( aHiDPI )
2164 else
2166
2167 // Update the cursor in the wx control
2168 HIDPI_GL_CANVAS::SetCursor( m_currentwxCursor );
2169
2170 return true;
2171}
2172
2173
2174void OPENGL_GAL::onSetNativeCursor( wxSetCursorEvent& aEvent )
2175{
2176 aEvent.SetCursor( m_currentwxCursor );
2177}
2178
2179
2180void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
2181{
2182 // Now we should only store the position of the mouse cursor
2183 // The real drawing routines are in blitCursor()
2184 //VECTOR2D screenCursor = m_worldScreenMatrix * aCursorPosition;
2185 //m_cursorPosition = m_screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
2186 m_cursorPosition = aCursorPosition;
2187}
2188
2189
2190void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
2191 const bool aReserve )
2192{
2193 /* Helper drawing: ____--- v3 ^
2194 * ____---- ... \ \
2195 * ____---- ... \ end \
2196 * v1 ____---- ... ____---- \ width
2197 * ---- ...___---- \ \
2198 * \ ___...-- \ v
2199 * \ ____----... ____---- v2
2200 * ---- ... ____----
2201 * start \ ... ____----
2202 * \... ____----
2203 * ----
2204 * v0
2205 * dots mark triangles' hypotenuses
2206 */
2207
2209 * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
2211 * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
2212
2213 VECTOR2D vs( v2.x - v1.x, v2.y - v1.y );
2214
2215 if( aReserve )
2216 reserveLineQuads( 1 );
2217
2218 // Line width is maintained by the vertex shader
2220 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2221
2223 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2224
2226 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2227
2229 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2230
2232 m_currentManager->Vertex( aEndPoint, m_layerDepth );
2233
2235 m_currentManager->Vertex( aStartPoint, m_layerDepth );
2236}
2237
2238
2239void OPENGL_GAL::reserveLineQuads( const int aLineCount )
2240{
2241 m_currentManager->Reserve( 6 * aLineCount );
2242}
2243
2244
2245void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2246{
2247 if( m_isFillEnabled )
2248 {
2250 drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
2251 }
2252
2253 if( m_isStrokeEnabled )
2254 {
2256 m_strokeColor.a );
2257 drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
2258 }
2259}
2260
2261
2262void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
2263{
2264 Save();
2265
2267 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2268 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2269
2270 /* Draw a triangle that contains the semicircle, then shade it to leave only
2271 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2272 * (if you want to understand more, check the vertex shader source [shader.vert]).
2273 * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
2274 * v2
2275 * /\
2276 * /__\
2277 * v0 //__\\ v1
2278 */
2280 m_currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2281
2283 m_currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2284
2286 m_currentManager->Vertex( 0.0f, aRadius * 2.0f, m_layerDepth ); // v2
2287
2288 Restore();
2289}
2290
2291
2292void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle,
2293 bool aReserve )
2294{
2295 double outerRadius = aRadius + ( m_lineWidth / 2 );
2296
2297 Save();
2298
2299 if( aReserve )
2301
2302 m_currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
2303 m_currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
2304
2305 /* Draw a triangle that contains the semicircle, then shade it to leave only
2306 * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
2307 * (if you want to understand more, check the vertex shader source [shader.vert]), the
2308 * radius and the line width. Shader uses these coordinates to determine if fragments are
2309 * inside the semicircle or not.
2310 * v2
2311 * /\
2312 * /__\
2313 * v0 //__\\ v1
2314 */
2316 m_currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v0
2317
2319 m_currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, m_layerDepth ); // v1
2320
2322 m_currentManager->Vertex( 0.0f, outerRadius * 2.0f, m_layerDepth ); // v2
2323
2324 Restore();
2325}
2326
2327
2328void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
2329{
2330 if( m_isFillEnabled )
2331 {
2334
2335 // Any non convex polygon needs to be tesselated
2336 // for this purpose the GLU standard functions are used
2338 gluTessBeginPolygon( m_tesselator, &params );
2339 gluTessBeginContour( m_tesselator );
2340
2341 GLdouble* point = aPoints;
2342
2343 for( int i = 0; i < aPointCount; ++i )
2344 {
2345 gluTessVertex( m_tesselator, point, point );
2346 point += 3; // 3 coordinates
2347 }
2348
2349 gluTessEndContour( m_tesselator );
2350 gluTessEndPolygon( m_tesselator );
2351
2352 // Free allocated intersecting points
2353 m_tessIntersects.clear();
2354 }
2355
2356 if( m_isStrokeEnabled )
2357 {
2359 [&]( int idx )
2360 {
2361 return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] );
2362 },
2363 aPointCount );
2364 }
2365}
2366
2367
2368void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
2369 bool aReserve )
2370{
2371 wxCHECK( aPointCount > 0, /* return */ );
2372
2374
2375 if( aPointCount == 1 )
2376 {
2377 drawLineQuad( aPointGetter( 0 ), aPointGetter( 0 ), aReserve );
2378 return;
2379 }
2380
2381 if( aReserve )
2382 {
2383 reserveLineQuads( aPointCount - 1 );
2384 }
2385
2386 for( int i = 1; i < aPointCount; ++i )
2387 {
2388 auto start = aPointGetter( i - 1 );
2389 auto end = aPointGetter( i );
2390
2391 drawLineQuad( start, end, false );
2392 }
2393}
2394
2395
2396void OPENGL_GAL::drawSegmentChain( const std::function<VECTOR2D( int )>& aPointGetter,
2397 int aPointCount, double aWidth, bool aReserve )
2398{
2399 wxCHECK( aPointCount >= 2, /* return */ );
2400
2402
2403 int vertices = 0;
2404
2405 for( int i = 1; i < aPointCount; ++i )
2406 {
2407 auto start = aPointGetter( i - 1 );
2408 auto end = aPointGetter( i );
2409
2410 VECTOR2D startEndVector = end - start;
2411 double lineLength = startEndVector.EuclideanNorm();
2412
2413 float startx = start.x;
2414 float starty = start.y;
2415 float endx = start.x + lineLength;
2416 float endy = start.y + lineLength;
2417
2418 // Be careful about floating point rounding. As we draw segments in larger and larger
2419 // coordinates, the shader (which uses floats) will lose precision and stop drawing small
2420 // segments. In this case, we need to draw a circle for the minimal segment.
2421 if( startx == endx || starty == endy )
2422 {
2423 vertices += 3; // One circle
2424 continue;
2425 }
2426
2427 if( m_isFillEnabled || aWidth == 1.0 )
2428 {
2429 vertices += 6; // One line
2430 }
2431 else
2432 {
2433 vertices += 6 + 6 + 3 + 3; // Two lines and two half-circles
2434 }
2435 }
2436
2437 m_currentManager->Reserve( vertices );
2438
2439 for( int i = 1; i < aPointCount; ++i )
2440 {
2441 auto start = aPointGetter( i - 1 );
2442 auto end = aPointGetter( i );
2443
2444 drawSegment( start, end, aWidth, false );
2445 }
2446}
2447
2448
2449int OPENGL_GAL::drawBitmapChar( unsigned long aChar, bool aReserve )
2450{
2451 const float TEX_X = font_image.width;
2452 const float TEX_Y = font_image.height;
2453
2454 // handle space
2455 if( aChar == ' ' )
2456 {
2457 const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
2458 wxCHECK( g, 0 );
2459
2460 // Match stroke font as well as possible
2461 double spaceWidth = g->advance * 0.74;
2462
2463 Translate( VECTOR2D( spaceWidth, 0 ) );
2464 return KiROUND( spaceWidth );
2465 }
2466
2467 const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
2468
2469 // If the glyph is not found (happens for many esoteric unicode chars)
2470 // shows a '?' instead.
2471 if( !glyph )
2472 glyph = LookupGlyph( '?' );
2473
2474 if( !glyph ) // Should not happen.
2475 return 0;
2476
2477 const float X = glyph->atlas_x + font_information.smooth_pixels;
2478 const float Y = glyph->atlas_y + font_information.smooth_pixels;
2479 const float XOFF = glyph->minx;
2480
2481 // adjust for height rounding
2482 const float round_adjust = ( glyph->maxy - glyph->miny )
2483 - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
2484 const float top_adjust = font_information.max_y - glyph->maxy;
2485 const float YOFF = round_adjust + top_adjust;
2486 const float W = glyph->atlas_w - font_information.smooth_pixels * 2;
2487 const float H = glyph->atlas_h - font_information.smooth_pixels * 2;
2488 const float B = 0;
2489
2490 if( aReserve )
2492
2493 Translate( VECTOR2D( XOFF, YOFF ) );
2494
2495 /* Glyph:
2496 * v0 v1
2497 * +--+
2498 * | /|
2499 * |/ |
2500 * +--+
2501 * v2 v3
2502 */
2503 m_currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
2504 m_currentManager->Vertex( -B, -B, 0 ); // v0
2505
2506 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2507 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2508
2509 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2510 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2511
2512
2513 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
2514 m_currentManager->Vertex( W + B, -B, 0 ); // v1
2515
2516 m_currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
2517 m_currentManager->Vertex( -B, H + B, 0 ); // v2
2518
2519 m_currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
2520 m_currentManager->Vertex( W + B, H + B, 0 ); // v3
2521
2522 Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
2523
2524 return glyph->advance;
2525}
2526
2527
2528void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight, bool aReserve )
2529{
2530 // To draw an overbar, simply draw an overbar
2531 const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
2532 wxCHECK( glyph, /* void */ );
2533
2534 const float H = glyph->maxy - glyph->miny;
2535
2536 Save();
2537
2538 Translate( VECTOR2D( -aLength, -aHeight ) );
2539
2540 if( aReserve )
2542
2544
2546
2547 m_currentManager->Vertex( 0, 0, 0 ); // v0
2548 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2549 m_currentManager->Vertex( 0, H, 0 ); // v2
2550
2551 m_currentManager->Vertex( aLength, 0, 0 ); // v1
2552 m_currentManager->Vertex( 0, H, 0 ); // v2
2553 m_currentManager->Vertex( aLength, H, 0 ); // v3
2554
2555 Restore();
2556}
2557
2558
2559std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
2560{
2561 static const FONT_GLYPH_TYPE* defaultGlyph = LookupGlyph( '(' ); // for strange chars
2562
2563 VECTOR2D textSize( 0, 0 );
2564 float commonOffset = std::numeric_limits<float>::max();
2565 float charHeight = font_information.max_y - defaultGlyph->miny;
2566 int overbarDepth = -1;
2567 int braceNesting = 0;
2568
2569 for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
2570 {
2571 if( *chIt == '~' && overbarDepth == -1 )
2572 {
2573 UTF8::uni_iter lookahead = chIt;
2574
2575 if( ++lookahead != end && *lookahead == '{' )
2576 {
2577 chIt = lookahead;
2578 overbarDepth = braceNesting;
2579 braceNesting++;
2580 continue;
2581 }
2582 }
2583 else if( *chIt == '{' )
2584 {
2585 braceNesting++;
2586 }
2587 else if( *chIt == '}' )
2588 {
2589 if( braceNesting > 0 )
2590 braceNesting--;
2591
2592 if( braceNesting == overbarDepth )
2593 {
2594 overbarDepth = -1;
2595 continue;
2596 }
2597 }
2598
2599 const FONT_GLYPH_TYPE* glyph = LookupGlyph( *chIt );
2600
2601 if( !glyph // Not coded in font
2602 || *chIt == '-' || *chIt == '_' ) // Strange size of these 2 chars
2603 {
2604 glyph = defaultGlyph;
2605 }
2606
2607 if( glyph )
2608 textSize.x += glyph->advance;
2609 }
2610
2611 textSize.y = std::max<float>( textSize.y, charHeight );
2612 commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
2613 textSize.y -= commonOffset;
2614
2615 return std::make_pair( textSize, commonOffset );
2616}
2617
2618
2619void OPENGL_GAL::onPaint( wxPaintEvent& aEvent )
2620{
2621 PostPaint( aEvent );
2622}
2623
2624
2625void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
2626{
2627 // Post the mouse event to the event listener registered in constructor, if any
2628 if( m_mouseListener )
2629 wxPostEvent( m_mouseListener, aEvent );
2630}
2631
2632
2633void OPENGL_GAL::skipGestureEvent( wxGestureEvent& aEvent )
2634{
2635 // Post the gesture event to the event listener registered in constructor, if any
2636 if( m_mouseListener )
2637 wxPostEvent( m_mouseListener, aEvent );
2638}
2639
2640
2642{
2643 if( !IsCursorEnabled() )
2644 return;
2645
2647
2648 const int cursorSize = m_fullscreenCursor ? 8000 : 80;
2649
2650 VECTOR2D cursorBegin = m_cursorPosition - cursorSize / ( 2 * m_worldScale );
2651 VECTOR2D cursorEnd = m_cursorPosition + cursorSize / ( 2 * m_worldScale );
2652 VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
2653
2654 const COLOR4D color = getCursorColor();
2655
2656 glActiveTexture( GL_TEXTURE0 );
2657 glDisable( GL_TEXTURE_2D );
2658 glEnable( GL_BLEND );
2659 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
2660
2661 glLineWidth( 1.0 );
2662 glColor4d( color.r, color.g, color.b, color.a );
2663
2664 glBegin( GL_LINES );
2665 glVertex2d( cursorCenter.x, cursorBegin.y );
2666 glVertex2d( cursorCenter.x, cursorEnd.y );
2667
2668 glVertex2d( cursorBegin.x, cursorCenter.y );
2669 glVertex2d( cursorEnd.x, cursorCenter.y );
2670 glEnd();
2671}
2672
2673
2675{
2676 wxASSERT_MSG( m_groups.size() < std::numeric_limits<unsigned int>::max(),
2677 wxT( "There are no free slots to store a group" ) );
2678
2679 while( m_groups.find( m_groupCounter ) != m_groups.end() )
2681
2682 return m_groupCounter++;
2683}
2684
2685
2687{
2688#ifndef KICAD_USE_EGL
2689 wxASSERT( IsShownOnScreen() );
2690#endif // KICAD_USE_EGL
2691
2692 wxASSERT_MSG( m_isContextLocked, "This should only be called from within a locked context." );
2693
2694 // Check correct initialization from the constructor
2695 if( m_tesselator == nullptr )
2696 throw std::runtime_error( "Could not create the tesselator" );
2697 GLenum err = glewInit();
2698
2699#ifdef KICAD_USE_EGL
2700 // TODO: better way to check when EGL is ready (init fails at "getString(GL_VERSION)")
2701 for( int i = 0; i < 10; i++ )
2702 {
2703 if( GLEW_OK == err )
2704 break;
2705
2706 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
2707 err = glewInit();
2708 }
2709
2710#endif // KICAD_USE_EGL
2711
2712 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
2713 (const char*) glGetString( GL_VERSION ) );
2714
2715 if( GLEW_OK != err )
2716 throw std::runtime_error( (const char*) glewGetErrorString( err ) );
2717
2718 // Check the OpenGL version (minimum 2.1 is required)
2719 if( !GLEW_VERSION_2_1 )
2720 throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2721
2722#if defined( __LINUX__ ) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2723#ifdef DEBUG
2724 if( GLEW_ARB_debug_output )
2725 enableGlDebug( true );
2726#endif
2727#endif
2728
2729 // Framebuffers have to be supported
2730 if( !GLEW_EXT_framebuffer_object )
2731 throw std::runtime_error( "Framebuffer objects are not supported!" );
2732
2733 // Vertex buffer has to be supported
2734 if( !GLEW_ARB_vertex_buffer_object )
2735 throw std::runtime_error( "Vertex buffer objects are not supported!" );
2736
2737 // Prepare shaders
2738 if( !m_shader->IsLinked()
2740 BUILTIN_SHADERS::glsl_kicad_vert ) )
2741 {
2742 throw std::runtime_error( "Cannot compile vertex shader!" );
2743 }
2744
2745 if( !m_shader->IsLinked()
2747 BUILTIN_SHADERS::glsl_kicad_frag ) )
2748 {
2749 throw std::runtime_error( "Cannot compile fragment shader!" );
2750 }
2751
2752 if( !m_shader->IsLinked() && !m_shader->Link() )
2753 throw std::runtime_error( "Cannot link the shaders!" );
2754
2755 // Check if video card supports textures big enough to fit the font atlas
2756 int maxTextureSize;
2757 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2758
2759 if( maxTextureSize < (int) font_image.width || maxTextureSize < (int) font_image.height )
2760 {
2761 // TODO implement software texture scaling
2762 // for bitmap fonts and use a higher resolution texture?
2763 throw std::runtime_error( "Requested texture size is not supported" );
2764 }
2765
2767
2768 m_cachedManager = new VERTEX_MANAGER( true );
2769 m_nonCachedManager = new VERTEX_MANAGER( false );
2770 m_overlayManager = new VERTEX_MANAGER( false );
2771 m_tempManager = new VERTEX_MANAGER( false );
2772
2773 // Make VBOs use shaders
2778
2779 m_isInitialized = true;
2780}
2781
2782
2783// Callback functions for the tesselator. Compare Redbook Chapter 11.
2784void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2785{
2786 GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2787 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2788 VERTEX_MANAGER* vboManager = param->vboManager;
2789
2790 assert( vboManager );
2791 vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2792}
2793
2794
2795void CALLBACK CombineCallback( GLdouble coords[3], GLdouble* vertex_data[4], GLfloat weight[4],
2796 GLdouble** dataOut, void* aData )
2797{
2798 GLdouble* vertex = new GLdouble[3];
2799 OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2800
2801 // Save the pointer so we can delete it later
2802 // Note, we use the default_delete for an array because macOS
2803 // decides to bundle an ancient libc++ that mismatches the C++17 support of clang
2804 param->intersectPoints.emplace_back( vertex, std::default_delete<GLdouble[]>() );
2805
2806 memcpy( vertex, coords, 3 * sizeof( GLdouble ) );
2807
2808 *dataOut = vertex;
2809}
2810
2811
2812void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2813{
2814 // This callback is needed to force GLU tesselator to use triangles only
2815}
2816
2817
2818void CALLBACK ErrorCallback( GLenum aErrorCode )
2819{
2820 //throw std::runtime_error( std::string( "Tessellation error: " ) +
2821 //std::string( (const char*) gluErrorString( aErrorCode ) );
2822}
2823
2824
2825static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2826{
2827 gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, (void( CALLBACK* )()) VertexCallback );
2828 gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, (void( CALLBACK* )()) CombineCallback );
2829 gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, (void( CALLBACK* )()) EdgeCallback );
2830 gluTessCallback( aTesselator, GLU_TESS_ERROR, (void( CALLBACK* )()) ErrorCallback );
2831}
2832
2833
2834void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2835{
2836 m_cachedManager->EnableDepthTest( aEnabled );
2838 m_overlayManager->EnableDepthTest( aEnabled );
2839}
2840
2841
2842inline double round_to_half_pixel( double f, double r )
2843{
2844 return ( ceil( f / r ) - 0.5 ) * r;
2845}
2846
2847
2849{
2851 auto pixelSize = m_worldScale;
2852
2853 // we need -m_lookAtPoint == -k * pixelSize + 0.5 * pixelSize for OpenGL
2854 // meaning m_lookAtPoint = (k-0.5)*pixelSize with integer k
2857
2859}
2860
2861
2862void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
2863{
2864 if( aGlyph.IsStroke() )
2865 {
2866 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( aGlyph );
2867
2868 DrawPolylines( strokeGlyph );
2869 }
2870 else if( aGlyph.IsOutline() )
2871 {
2872 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
2873
2876
2877 outlineGlyph.Triangulate(
2878 [&]( const VECTOR2D& aPt1, const VECTOR2D& aPt2, const VECTOR2D& aPt3 )
2879 {
2881
2882 m_currentManager->Vertex( aPt1.x, aPt1.y, m_layerDepth );
2883 m_currentManager->Vertex( aPt2.x, aPt2.y, m_layerDepth );
2884 m_currentManager->Vertex( aPt3.x, aPt3.y, m_layerDepth );
2885 } );
2886 }
2887}
2888
2889
2890void OPENGL_GAL::DrawGlyphs( const std::vector<std::unique_ptr<KIFONT::GLYPH>>& aGlyphs )
2891{
2892 if( aGlyphs.empty() )
2893 return;
2894
2895 bool allGlyphsAreStroke = true;
2896 bool allGlyphsAreOutline = true;
2897
2898 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2899 {
2900 if( !glyph->IsStroke() )
2901 {
2902 allGlyphsAreStroke = false;
2903 break;
2904 }
2905 }
2906
2907 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2908 {
2909 if( !glyph->IsOutline() )
2910 {
2911 allGlyphsAreOutline = false;
2912 break;
2913 }
2914 }
2915
2916 if( allGlyphsAreStroke )
2917 {
2918 // Optimized path for stroke fonts that pre-reserves line quads.
2919 int lineQuadCount = 0;
2920
2921 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2922 {
2923 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2924
2925 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2926 lineQuadCount += points.size() - 1;
2927 }
2928
2929 reserveLineQuads( lineQuadCount );
2930
2931 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2932 {
2933 const auto& strokeGlyph = static_cast<const KIFONT::STROKE_GLYPH&>( *glyph );
2934
2935 for( const std::vector<VECTOR2D>& points : strokeGlyph )
2936 {
2938 [&]( int idx )
2939 {
2940 return points[idx];
2941 },
2942 points.size(), false );
2943 }
2944 }
2945
2946 return;
2947 }
2948 else if( allGlyphsAreOutline )
2949 {
2950 // Optimized path for outline fonts that pre-reserves glyph triangles.
2951 int triangleCount = 0;
2952
2953 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2954 {
2955 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2956
2957 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2958 {
2960 outlineGlyph.TriangulatedPolygon( i );
2961
2962 triangleCount += polygon->GetTriangleCount();
2963 }
2964 }
2965
2968
2969 m_currentManager->Reserve( 3 * triangleCount );
2970
2971 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs )
2972 {
2973 const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( *glyph );
2974
2975 for( unsigned int i = 0; i < outlineGlyph.TriangulatedPolyCount(); i++ )
2976 {
2978 outlineGlyph.TriangulatedPolygon( i );
2979
2980 for( size_t j = 0; j < polygon->GetTriangleCount(); j++ )
2981 {
2982 VECTOR2I a, b, c;
2983 polygon->GetTriangle( j, a, b, c );
2984
2988 }
2989 }
2990 }
2991 }
2992 else
2993 {
2994 // Regular path
2995 for( size_t i = 0; i < aGlyphs.size(); i++ )
2996 DrawGlyph( *aGlyphs[i], i, aGlyphs.size() );
2997 }
2998}
int color
Definition: DXF_plotter.cpp:60
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
void SetOpenGLInfo(const char *aVendor, const char *aRenderer, const char *aVersion)
A setter for OpenGL info when it's initialized.
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:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:49
const wxImage * GetOriginalImageData() const
Definition: bitmap_base.h:71
VECTOR2I GetSizePixels() const
Definition: bitmap_base.h:107
EDA_ANGLE Rotation() const
Definition: bitmap_base.h:218
bool IsMirroredX() const
Definition: bitmap_base.h:216
bool IsMirroredY() const
Definition: bitmap_base.h:217
KIID GetImageID() const
Definition: bitmap_base.h:76
int GetPPI() const
Definition: bitmap_base.h:118
static const wxCursor GetHiDPICursor(KICURSOR aCursorType)
Definition: cursors.cpp:584
static const wxCursor GetCursor(KICURSOR aCursorType)
Definition: cursors.cpp:570
double AsDegrees() const
Definition: eda_angle.h:113
double AsRadians() const
Definition: eda_angle.h:117
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.
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static int SetSwapInterval(int aVal)
Attempt to set the OpenGL swap interval.
Definition: gl_utils.h:49
wxGLCanvas wrapper for HiDPI/Retina support.
virtual wxSize GetNativePixelSize() const
double GetScaleFactor() const
Get the current scale factor.
virtual bool IsStroke() const
Definition: glyph.h:51
virtual bool IsOutline() const
Definition: glyph.h:50
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:392
double g
Green component.
Definition: color4d.h:393
double a
Alpha component.
Definition: color4d.h:395
static const COLOR4D BLACK
Definition: color4d.h:402
double b
Blue component.
Definition: color4d.h:394
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.
VECTOR2D GetVisibleGridSize() const
Return the visible grid size in x and y directions.
double m_layerDepth
The actual layer depth.
MATRIX3x3D m_screenWorldMatrix
Screen transformation.
friend class GAL_SCOPED_ATTRS
bool m_axesEnabled
Should the axes be drawn.
float m_gridLineWidth
Line width of the grid.
VECTOR2I m_screenSize
Screen size in screen (wx logical) coordinates.
GR_TEXT_H_ALIGN_T GetHorizontalJustify() const
void normalize(T &a, T &b)
Ensure that the first element is smaller than the second.
VECTOR2D m_depthRange
Range of the depth.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
virtual bool SetNativeCursorStyle(KICURSOR aCursor, bool aHiDPI)
Set the cursor in the native panel.
GRID_STYLE m_gridStyle
Grid display style.
COLOR4D m_axesColor
Color of the axes.
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.
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.
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:171
const size_t m_cacheMaxElements
Definition: opengl_gal.cpp:115
GLuint RequestBitmap(const BITMAP_BASE *aBitmap)
Definition: opengl_gal.cpp:134
std::list< GLuint > m_freedTextureIds
Definition: opengl_gal.cpp:121
const size_t m_cacheMaxSize
Definition: opengl_gal.cpp:116
std::map< const KIID, CACHED_BITMAP > m_bitmaps
Definition: opengl_gal.cpp:118
std::list< KIID > m_cacheLru
Definition: opengl_gal.cpp:119
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:71
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:845
unsigned int m_groupCounter
Counter used for generating keys for groups.
Definition: opengl_gal.h:346
void EndDiffLayer() override
Ends rendering of a differential layer.
VERTEX_MANAGER * m_overlayManager
Container for storing overlaid VERTEX_ITEMs.
Definition: opengl_gal.h:351
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:371
VERTEX_MANAGER * m_currentManager
Currently used VERTEX_MANAGER (for storing VERTEX_ITEMs).
Definition: opengl_gal.h:347
void drawCircle(const VECTOR2D &aCenterPoint, double aRadius, bool aReserve=true)
Internal method for circle drawing.
Definition: opengl_gal.cpp:909
std::deque< std::shared_ptr< GLdouble > > m_tessIntersects
Definition: opengl_gal.h:388
void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: opengl_gal.cpp:903
void LockContext(int aClientCookie) override
Use GAL_CONTEXT_LOCKER RAII object unless you know what you're doing.
Definition: opengl_gal.cpp:780
unsigned int m_mainBuffer
Main rendering target.
Definition: opengl_gal.h:358
std::unique_ptr< GL_BITMAP_CACHE > m_bitmapCache
Definition: opengl_gal.h:384
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:472
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:370
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 DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle, double aWidth, double aMaxError) override
Draw an arc segment.
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:513
unsigned int m_overlayBuffer
Auxiliary rendering target (for menus etc.)
Definition: opengl_gal.h:359
void PostPaint(wxPaintEvent &aEvent)
Post an event to #m_paint_listener.
Definition: opengl_gal.cpp:505
OPENGL_COMPOSITOR * m_compositor
Handles multiple rendering targets.
Definition: opengl_gal.h:357
VERTEX_MANAGER * m_cachedManager
Container for storing cached VERTEX_ITEMs.
Definition: opengl_gal.h:349
void Translate(const VECTOR2D &aTranslation) override
Translate the context.
void DrawPolyline(const std::deque< VECTOR2D > &aPointList) override
Draw a polyline.
bool SetNativeCursorStyle(KICURSOR aCursor, bool aHiDPI) override
Set the cursor in the native panel.
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:376
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:379
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:111
wxEvtHandler * m_mouseListener
Definition: opengl_gal.h:337
int m_swapInterval
Used to store swap interval information.
Definition: opengl_gal.h:335
void ClearCache() override
Delete all data created during caching of graphic items.
double getWorldPixelSize() const
Definition: opengl_gal.cpp:536
void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: opengl_gal.cpp:838
GROUPS_MAP m_groups
Stores information about VBO objects (groups)
Definition: opengl_gal.h:345
void endUpdate() override
Definition: opengl_gal.cpp:821
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:350
int drawBitmapChar(unsigned long aChar, bool aReserve=true)
Draw a single character using bitmap font.
GLUtesselator * m_tesselator
Definition: opengl_gal.h:387
wxEvtHandler * m_paintListener
Definition: opengl_gal.h:338
void StartDiffLayer() override
Begins rendering of a differential layer.
bool m_isContextLocked
Used for assertion checking.
Definition: opengl_gal.h:374
void Save() override
Save the context.
wxCursor m_currentwxCursor
wxCursor showing the current native cursor.
Definition: opengl_gal.h:382
bool m_isFramebufferInitialized
Are the framebuffers initialized?
Definition: opengl_gal.h:368
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:340
virtual bool HasTarget(RENDER_TARGET aTarget) override
Return true if the target exists.
void reserveLineQuads(const int aLineCount)
Reserve specified number of line quads.
void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: opengl_gal.cpp:830
OPENGL_GAL(const KIGFX::VC_SETTINGS &aVcSettings, GAL_DISPLAY_OPTIONS &aDisplayOptions, wxWindow *aParent, wxEvtHandler *aMouseListener=nullptr, wxEvtHandler *aPaintListener=nullptr, const wxString &aName=wxT("GLCanvas"))
Definition: opengl_gal.cpp:313
void beginUpdate() override
Definition: opengl_gal.cpp:805
double calcAngleStep(double aRadius) const
Compute the angle step when drawing arcs/circles approximated with lines.
Definition: opengl_gal.h:579
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:373
void BeginDrawing() override
Start/end drawing functions, draw calls can be only made in between the calls to BeginDrawing()/EndDr...
Definition: opengl_gal.cpp:551
GLint ufm_screenPixelSize
Definition: opengl_gal.h:377
void Rotate(double aAngle) override
Rotate the context.
int BeginGroup() override
Begin a group.
GLint ufm_pixelSizeMultiplier
Definition: opengl_gal.h:378
VECTOR2D getScreenPixelSize() const
Definition: opengl_gal.cpp:543
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 skipGestureEvent(wxGestureEvent &aEvent)
Skip the gesture event to the parent.
void UnlockContext(int aClientCookie) override
Definition: opengl_gal.cpp:790
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:354
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:720
SHADER * m_shader
There is only one shader used for different objects.
Definition: opengl_gal.h:365
void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle) override
Draw an arc.
Definition: opengl_gal.cpp:971
void DrawPolylines(const std::vector< std::vector< VECTOR2D > > &aPointLists) override
Draw multiple polylines.
RENDER_TARGET m_currentTarget
Current rendering target.
Definition: opengl_gal.h:361
wxGLContext * m_glPrivContext
Canvas-specific OpenGL context.
Definition: opengl_gal.h:334
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:369
static wxGLContext * m_glMainContext
Parent OpenGL context.
Definition: opengl_gal.h:333
unsigned int m_tempBuffer
Temporary rendering target (for diffing etc.)
Definition: opengl_gal.h:360
void init()
Basic OpenGL initialization and feature checks.
static int m_instanceCounter
GL GAL instance counter.
Definition: opengl_gal.h:336
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)
Change 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.
Definition: kiid.h:49
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition: matrix3x3.h:295
T m_data[3][3]
Definition: matrix3x3.h:65
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition: pgm_base.h:115
A small class to help profiling.
Definition: profile.h:49
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition: profile.h:88
void Start()
Start or restart the counter.
Definition: profile.h:77
std::string to_string()
Definition: profile.h:155
double msecs(bool aSinceLast=false)
Definition: profile.h:149
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)
Return the aIndex-th subpolygon in the set.
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
unsigned int TriangulatedPolyCount() const
Return the number of triangulated polygons.
int OutlineCount() const
Return the number of outlines in the set.
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:209
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:72
uni_iter uend() const
Return a uni_iter initialized to the end of "this" UTF8 byte sequence.
Definition: utf8.h:292
uni_iter ubegin() const
Returns a uni_iter initialized to the start of "this" UTF8 byte sequence.
Definition: utf8.h:284
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
T y
Definition: vector3.h:64
T x
Definition: vector3.h:63
@ BLUE
Definition: color4d.h:56
KICURSOR
Definition: cursors.h:34
static constexpr EDA_ANGLE FULL_CIRCLE
Definition: eda_angle.h:399
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.
KIID niluuid(0)
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:247
@ 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:36
@ TARGET_NONCACHED
Auxiliary rendering target (noncached)
Definition: definitions.h:38
@ TARGET_TEMP
Temporary target for drawing in separate layer.
Definition: definitions.h:40
@ TARGET_CACHED
Main rendering target (cached)
Definition: definitions.h:37
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition: definitions.h:39
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
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)
static wxGLAttributes getGLAttribs()
Definition: opengl_gal.cpp:78
void CALLBACK ErrorCallback(GLenum aErrorCode)
double round_to_half_pixel(double f, double r)
#define SEG_PER_CIRCLE_COUNT
Definition: opengl_gal.h:53
#define CALLBACK
The default number of points for circle approximation.
Definition: opengl_gal.h:49
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
const int scale
std::vector< FAB_LAYER_COLOR > dummy
unsigned char pixels[1024 *1024 *3]
Definition: gl_resources.h:38
VERTEX_MANAGER * vboManager
Manager used for storing new vertices.
Definition: opengl_gal.h:323
std::deque< std::shared_ptr< GLdouble > > & intersectPoints
Intersect points, that have to be freed after tessellation.
Definition: opengl_gal.h:326
Structure to keep VIEW_CONTROLS settings for easy store/restore operations.
Definition: view_controls.h:43
VECTOR3I v1(5, 5, 5)
VECTOR2I end
VECTOR2I v2(1, 0)
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_H_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
wxLogTrace helper definitions.
#define KI_TRACE(aWhat,...)
void enableGlDebug(bool aEnable)
Enable or disable OpenGL driver messages output.
Definition: utils.cpp:188
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< int32_t > VECTOR2I
Definition: vector2d.h:695
VECTOR2< double > VECTOR2D
Definition: vector2d.h:694
VECTOR2I ToVECTOR2I(const wxSize &aSize)
Definition: vector2wx.h:30