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