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