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