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