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