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