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