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