KiCad PCB EDA Suite
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 (C) 2012-2018 Kicad Developers, see AUTHORS.txt for contributors.
6  * Copyright (C) 2013-2017 CERN
7  * @author Maciej Suminski <maciej.suminski@cern.ch>
8  *
9  * Graphics Abstraction Layer (GAL) for OpenGL
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 #include <gal/opengl/opengl_gal.h>
30 #include <gal/opengl/utils.h>
31 #include <gal/definitions.h>
32 #include <gl_context_mgr.h>
34 #include <text_utils.h>
35 #include <bitmap_base.h>
36 
37 #include <macros.h>
38 
39 #ifdef __WXDEBUG__
40 #include <profile.h>
41 #include <wx/log.h>
42 #endif /* __WXDEBUG__ */
43 
44 #include <limits>
45 #include <functional>
46 using namespace std::placeholders;
47 using namespace KIGFX;
48 
49 // A ugly workaround to avoid serious issues (crashes) when using bitmaps cache
50 // to speedup redraw.
51 // issues arise when using bitmaps in page layout, when the page layout containd bitmaps,
52 // and is common to schematic and board editor,
53 // and the schematic is a hierarchy and when using cross-probing
54 // When the cross probing from pcbnew to eeschema switches to a sheet, the bitmaps cache
55 // becomes broken (in fact the associated texture).
56 // I hope (JPC) it will be fixed later, but a slighty slower refresh is better than a crash
57 #define DISABLE_BITMAP_CACHE
58 
59 // The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
60 // (see ubuntu-font-licence-1.0.txt for details)
61 #include "gl_resources.h"
62 #include "gl_builtin_shaders.h"
63 using namespace KIGFX::BUILTIN_FONT;
64 
65 static void InitTesselatorCallbacks( GLUtesselator* aTesselator );
66 static const int glAttributes[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 8, 0 };
67 
68 wxGLContext* OPENGL_GAL::glMainContext = NULL;
69 int OPENGL_GAL::instanceCounter = 0;
70 GLuint OPENGL_GAL::fontTexture = 0;
71 bool OPENGL_GAL::isBitmapFontLoaded = false;
72 SHADER* OPENGL_GAL::shader = NULL;
73 
74 namespace KIGFX {
76 {
77 public:
79  {
80  }
81 
82  ~GL_BITMAP_CACHE();
83 
84  GLuint RequestBitmap( const BITMAP_BASE* aBitmap );
85 
86 private:
87 
89  {
90  GLuint id;
91  int w, h;
92  };
93 
94  GLuint cacheBitmap( const BITMAP_BASE* aBitmap );
95 
96  std::map<const BITMAP_BASE*, CACHED_BITMAP> m_bitmaps;
97 };
98 
99 };
100 
101 
102 GL_BITMAP_CACHE::~GL_BITMAP_CACHE()
103 {
104  for ( auto b = m_bitmaps.begin(); b != m_bitmaps.end(); ++b )
105  glDeleteTextures( 1, &b->second.id );
106 }
107 
108 
109 GLuint GL_BITMAP_CACHE::RequestBitmap( const BITMAP_BASE* aBitmap )
110 {
111  auto it = m_bitmaps.find( aBitmap) ;
112 
113  if ( it != m_bitmaps.end() )
114  {
115  // A bitmap is found in cache bitmap.
116  // Ensure the associated texture is still valide (can be destoyed somewhere)
117  if( glIsTexture( it->second.id ) )
118  return it->second.id;
119 
120  // else if not valid, it will be recreated.
121  }
122 
123  return cacheBitmap( aBitmap );
124 }
125 
126 
127 GLuint GL_BITMAP_CACHE::cacheBitmap( const BITMAP_BASE* aBitmap )
128 {
129  CACHED_BITMAP bmp;
130 
131  bmp.w = aBitmap->GetSizePixels().x;
132  bmp.h = aBitmap->GetSizePixels().y;
133 
134  // The bitmap size needs to be a multiple of 4.
135  // This is easiest to achieve by ensuring that each row
136  // has a multiple of 4 pixels
137  int extra_w = bmp.w % 4;
138 
139  if( extra_w )
140  extra_w = 4 - extra_w;
141 
142  GLuint textureID;
143  glGenTextures(1, &textureID);
144 
145  // make_unique initializes this to 0, so extra pixels are transparent
146  auto buf = std::make_unique<uint8_t[]>( ( bmp.w + extra_w ) * bmp.h * 4 );
147  auto imgData = const_cast<BITMAP_BASE*>( aBitmap )->GetImageData();
148 
149  for( int y = 0; y < bmp.h; y++ )
150  {
151  for( int x = 0; x < bmp.w; x++ )
152  {
153  uint8_t *p = buf.get() + ( ( bmp.w + extra_w ) * y + x ) * 4;
154 
155  p[0] = imgData->GetRed( x, y );
156  p[1] = imgData->GetGreen( x, y );
157  p[2] = imgData->GetBlue( x, y );
158 
159  if( imgData->HasAlpha() )
160  p[3] = imgData->GetAlpha( x, y );
161  else
162  p[3] = 255;
163  }
164  }
165 
166  glBindTexture( GL_TEXTURE_2D, textureID );
167  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, bmp.w + extra_w, bmp.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf.get() );
168 
169  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
170  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
171 
172  bmp.id = textureID;
173 
174 #ifndef DISABLE_BITMAP_CACHE
175  m_bitmaps[ aBitmap ] = bmp;
176 #endif
177 
178  return textureID;
179 }
180 
181 OPENGL_GAL::OPENGL_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
182  wxEvtHandler* aMouseListener, wxEvtHandler* aPaintListener,
183  const wxString& aName ) :
184  GAL( aDisplayOptions ),
185  HIDPI_GL_CANVAS( aParent, wxID_ANY, (int*) glAttributes, wxDefaultPosition, wxDefaultSize,
186  wxEXPAND, aName ),
187  mouseListener( aMouseListener ),
188  paintListener( aPaintListener ),
189  currentManager( nullptr ),
190  cachedManager( nullptr ),
191  nonCachedManager( nullptr ),
192  overlayManager( nullptr ),
193  mainBuffer( 0 ),
194  overlayBuffer( 0 ),
195  isContextLocked( false ),
196  lockClientCookie( 0 )
197 {
198 // IsDisplayAttr() handles WX_GL_{MAJOR,MINOR}_VERSION correctly only in 3.0.4
199 // starting with 3.1.0 one should use wxGLContext::IsOk() (done by GL_CONTEXT_MANAGER)
200 #if wxCHECK_VERSION( 3, 0, 3 ) and !wxCHECK_VERSION( 3, 1, 0 )
201  const int attr[] = { WX_GL_MAJOR_VERSION, 2, WX_GL_MINOR_VERSION, 1, 0 };
202 
203  if( !IsDisplaySupported( attr ) )
204  throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
205 #endif /* wxCHECK_VERSION( 3, 0, 3 ) */
206 
207  if( glMainContext == NULL )
208  {
210 
211  if( !glMainContext )
212  throw std::runtime_error( "Could not create the main OpenGL context" );
213 
215  shader = new SHADER();
216  }
217  else
218  {
220 
221  if( !glPrivContext )
222  throw std::runtime_error( "Could not create a private OpenGL context" );
223  }
224 
225  ++instanceCounter;
226 
227  bitmapCache.reset( new GL_BITMAP_CACHE );
228 
231 
232  // Initialize the flags
233  isFramebufferInitialized = false;
234  isBitmapFontInitialized = false;
235  isInitialized = false;
236  isGrouping = false;
237  groupCounter = 0;
238 
239  // Connecting the event handlers
240  Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::onPaint ) );
241 
242  // Mouse events are skipped to the parent
243  Connect( wxEVT_MOTION, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
244  Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
245  Connect( wxEVT_LEFT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
246  Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
247  Connect( wxEVT_MIDDLE_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
248  Connect( wxEVT_MIDDLE_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
249  Connect( wxEVT_MIDDLE_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
250  Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
251  Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
252  Connect( wxEVT_RIGHT_DCLICK, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
253  Connect( wxEVT_MOUSEWHEEL, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
254 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
255  Connect( wxEVT_MAGNIFY, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
256 #endif
257 #if defined _WIN32 || defined _WIN64
258  Connect( wxEVT_ENTER_WINDOW, wxMouseEventHandler( OPENGL_GAL::skipMouseEvent ) );
259 #endif
260 
261  SetSize( aParent->GetClientSize() );
262  screenSize = VECTOR2I( aParent->GetClientSize() );
263 
264  // Grid color settings are different in Cairo and OpenGL
265  SetGridColor( COLOR4D( 0.8, 0.8, 0.8, 0.1 ) );
266  SetAxesColor( COLOR4D( BLUE ) );
267 
268  // Tesselator initialization
269  tesselator = gluNewTess();
271 
272  if( tesselator == NULL )
273  throw std::runtime_error( "Could not create the tesselator" );
274 
275  gluTessProperty( tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
276 
278 }
279 
280 
282 {
284 
285  --instanceCounter;
286  glFlush();
287  gluDeleteTess( tesselator );
288  ClearCache();
289 
290  delete compositor;
291 
292  if( isInitialized )
293  {
294  delete cachedManager;
295  delete nonCachedManager;
296  delete overlayManager;
297  }
298 
300 
301  // If it was the main context, then it will be deleted
302  // when the last OpenGL GAL instance is destroyed (a few lines below)
305 
306  // Are we destroying the last GAL instance?
307  if( instanceCounter == 0 )
308  {
310 
311  if( isBitmapFontLoaded )
312  {
313  glDeleteTextures( 1, &fontTexture );
314  isBitmapFontLoaded = false;
315  }
316 
317  delete shader;
318 
321  glMainContext = NULL;
322  }
323 }
324 
325 
327 {
328  bool refresh = false;
329 
331  {
333  isFramebufferInitialized = false;
334  refresh = true;
335  }
336 
337  if( super::updatedGalDisplayOptions( aOptions ) || refresh )
338  {
339  Refresh();
340  refresh = true;
341  }
342 
343  return refresh;
344 }
345 
347 {
348  auto matrix = GetScreenWorldMatrix();
349  return std::min( std::abs( matrix.GetScale().x ), std::abs( matrix.GetScale().y ) );
350 }
351 
353 {
354 #ifdef __WXDEBUG__
355  PROF_COUNTER totalRealTime( "OPENGL_GAL::beginDrawing()", true );
356 #endif /* __WXDEBUG__ */
357 
358  wxASSERT_MSG( isContextLocked, "GAL_DRAWING_CONTEXT RAII object should have locked context. "
359  "Calling GAL::beginDrawing() directly is not allowed." );
360 
361  wxASSERT_MSG( IsVisible(), "GAL::beginDrawing() must not be entered when GAL is not visible. "
362  "Other drawing routines will expect everything to be initialized "
363  "which will not be the case." );
364 
365  if( !isInitialized )
366  init();
367 
368  // Set up the view port
369  glMatrixMode( GL_PROJECTION );
370  glLoadIdentity();
371 
372  // Create the screen transformation (Do the RH-LH conversion here)
373  glOrtho( 0, (GLint) screenSize.x, (GLsizei) screenSize.y, 0, -depthRange.x, -depthRange.y );
374 
376  {
377  // Prepare rendering target buffers
381 
383  }
384 
385  compositor->Begin();
386 
387  // Disable 2D Textures
388  glDisable( GL_TEXTURE_2D );
389 
390  glShadeModel( GL_FLAT );
391 
392  // Enable the depth buffer
393  glEnable( GL_DEPTH_TEST );
394  glDepthFunc( GL_LESS );
395 
396  // Setup blending, required for transparent objects
397  glEnable( GL_BLEND );
398  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
399 
400  glMatrixMode( GL_MODELVIEW );
401 
402  // Set up the world <-> screen transformation
404  GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
405  matrixData[0] = worldScreenMatrix.m_data[0][0];
406  matrixData[1] = worldScreenMatrix.m_data[1][0];
407  matrixData[2] = worldScreenMatrix.m_data[2][0];
408  matrixData[4] = worldScreenMatrix.m_data[0][1];
409  matrixData[5] = worldScreenMatrix.m_data[1][1];
410  matrixData[6] = worldScreenMatrix.m_data[2][1];
411  matrixData[12] = worldScreenMatrix.m_data[0][2];
412  matrixData[13] = worldScreenMatrix.m_data[1][2];
413  matrixData[14] = worldScreenMatrix.m_data[2][2];
414  glLoadMatrixd( matrixData );
415 
416  // Set defaults
419 
420  // Remove all previously stored items
423 
427 
429  {
430  // Keep bitmap font texture always bound to the second texturing unit
431  const GLint FONT_TEXTURE_UNIT = 2;
432 
433  // Either load the font atlas to video memory, or simply bind it to a texture unit
434  if( !isBitmapFontLoaded )
435  {
436  glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
437  glGenTextures( 1, &fontTexture );
438  glBindTexture( GL_TEXTURE_2D, fontTexture );
439  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, font_image.width, font_image.height,
440  0, GL_RGB, GL_UNSIGNED_BYTE, font_image.pixels );
441  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
442  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
443  checkGlError( "loading bitmap font" );
444 
445  glActiveTexture( GL_TEXTURE0 );
446 
447  isBitmapFontLoaded = true;
448  }
449  else
450  {
451  glActiveTexture( GL_TEXTURE0 + FONT_TEXTURE_UNIT );
452  glBindTexture( GL_TEXTURE_2D, fontTexture );
453  glActiveTexture( GL_TEXTURE0 );
454  }
455 
456  // Set shader parameter
457  GLint ufm_fontTexture = shader->AddParameter( "fontTexture" );
458  GLint ufm_fontTextureWidth = shader->AddParameter( "fontTextureWidth" );
459  ufm_worldPixelSize = shader->AddParameter( "worldPixelSize" );
460 
461  shader->Use();
462  shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT );
463  shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width );
464  shader->Deactivate();
465  checkGlError( "setting bitmap font sampler as shader parameter" );
466 
468  }
469 
470  shader->Use();
472  shader->Deactivate();
473 
474  // Something betreen BeginDrawing and EndDrawing seems to depend on
475  // this texture unit being active, but it does not assure it itself.
476  glActiveTexture( GL_TEXTURE0 );
477 
478  // Unbind buffers - set compositor for direct drawing
480 
481 #ifdef __WXDEBUG__
482  totalRealTime.Stop();
483  wxLogTrace( "GAL_PROFILE", wxT( "OPENGL_GAL::beginDrawing(): %.1f ms" ), totalRealTime.msecs() );
484 #endif /* __WXDEBUG__ */
485 }
486 
487 
489 {
490  wxASSERT_MSG( isContextLocked, "What happened to the context lock?" );
491 
492 #ifdef __WXDEBUG__
493  PROF_COUNTER totalRealTime( "OPENGL_GAL::endDrawing()", true );
494 #endif /* __WXDEBUG__ */
495 
496  // Cached & non-cached containers are rendered to the same buffer
500 
501  // Overlay container is rendered to a different buffer
504 
505  // Be sure that the framebuffer is not colorized (happens on specific GPU&drivers combinations)
506  glColor4d( 1.0, 1.0, 1.0, 1.0 );
507 
508  // Draw the remaining contents, blit the rendering targets to the screen, swap the buffers
511  compositor->Present();
512  blitCursor();
513 
514  SwapBuffers();
515 
516 #ifdef __WXDEBUG__
517  totalRealTime.Stop();
518  wxLogTrace( "GAL_PROFILE", wxT( "OPENGL_GAL::endDrawing(): %.1f ms" ), totalRealTime.msecs() );
519 #endif /* __WXDEBUG__ */
520 }
521 
522 
523 void OPENGL_GAL::lockContext( int aClientCookie )
524 {
525  wxASSERT_MSG( !isContextLocked, "Context already locked." );
526  isContextLocked = true;
527  lockClientCookie = aClientCookie;
528 
530 }
531 
532 
533 void OPENGL_GAL::unlockContext( int aClientCookie )
534 {
535  wxASSERT_MSG( isContextLocked, "Context not locked. A GAL_CONTEXT_LOCKER RAII object must "
536  "be stacked rather than making separate lock/unlock calls." );
537 
538  wxASSERT_MSG( lockClientCookie == aClientCookie, "Context was locked by a different client. "
539  "Should not be possible with RAII objects." );
540 
541  isContextLocked = false;
542 
544 }
545 
546 
548 {
549  wxASSERT_MSG( isContextLocked, "GAL_UPDATE_CONTEXT RAII object should have locked context. "
550  "Calling this from anywhere else is not allowed." );
551 
552  wxASSERT_MSG( IsVisible(), "GAL::beginUpdate() must not be entered when GAL is not visible. "
553  "Other update routines will expect everything to be initialized "
554  "which will not be the case." );
555 
556  if( !isInitialized )
557  init();
558 
559  cachedManager->Map();
560 }
561 
562 
564 {
565  if( !isInitialized )
566  return;
567 
568  cachedManager->Unmap();
569 }
570 
571 
572 void OPENGL_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
573 {
575 
576  drawLineQuad( aStartPoint, aEndPoint );
577 }
578 
579 
580 void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
581  double aWidth )
582 {
583  if( aStartPoint == aEndPoint ) // 0 length segments are just a circle.
584  {
585  DrawCircle( aStartPoint, aWidth/2 );
586  return;
587  }
588 
589  if( isFillEnabled || aWidth == 1.0 )
590  {
592 
593  SetLineWidth( aWidth );
594  drawLineQuad( aStartPoint, aEndPoint );
595  }
596  else
597  {
598  auto startEndVector = aEndPoint - aStartPoint;
599  auto lineAngle = startEndVector.Angle();
600  // Outlined tracks
601  double lineLength = startEndVector.EuclideanNorm();
602 
604 
605  Save();
606 
607  currentManager->Translate( aStartPoint.x, aStartPoint.y, 0.0 );
608  currentManager->Rotate( lineAngle, 0.0f, 0.0f, 1.0f );
609 
610  drawLineQuad( VECTOR2D( 0.0, aWidth / 2.0 ),
611  VECTOR2D( lineLength, aWidth / 2.0 ) );
612 
613  drawLineQuad( VECTOR2D( 0.0, -aWidth / 2.0 ),
614  VECTOR2D( lineLength, -aWidth / 2.0 ) );
615 
616  // Draw line caps
617  drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 );
618  drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 );
619 
620  Restore();
621  }
622 }
623 
624 
625 void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
626 {
627  if( isFillEnabled )
628  {
629  currentManager->Reserve( 3 );
631 
632  /* Draw a triangle that contains the circle, then shade it leaving only the circle.
633  * Parameters given to Shader() are indices of the triangle's vertices
634  * (if you want to understand more, check the vertex shader source [shader.vert]).
635  * Shader uses this coordinates to determine if fragments are inside the circle or not.
636  * v2
637  * /\
638  * //\\
639  * v0 /_\/_\ v1
640  */
642  currentManager->Vertex( aCenterPoint.x - aRadius * sqrt( 3.0f ), // v0
643  aCenterPoint.y - aRadius, layerDepth );
644 
646  currentManager->Vertex( aCenterPoint.x + aRadius * sqrt( 3.0f), // v1
647  aCenterPoint.y - aRadius, layerDepth );
648 
650  currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, // v2
651  layerDepth );
652  }
653 
654  if( isStrokeEnabled )
655  {
656  currentManager->Reserve( 3 );
658 
659  /* Draw a triangle that contains the circle, then shade it leaving only the circle.
660  * Parameters given to Shader() are indices of the triangle's vertices
661  * (if you want to understand more, check the vertex shader source [shader.vert]).
662  * and the line width. Shader uses this coordinates to determine if fragments are
663  * inside the circle or not.
664  * v2
665  * /\
666  * //\\
667  * v0 /_\/_\ v1
668  */
669  double outerRadius = aRadius + ( lineWidth / 2 );
671  currentManager->Vertex( aCenterPoint.x - outerRadius * sqrt( 3.0f ), // v0
672  aCenterPoint.y - outerRadius, layerDepth );
673 
675  currentManager->Vertex( aCenterPoint.x + outerRadius * sqrt( 3.0f ), // v1
676  aCenterPoint.y - outerRadius, layerDepth );
677 
679  currentManager->Vertex( aCenterPoint.x, aCenterPoint.y + outerRadius * 2.0f, // v2
680  layerDepth );
681  }
682 }
683 
684 
685 void OPENGL_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
686  double aEndAngle )
687 {
688  if( aRadius <= 0 )
689  return;
690 
691  // Swap the angles, if start angle is greater than end angle
692  SWAP( aStartAngle, >, aEndAngle );
693 
694  const double alphaIncrement = calcAngleStep( aRadius );
695 
696  Save();
697  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
698 
699  if( isFillEnabled )
700  {
701  double alpha;
704 
705  // Triangle fan
706  for( alpha = aStartAngle; ( alpha + alphaIncrement ) < aEndAngle; )
707  {
708  currentManager->Reserve( 3 );
709  currentManager->Vertex( 0.0, 0.0, layerDepth );
710  currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, layerDepth );
711  alpha += alphaIncrement;
712  currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, layerDepth );
713  }
714 
715  // The last missing triangle
716  const VECTOR2D endPoint( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
717 
718  currentManager->Reserve( 3 );
719  currentManager->Vertex( 0.0, 0.0, layerDepth );
720  currentManager->Vertex( cos( alpha ) * aRadius, sin( alpha ) * aRadius, layerDepth );
721  currentManager->Vertex( endPoint.x, endPoint.y, layerDepth );
722  }
723 
724  if( isStrokeEnabled )
725  {
727 
728  VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius );
729  double alpha;
730 
731  for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
732  {
733  VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
734  DrawLine( p, p_next );
735 
736  p = p_next;
737  }
738 
739  // Draw the last missing part
740  if( alpha != aEndAngle )
741  {
742  VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
743  DrawLine( p, p_last );
744  }
745  }
746 
747  Restore();
748 }
749 
750 
751 void OPENGL_GAL::DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle,
752  double aEndAngle, double aWidth )
753 {
754  if( aRadius <= 0 )
755  {
756  // Arcs of zero radius are a circle of aWidth diameter
757  if( aWidth > 0 )
758  DrawCircle( aCenterPoint, aWidth / 2.0 );
759 
760  return;
761  }
762 
763  // Swap the angles, if start angle is greater than end angle
764  SWAP( aStartAngle, >, aEndAngle );
765 
766  const double alphaIncrement = calcAngleStep( aRadius );
767 
768  Save();
769  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0 );
770 
771  if( isStrokeEnabled )
772  {
774 
775  double width = aWidth / 2.0;
776  VECTOR2D startPoint( cos( aStartAngle ) * aRadius,
777  sin( aStartAngle ) * aRadius );
778  VECTOR2D endPoint( cos( aEndAngle ) * aRadius,
779  sin( aEndAngle ) * aRadius );
780 
781  drawStrokedSemiCircle( startPoint, width, aStartAngle + M_PI );
782  drawStrokedSemiCircle( endPoint, width, aEndAngle );
783 
784  VECTOR2D pOuter( cos( aStartAngle ) * ( aRadius + width ),
785  sin( aStartAngle ) * ( aRadius + width ) );
786 
787  VECTOR2D pInner( cos( aStartAngle ) * ( aRadius - width ),
788  sin( aStartAngle ) * ( aRadius - width ) );
789 
790  double alpha;
791 
792  for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
793  {
794  VECTOR2D pNextOuter( cos( alpha ) * ( aRadius + width ),
795  sin( alpha ) * ( aRadius + width ) );
796  VECTOR2D pNextInner( cos( alpha ) * ( aRadius - width ),
797  sin( alpha ) * ( aRadius - width ) );
798 
799  DrawLine( pOuter, pNextOuter );
800  DrawLine( pInner, pNextInner );
801 
802  pOuter = pNextOuter;
803  pInner = pNextInner;
804  }
805 
806  // Draw the last missing part
807  if( alpha != aEndAngle )
808  {
809  VECTOR2D pLastOuter( cos( aEndAngle ) * ( aRadius + width ),
810  sin( aEndAngle ) * ( aRadius + width ) );
811  VECTOR2D pLastInner( cos( aEndAngle ) * ( aRadius - width ),
812  sin( aEndAngle ) * ( aRadius - width ) );
813 
814  DrawLine( pOuter, pLastOuter );
815  DrawLine( pInner, pLastInner );
816  }
817  }
818 
819  if( isFillEnabled )
820  {
822  SetLineWidth( aWidth );
823 
824  VECTOR2D p( cos( aStartAngle ) * aRadius, sin( aStartAngle ) * aRadius );
825  double alpha;
826 
827  for( alpha = aStartAngle + alphaIncrement; alpha <= aEndAngle; alpha += alphaIncrement )
828  {
829  VECTOR2D p_next( cos( alpha ) * aRadius, sin( alpha ) * aRadius );
830  DrawLine( p, p_next );
831 
832  p = p_next;
833  }
834 
835  // Draw the last missing part
836  if( alpha != aEndAngle )
837  {
838  VECTOR2D p_last( cos( aEndAngle ) * aRadius, sin( aEndAngle ) * aRadius );
839  DrawLine( p, p_last );
840  }
841  }
842 
843  Restore();
844 }
845 
846 
847 void OPENGL_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
848 {
849  // Compute the diagonal points of the rectangle
850  VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y );
851  VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
852 
853  // Fill the rectangle
854  if( isFillEnabled )
855  {
856  currentManager->Reserve( 6 );
859 
860  currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
861  currentManager->Vertex( diagonalPointA.x, diagonalPointA.y, layerDepth );
862  currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
863 
864  currentManager->Vertex( aStartPoint.x, aStartPoint.y, layerDepth );
865  currentManager->Vertex( aEndPoint.x, aEndPoint.y, layerDepth );
866  currentManager->Vertex( diagonalPointB.x, diagonalPointB.y, layerDepth );
867  }
868 
869  // Stroke the outline
870  if( isStrokeEnabled )
871  {
873 
874  std::deque<VECTOR2D> pointList;
875  pointList.push_back( aStartPoint );
876  pointList.push_back( diagonalPointA );
877  pointList.push_back( aEndPoint );
878  pointList.push_back( diagonalPointB );
879  pointList.push_back( aStartPoint );
880  DrawPolyline( pointList );
881  }
882 }
883 
884 
885 void OPENGL_GAL::DrawPolyline( const std::deque<VECTOR2D>& aPointList )
886 {
887  drawPolyline( [&](int idx) { return aPointList[idx]; }, aPointList.size() );
888 }
889 
890 
891 void OPENGL_GAL::DrawPolyline( const VECTOR2D aPointList[], int aListSize )
892 {
893  drawPolyline( [&](int idx) { return aPointList[idx]; }, aListSize );
894 }
895 
896 
898 {
899  auto numPoints = aLineChain.PointCount();
900 
901  if( aLineChain.IsClosed() )
902  numPoints += 1;
903 
904  drawPolyline( [&](int idx) { return aLineChain.CPoint(idx); }, numPoints );
905 }
906 
907 
908 void OPENGL_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
909 {
910  auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aPointList.size()] );
911  GLdouble* ptr = points.get();
912 
913  for( const VECTOR2D& p : aPointList )
914  {
915  *ptr++ = p.x;
916  *ptr++ = p.y;
917  *ptr++ = layerDepth;
918  }
919 
920  drawPolygon( points.get(), aPointList.size() );
921 }
922 
923 
924 void OPENGL_GAL::DrawPolygon( const VECTOR2D aPointList[], int aListSize )
925 {
926  auto points = std::unique_ptr<GLdouble[]>( new GLdouble[3 * aListSize] );
927  GLdouble* target = points.get();
928  const VECTOR2D* src = aPointList;
929 
930  for( int i = 0; i < aListSize; ++i )
931  {
932  *target++ = src->x;
933  *target++ = src->y;
934  *target++ = layerDepth;
935  ++src;
936  }
937 
938  drawPolygon( points.get(), aListSize );
939 }
940 
941 
943 {
946 
947  if( isFillEnabled )
948  {
949  for( unsigned int j = 0; j < aPolySet.TriangulatedPolyCount(); ++j )
950  {
951  auto triPoly = aPolySet.TriangulatedPolygon( j );
952 
953  for( size_t i = 0; i < triPoly->GetTriangleCount(); i++ )
954  {
955  VECTOR2I a, b, c;
956  triPoly->GetTriangle( i, a, b, c );
957  currentManager->Vertex( a.x, a.y, layerDepth );
958  currentManager->Vertex( b.x, b.y, layerDepth );
959  currentManager->Vertex( c.x, c.y, layerDepth );
960  }
961  }
962  }
963 
964  if( isStrokeEnabled )
965  {
966  for( int j = 0; j < aPolySet.OutlineCount(); ++j )
967  {
968  const auto& poly = aPolySet.Polygon( j );
969 
970  for( const auto& lc : poly )
971  {
972  DrawPolyline( lc );
973  }
974  }
975  }
976 }
977 
978 
980 {
981  if ( aPolySet.IsTriangulationUpToDate() )
982  {
983  drawTriangulatedPolyset( aPolySet );
984  return;
985  }
986 
987  for( int j = 0; j < aPolySet.OutlineCount(); ++j )
988  {
989  const SHAPE_LINE_CHAIN& outline = aPolySet.COutline( j );
990  DrawPolygon( outline );
991  }
992 }
993 
994 
995 
997 {
998  if( aPolygon.SegmentCount() == 0 )
999  return;
1000 
1001  const int pointCount = aPolygon.SegmentCount() + 1;
1002  std::unique_ptr<GLdouble[]> points( new GLdouble[3 * pointCount] );
1003  GLdouble* ptr = points.get();
1004 
1005  for( int i = 0; i < pointCount; ++i )
1006  {
1007  const VECTOR2I& p = aPolygon.CPoint( i );
1008  *ptr++ = p.x;
1009  *ptr++ = p.y;
1010  *ptr++ = layerDepth;
1011  }
1012 
1013  drawPolygon( points.get(), pointCount );
1014 }
1015 
1016 
1017 void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
1018  const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint )
1019 {
1020  // FIXME The drawing quality needs to be improved
1021  // FIXME Perhaps choose a quad/triangle strip instead?
1022  // FIXME Brute force method, use a better (recursive?) algorithm
1023 
1024  std::deque<VECTOR2D> pointList;
1025 
1026  double t = 0.0;
1027  double dt = 1.0 / (double) CURVE_POINTS;
1028 
1029  for( int i = 0; i <= CURVE_POINTS; i++ )
1030  {
1031  double omt = 1.0 - t;
1032  double omt2 = omt * omt;
1033  double omt3 = omt * omt2;
1034  double t2 = t * t;
1035  double t3 = t * t2;
1036 
1037  VECTOR2D vertex = omt3 * aStartPoint + 3.0 * t * omt2 * aControlPointA
1038  + 3.0 * t2 * omt * aControlPointB + t3 * aEndPoint;
1039 
1040  pointList.push_back( vertex );
1041 
1042  t += dt;
1043  }
1044 
1045  DrawPolyline( pointList );
1046 }
1047 
1048 
1049 void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap )
1050 {
1051  // We have to calculate the pixel size in users units to draw the image.
1052  // worldUnitLength is a factor used for converting IU to inches
1053  double scale = 1.0 / ( aBitmap.GetPPI() * worldUnitLength );
1054  double w = (double) aBitmap.GetSizePixels().x * scale;
1055  double h = (double) aBitmap.GetSizePixels().y * scale;
1056 
1057  auto xform = currentManager->GetTransformation();
1058 
1059  glm::vec4 v0 = xform * glm::vec4( -w/2, -h/2, 0.0, 0.0 );
1060  glm::vec4 v1 = xform * glm::vec4( w/2, h/2, 0.0, 0.0 );
1061  glm::vec4 trans = xform[3];
1062 
1063  auto texture_id = bitmapCache->RequestBitmap( &aBitmap );
1064 
1065  if( !glIsTexture( texture_id ) ) // ensure the bitmap texture is still valid
1066  return;
1067 
1068  auto oldTarget = GetTarget();
1069 
1070  glPushMatrix();
1071  glTranslated( trans.x, trans.y, trans.z );
1072 
1074  glEnable(GL_TEXTURE_2D);
1075  glActiveTexture( GL_TEXTURE0 );
1076  glBindTexture( GL_TEXTURE_2D, texture_id );
1077 
1078  glBegin( GL_QUADS );
1079  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1080  glTexCoord2f( 0.0, 0.0 );
1081  glVertex3f( v0.x, v0.y, layerDepth );
1082  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1083  glTexCoord2f( 1.0, 0.0 );
1084  glVertex3f( v1.x, v0.y, layerDepth );
1085  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1086  glTexCoord2f( 1.0, 1.0 );
1087  glVertex3f( v1.x, v1.y, layerDepth );
1088  glColor4f( 1.0, 1.0, 1.0, 1.0 );
1089  glTexCoord2f( 0.0, 1.0 );
1090  glVertex3f( v0.x, v1.y, layerDepth );
1091  glEnd();
1092 
1093  SetTarget( oldTarget );
1094  glBindTexture( GL_TEXTURE_2D, 0 );
1095 
1096 #ifdef DISABLE_BITMAP_CACHE
1097  glDeleteTextures( 1, &texture_id );
1098 #endif
1099 
1100  glPopMatrix();
1101 }
1102 
1103 
1104 void OPENGL_GAL::BitmapText( const wxString& aText, const VECTOR2D& aPosition,
1105  double aRotationAngle )
1106 {
1107  wxASSERT_MSG( !IsTextMirrored(), "No support for mirrored text using bitmap fonts." );
1108 
1109  auto processedText = ProcessOverbars( aText );
1110  const auto& text = processedText.first;
1111  const auto& overbars = processedText.second;
1112 
1113  // Compute text size, so it can be properly justified
1114  VECTOR2D textSize;
1115  float commonOffset;
1116  std::tie( textSize, commonOffset ) = computeBitmapTextSize( text );
1117 
1118  const double SCALE = 1.4 * GetGlyphSize().y / textSize.y;
1119  bool overbar = false;
1120 
1121  int overbarLength = 0;
1122  double overbarHeight = textSize.y;
1123 
1124  Save();
1125 
1127  currentManager->Translate( aPosition.x, aPosition.y, layerDepth );
1128  currentManager->Rotate( aRotationAngle, 0.0f, 0.0f, -1.0f );
1129 
1130  double sx = SCALE * ( globalFlipX ? -1.0 : 1.0 );
1131  double sy = SCALE * ( globalFlipY ? -1.0 : 1.0 );
1132 
1133  currentManager->Scale( sx, sy, 0 );
1134  currentManager->Translate( 0, -commonOffset, 0 );
1135 
1136  switch( GetHorizontalJustify() )
1137  {
1139  Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
1140  break;
1141 
1143  //if( !IsTextMirrored() )
1144  Translate( VECTOR2D( -textSize.x, 0 ) );
1145  break;
1146 
1147  case GR_TEXT_HJUSTIFY_LEFT:
1148  //if( IsTextMirrored() )
1149  //Translate( VECTOR2D( -textSize.x, 0 ) );
1150  break;
1151  }
1152 
1153  switch( GetVerticalJustify() )
1154  {
1155  case GR_TEXT_VJUSTIFY_TOP:
1156  Translate( VECTOR2D( 0, -textSize.y ) );
1157  overbarHeight = -textSize.y / 2.0;
1158  break;
1159 
1161  Translate( VECTOR2D( 0, -textSize.y / 2.0 ) );
1162  overbarHeight = 0;
1163  break;
1164 
1166  break;
1167  }
1168 
1169  int i = 0;
1170 
1171  for( UTF8::uni_iter chIt = text.ubegin(), end = text.uend(); chIt < end; ++chIt )
1172  {
1173  unsigned int c = *chIt;
1174  wxASSERT_MSG( c != '\n' && c != '\r', wxT( "No support for multiline bitmap text yet" ) );
1175 
1176  // Handle overbar
1177  if( overbars[i] && !overbar )
1178  {
1179  overbar = true; // beginning of an overbar
1180  }
1181  else if( overbar && !overbars[i] )
1182  {
1183  overbar = false; // end of an overbar
1184  drawBitmapOverbar( overbarLength, overbarHeight );
1185  overbarLength = 0;
1186  }
1187 
1188  if( overbar )
1189  overbarLength += drawBitmapChar( c );
1190  else
1191  drawBitmapChar( c );
1192 
1193  ++i;
1194  }
1195 
1196  // Handle the case when overbar is active till the end of the drawn text
1197  currentManager->Translate( 0, commonOffset, 0 );
1198 
1199  if( overbar && overbarLength > 0 )
1200  drawBitmapOverbar( overbarLength, overbarHeight );
1201 
1202  Restore();
1203 }
1204 
1205 
1207 {
1210 
1212 
1213  // sub-pixel lines all render the same
1214  float minorLineWidth = std::fmax( 1.0f, gridLineWidth ) * getWorldPixelSize();
1215  float majorLineWidth = minorLineWidth * 2.0f;
1216 
1217  // Draw the axis and grid
1218  // For the drawing the start points, end points and increments have
1219  // to be calculated in world coordinates
1220  VECTOR2D worldStartPoint = screenWorldMatrix * VECTOR2D( 0.0, 0.0 );
1221  VECTOR2D worldEndPoint = screenWorldMatrix * VECTOR2D( screenSize );
1222 
1223  // Draw axes if desired
1224  if( axesEnabled )
1225  {
1226  SetLineWidth( minorLineWidth );
1228 
1229  DrawLine( VECTOR2D( worldStartPoint.x, 0 ), VECTOR2D( worldEndPoint.x, 0 ) );
1230  DrawLine( VECTOR2D( 0, worldStartPoint.y ), VECTOR2D( 0, worldEndPoint.y ) );
1231  }
1232 
1233  // force flush
1235 
1236  if( !gridVisibility )
1237  return;
1238 
1239  int gridScreenSizeDense = KiROUND( gridSize.x * worldScale );
1240  int gridScreenSizeCoarse = KiROUND( gridSize.x * static_cast<double>( gridTick ) * worldScale );
1241 
1242  const double gridThreshold = computeMinGridSpacing();
1243 
1244  // Check if the grid would not be too dense
1245  if( std::max( gridScreenSizeDense, gridScreenSizeCoarse ) < gridThreshold )
1246  return;
1247 
1248  // Compute grid staring and ending indexes to draw grid points on the
1249  // visible screen area
1250  // Note: later any point coordinate will be offsetted by gridOrigin
1251  int gridStartX = KiROUND( ( worldStartPoint.x - gridOrigin.x ) / gridSize.x );
1252  int gridEndX = KiROUND( ( worldEndPoint.x - gridOrigin.x ) / gridSize.x );
1253  int gridStartY = KiROUND( ( worldStartPoint.y - gridOrigin.y ) / gridSize.y );
1254  int gridEndY = KiROUND( ( worldEndPoint.y - gridOrigin.y ) / gridSize.y );
1255 
1256  // Ensure start coordinate > end coordinate
1257  if( gridStartX > gridEndX )
1258  std::swap( gridStartX, gridEndX );
1259 
1260  if( gridStartY > gridEndY )
1261  std::swap( gridStartY, gridEndY );
1262 
1263  // Ensure the grid fills the screen
1264  --gridStartX; ++gridEndX;
1265  --gridStartY; ++gridEndY;
1266 
1267  glDisable( GL_DEPTH_TEST );
1268  glDisable( GL_TEXTURE_2D );
1269 
1270  if( gridStyle == GRID_STYLE::DOTS )
1271  {
1272  glEnable( GL_STENCIL_TEST );
1273  glStencilFunc( GL_ALWAYS, 1, 1 );
1274  glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1275  glColor4d( 0.0, 0.0, 0.0, 0.0 );
1276  SetStrokeColor( COLOR4D( 0.0, 0.0, 0.0, 0.0 ) );
1277  }
1278  else
1279  {
1280  glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1282  }
1283 
1285  {
1286  SetLineWidth( minorLineWidth );
1287 
1288  // calculate a line len = 2 minorLineWidth, in internal unit value
1289  // (in fact the size of cross is lineLen*2)
1290  int lineLen = KiROUND( minorLineWidth * 2.0 );
1291 
1292  // Vertical positions
1293  for( int j = gridStartY; j <= gridEndY; j++ )
1294  {
1295  if( ( j % gridTick == 0 && gridScreenSizeCoarse > gridThreshold )
1296  || gridScreenSizeDense > gridThreshold )
1297  {
1298  int posY = j * gridSize.y + gridOrigin.y;
1299 
1300  // Horizontal positions
1301  for( int i = gridStartX; i <= gridEndX; i++ )
1302  {
1303  if( ( i % gridTick == 0 && gridScreenSizeCoarse > gridThreshold )
1304  || gridScreenSizeDense > gridThreshold )
1305  {
1306  int posX = i * gridSize.x + gridOrigin.x;
1307 
1308  DrawLine( VECTOR2D( posX - lineLen, posY ), VECTOR2D( posX + lineLen, posY ) );
1309  DrawLine( VECTOR2D( posX, posY - lineLen ), VECTOR2D( posX, posY + lineLen ) );
1310  }
1311  }
1312  }
1313  }
1314 
1316  }
1317  else
1318  {
1319  // Vertical lines
1320  for( int j = gridStartY; j <= gridEndY; j++ )
1321  {
1322  const double y = j * gridSize.y + gridOrigin.y;
1323 
1324  // If axes are drawn, skip the lines that would cover them
1325  if( axesEnabled && y == 0 )
1326  continue;
1327 
1328  if( j % gridTick == 0 && gridScreenSizeDense > gridThreshold )
1329  SetLineWidth( majorLineWidth );
1330  else
1331  SetLineWidth( minorLineWidth );
1332 
1333  if( ( j % gridTick == 0 && gridScreenSizeCoarse > gridThreshold )
1334  || gridScreenSizeDense > gridThreshold )
1335  {
1336  VECTOR2D a ( gridStartX * gridSize.x + gridOrigin.x, y );
1337  VECTOR2D b ( gridEndX * gridSize.x + gridOrigin.x, y );
1338 
1339  DrawLine( a, b );
1340  }
1341  }
1342 
1344 
1345  if( gridStyle == GRID_STYLE::DOTS )
1346  {
1347  glStencilFunc( GL_NOTEQUAL, 0, 1 );
1348  glColor4d( gridColor.r, gridColor.g, gridColor.b, gridColor.a );
1350  }
1351 
1352  // Horizontal lines
1353  for( int i = gridStartX; i <= gridEndX; i++ )
1354  {
1355  const double x = i * gridSize.x + gridOrigin.x;
1356 
1357  // If axes are drawn, skip the lines that would cover them
1358  if( axesEnabled && x == 0 )
1359  continue;
1360 
1361  if( i % gridTick == 0 && gridScreenSizeDense > gridThreshold )
1362  SetLineWidth( majorLineWidth );
1363  else
1364  SetLineWidth( minorLineWidth );
1365 
1366  if( ( i % gridTick == 0 && gridScreenSizeCoarse > gridThreshold )
1367  || gridScreenSizeDense > gridThreshold )
1368  {
1369  VECTOR2D a ( x, gridStartY * gridSize.y + gridOrigin.y );
1370  VECTOR2D b ( x, gridEndY * gridSize.y + gridOrigin.y );
1371  DrawLine( a, b );
1372  }
1373  }
1374 
1376 
1377  if( gridStyle == GRID_STYLE::DOTS )
1378  glDisable( GL_STENCIL_TEST );
1379  }
1380 
1381  glEnable( GL_DEPTH_TEST );
1382  glEnable( GL_TEXTURE_2D );
1383 }
1384 
1385 
1386 void OPENGL_GAL::ResizeScreen( int aWidth, int aHeight )
1387 {
1388  screenSize = VECTOR2I( aWidth, aHeight );
1389 
1390  // Resize framebuffers
1391  const float scaleFactor = GetBackingScaleFactor();
1392  compositor->Resize( aWidth * scaleFactor, aHeight * scaleFactor );
1393  isFramebufferInitialized = false;
1394 
1395  wxGLCanvas::SetSize( aWidth, aHeight );
1396 }
1397 
1398 
1399 bool OPENGL_GAL::Show( bool aShow )
1400 {
1401  bool s = wxGLCanvas::Show( aShow );
1402 
1403  if( aShow )
1404  wxGLCanvas::Raise();
1405 
1406  return s;
1407 }
1408 
1409 
1411 {
1412  glFlush();
1413 }
1414 
1415 
1417 {
1418  // Clear screen
1420  // NOTE: Black used here instead of m_clearColor; it will be composited later
1421  glClearColor( 0, 0, 0, 1 );
1422  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
1423 }
1424 
1425 
1426 void OPENGL_GAL::Transform( const MATRIX3x3D& aTransformation )
1427 {
1428  GLdouble matrixData[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
1429 
1430  matrixData[0] = aTransformation.m_data[0][0];
1431  matrixData[1] = aTransformation.m_data[1][0];
1432  matrixData[2] = aTransformation.m_data[2][0];
1433  matrixData[4] = aTransformation.m_data[0][1];
1434  matrixData[5] = aTransformation.m_data[1][1];
1435  matrixData[6] = aTransformation.m_data[2][1];
1436  matrixData[12] = aTransformation.m_data[0][2];
1437  matrixData[13] = aTransformation.m_data[1][2];
1438  matrixData[14] = aTransformation.m_data[2][2];
1439 
1440  glMultMatrixd( matrixData );
1441 }
1442 
1443 
1444 void OPENGL_GAL::Rotate( double aAngle )
1445 {
1446  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1447 }
1448 
1449 
1450 void OPENGL_GAL::Translate( const VECTOR2D& aVector )
1451 {
1452  currentManager->Translate( aVector.x, aVector.y, 0.0f );
1453 }
1454 
1455 
1456 void OPENGL_GAL::Scale( const VECTOR2D& aScale )
1457 {
1458  currentManager->Scale( aScale.x, aScale.y, 0.0f );
1459 }
1460 
1461 
1463 {
1465 }
1466 
1467 
1469 {
1471 }
1472 
1473 
1475 {
1476  isGrouping = true;
1477 
1478  std::shared_ptr<VERTEX_ITEM> newItem = std::make_shared<VERTEX_ITEM>( *cachedManager );
1479  int groupNumber = getNewGroupNumber();
1480  groups.insert( std::make_pair( groupNumber, newItem ) );
1481 
1482  return groupNumber;
1483 }
1484 
1485 
1487 {
1489  isGrouping = false;
1490 }
1491 
1492 
1493 void OPENGL_GAL::DrawGroup( int aGroupNumber )
1494 {
1495  if( groups[aGroupNumber] )
1496  cachedManager->DrawItem( *groups[aGroupNumber] );
1497 }
1498 
1499 
1500 void OPENGL_GAL::ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor )
1501 {
1502  if( groups[aGroupNumber] )
1503  cachedManager->ChangeItemColor( *groups[aGroupNumber], aNewColor );
1504 }
1505 
1506 
1507 void OPENGL_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth )
1508 {
1509  if( groups[aGroupNumber] )
1510  cachedManager->ChangeItemDepth( *groups[aGroupNumber], aDepth );
1511 }
1512 
1513 
1514 void OPENGL_GAL::DeleteGroup( int aGroupNumber )
1515 {
1516  // Frees memory in the container as well
1517  groups.erase( aGroupNumber );
1518 }
1519 
1520 
1522 {
1523  bitmapCache.reset( new GL_BITMAP_CACHE );
1524 
1525  groups.clear();
1526 
1527  if( isInitialized )
1528  cachedManager->Clear();
1529 }
1530 
1531 
1533 {
1534  wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
1535 }
1536 
1537 
1539 {
1540  wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
1541 }
1542 
1543 
1545 {
1546  switch( aTarget )
1547  {
1548  default:
1549  case TARGET_CACHED:
1551  break;
1552 
1553  case TARGET_NONCACHED:
1555  break;
1556 
1557  case TARGET_OVERLAY:
1559  break;
1560  }
1561 
1562  currentTarget = aTarget;
1563 }
1564 
1565 
1567 {
1568  return currentTarget;
1569 }
1570 
1571 
1573 {
1574  // Save the current state
1575  unsigned int oldTarget = compositor->GetBuffer();
1576 
1577  switch( aTarget )
1578  {
1579  // Cached and noncached items are rendered to the same buffer
1580  default:
1581  case TARGET_CACHED:
1582  case TARGET_NONCACHED:
1584  break;
1585 
1586  case TARGET_OVERLAY:
1588  break;
1589  }
1590 
1591 
1592  if( aTarget != TARGET_OVERLAY )
1594  else
1596 
1597  // Restore the previous state
1598  compositor->SetBuffer( oldTarget );
1599 }
1600 
1601 
1602 void OPENGL_GAL::DrawCursor( const VECTOR2D& aCursorPosition )
1603 {
1604  // Now we should only store the position of the mouse cursor
1605  // The real drawing routines are in blitCursor()
1606  //VECTOR2D screenCursor = worldScreenMatrix * aCursorPosition;
1607  //cursorPosition = screenWorldMatrix * VECTOR2D( screenCursor.x, screenCursor.y );
1608  cursorPosition = aCursorPosition;
1609 }
1610 
1611 
1612 void OPENGL_GAL::drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
1613 {
1614  /* Helper drawing: ____--- v3 ^
1615  * ____---- ... \ \
1616  * ____---- ... \ end \
1617  * v1 ____---- ... ____---- \ width
1618  * ---- ...___---- \ \
1619  * \ ___...-- \ v
1620  * \ ____----... ____---- v2
1621  * ---- ... ____----
1622  * start \ ... ____----
1623  * \... ____----
1624  * ----
1625  * v0
1626  * dots mark triangles' hypotenuses
1627  */
1628 
1629  auto v1 = currentManager->GetTransformation() * glm::vec4( aStartPoint.x, aStartPoint.y, 0.0, 0.0 );
1630  auto v2 = currentManager->GetTransformation() * glm::vec4( aEndPoint.x, aEndPoint.y, 0.0, 0.0 );
1631 
1632  VECTOR2D startEndVector( v2.x - v1.x, v2.y - v1.y );
1633 
1634  double lineLength = startEndVector.EuclideanNorm();
1635 
1636  VECTOR2D vs ( startEndVector );
1637  float aspect;
1638 
1639  if ( lineWidth == 0.0 ) // pixel-width line
1640  {
1641  vs = vs.Resize( 0.5 );
1642  aspect = ( lineLength + 1.0 );
1643  }
1644  else
1645  {
1646  vs = vs.Resize( 0.5 * lineWidth );
1647  aspect = ( lineLength + lineWidth ) / lineWidth;
1648  }
1649 
1650  currentManager->Reserve( 6 );
1651 
1652  // Line width is maintained by the vertex shader
1653  currentManager->Shader( SHADER_LINE_A, aspect, vs.x, vs.y );
1654  currentManager->Vertex( aStartPoint, layerDepth );
1655 
1656  currentManager->Shader( SHADER_LINE_B, aspect, vs.x, vs.y );
1657  currentManager->Vertex( aStartPoint, layerDepth );
1658 
1659  currentManager->Shader( SHADER_LINE_C, aspect, vs.x, vs.y );
1660  currentManager->Vertex( aEndPoint, layerDepth );
1661 
1662  currentManager->Shader( SHADER_LINE_D, aspect, vs.x, vs.y );
1663  currentManager->Vertex( aEndPoint, layerDepth );
1664 
1665  currentManager->Shader( SHADER_LINE_E, aspect, vs.x, vs.y );
1666  currentManager->Vertex( aEndPoint, layerDepth );
1667 
1668  currentManager->Shader( SHADER_LINE_F, aspect, vs.x, vs.y );
1669  currentManager->Vertex( aStartPoint, layerDepth );
1670 }
1671 
1672 
1673 void OPENGL_GAL::drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle )
1674 {
1675  if( isFillEnabled )
1676  {
1678  drawFilledSemiCircle( aCenterPoint, aRadius, aAngle );
1679  }
1680 
1681  if( isStrokeEnabled )
1682  {
1684  drawStrokedSemiCircle( aCenterPoint, aRadius, aAngle );
1685  }
1686 }
1687 
1688 
1689 void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
1690  double aAngle )
1691 {
1692  Save();
1693 
1694  currentManager->Reserve( 3 );
1695  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
1696  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1697 
1698  /* Draw a triangle that contains the semicircle, then shade it to leave only
1699  * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
1700  * (if you want to understand more, check the vertex shader source [shader.vert]).
1701  * Shader uses these coordinates to determine if fragments are inside the semicircle or not.
1702  * v2
1703  * /\
1704  * /__\
1705  * v0 //__\\ v1
1706  */
1708  currentManager->Vertex( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
1709 
1711  currentManager->Vertex( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
1712 
1714  currentManager->Vertex( 0.0f, aRadius * 2.0f, layerDepth ); // v2
1715 
1716  Restore();
1717 }
1718 
1719 
1720 void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius,
1721  double aAngle )
1722 {
1723  double outerRadius = aRadius + ( lineWidth / 2 );
1724 
1725  Save();
1726 
1727  currentManager->Reserve( 3 );
1728  currentManager->Translate( aCenterPoint.x, aCenterPoint.y, 0.0f );
1729  currentManager->Rotate( aAngle, 0.0f, 0.0f, 1.0f );
1730 
1731  /* Draw a triangle that contains the semicircle, then shade it to leave only
1732  * the semicircle. Parameters given to Shader() are indices of the triangle's vertices
1733  * (if you want to understand more, check the vertex shader source [shader.vert]), the
1734  * radius and the line width. Shader uses these coordinates to determine if fragments are
1735  * inside the semicircle or not.
1736  * v2
1737  * /\
1738  * /__\
1739  * v0 //__\\ v1
1740  */
1742  currentManager->Vertex( -outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0
1743 
1745  currentManager->Vertex( outerRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1
1746 
1748  currentManager->Vertex( 0.0f, outerRadius * 2.0f, layerDepth ); // v2
1749 
1750  Restore();
1751 }
1752 
1753 
1754 void OPENGL_GAL::drawPolygon( GLdouble* aPoints, int aPointCount )
1755 {
1756  if( isFillEnabled )
1757  {
1760 
1761  // Any non convex polygon needs to be tesselated
1762  // for this purpose the GLU standard functions are used
1764  gluTessBeginPolygon( tesselator, &params );
1765  gluTessBeginContour( tesselator );
1766 
1767  GLdouble* point = aPoints;
1768 
1769  for( int i = 0; i < aPointCount; ++i )
1770  {
1771  gluTessVertex( tesselator, point, point );
1772  point += 3; // 3 coordinates
1773  }
1774 
1775  gluTessEndContour( tesselator );
1776  gluTessEndPolygon( tesselator );
1777 
1778  // Free allocated intersecting points
1779  tessIntersects.clear();
1780  }
1781 
1782  if( isStrokeEnabled )
1783  {
1784  drawPolyline( [&](int idx) { return VECTOR2D( aPoints[idx * 3], aPoints[idx * 3 + 1] ); },
1785  aPointCount );
1786  }
1787 }
1788 
1789 
1790 void OPENGL_GAL::drawPolyline( const std::function<VECTOR2D (int)>& aPointGetter, int aPointCount )
1791 {
1792  if( aPointCount < 2 )
1793  return;
1794 
1796  int i;
1797 
1798  for( i = 1; i < aPointCount; ++i )
1799  {
1800  auto start = aPointGetter( i - 1 );
1801  auto end = aPointGetter( i );
1802 
1803  drawLineQuad( start, end );
1804  }
1805 }
1806 
1807 
1808 int OPENGL_GAL::drawBitmapChar( unsigned long aChar )
1809 {
1810  const float TEX_X = font_image.width;
1811  const float TEX_Y = font_image.height;
1812 
1813  // handle space
1814  if( aChar == ' ' )
1815  {
1816  const FONT_GLYPH_TYPE* g = LookupGlyph( 'x' );
1817  wxASSERT( g );
1818  Translate( VECTOR2D( g->advance, 0 ) );
1819  return g->advance;
1820  }
1821 
1822  const FONT_GLYPH_TYPE* glyph = LookupGlyph( aChar );
1823 
1824  // If the glyph is not found (happens for many esotheric unicode chars)
1825  // shows a '?' instead.
1826  if( !glyph )
1827  glyph = LookupGlyph( '?' );
1828 
1829  if( !glyph ) // Should not happen.
1830  return 0;
1831 
1832  const float X = glyph->atlas_x + font_information.smooth_pixels;
1833  const float Y = glyph->atlas_y + font_information.smooth_pixels;
1834  const float XOFF = glyph->minx;
1835 
1836  // adjust for height rounding
1837  const float round_adjust = ( glyph->maxy - glyph->miny )
1838  - float( glyph->atlas_h - font_information.smooth_pixels * 2 );
1839  const float top_adjust = font_information.max_y - glyph->maxy;
1840  const float YOFF = round_adjust + top_adjust;
1841  const float W = glyph->atlas_w - font_information.smooth_pixels *2;
1842  const float H = glyph->atlas_h - font_information.smooth_pixels *2;
1843  const float B = 0;
1844 
1845  currentManager->Reserve( 6 );
1846  Translate( VECTOR2D( XOFF, YOFF ) );
1847  /* Glyph:
1848  * v0 v1
1849  * +--+
1850  * | /|
1851  * |/ |
1852  * +--+
1853  * v2 v3
1854  */
1855  currentManager->Shader( SHADER_FONT, X / TEX_X, ( Y + H ) / TEX_Y );
1856  currentManager->Vertex( -B, -B, 0 ); // v0
1857 
1858  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
1859  currentManager->Vertex( W + B, -B, 0 ); // v1
1860 
1861  currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
1862  currentManager->Vertex( -B, H + B, 0 ); // v2
1863 
1864 
1865  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, ( Y + H ) / TEX_Y );
1866  currentManager->Vertex( W + B, -B, 0 ); // v1
1867 
1868  currentManager->Shader( SHADER_FONT, X / TEX_X, Y / TEX_Y );
1869  currentManager->Vertex( -B, H + B, 0 ); // v2
1870 
1871  currentManager->Shader( SHADER_FONT, ( X + W ) / TEX_X, Y / TEX_Y );
1872  currentManager->Vertex( W + B, H + B, 0 ); // v3
1873 
1874  Translate( VECTOR2D( -XOFF + glyph->advance, -YOFF ) );
1875 
1876  return glyph->advance;
1877 }
1878 
1879 
1880 void OPENGL_GAL::drawBitmapOverbar( double aLength, double aHeight )
1881 {
1882  // To draw an overbar, simply draw an overbar
1883  const FONT_GLYPH_TYPE* glyph = LookupGlyph( '_' );
1884  wxCHECK( glyph, /* void */ );
1885 
1886  const float H = glyph->maxy - glyph->miny;
1887 
1888  Save();
1889 
1890  Translate( VECTOR2D( -aLength, -aHeight-1.5*H ) );
1891 
1892  currentManager->Reserve( 6 );
1894 
1895  currentManager->Shader( 0 );
1896 
1897  currentManager->Vertex( 0, 0, 0 ); // v0
1898  currentManager->Vertex( aLength, 0, 0 ); // v1
1899  currentManager->Vertex( 0, H, 0 ); // v2
1900 
1901  currentManager->Vertex( aLength, 0, 0 ); // v1
1902  currentManager->Vertex( 0, H, 0 ); // v2
1903  currentManager->Vertex( aLength, H, 0 ); // v3
1904 
1905  Restore();
1906 }
1907 
1908 
1909 std::pair<VECTOR2D, float> OPENGL_GAL::computeBitmapTextSize( const UTF8& aText ) const
1910 {
1911  VECTOR2D textSize( 0, 0 );
1912  float commonOffset = std::numeric_limits<float>::max();
1913  static const auto defaultGlyph = LookupGlyph( '(' ); // for strange chars
1914 
1915  for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt )
1916  {
1917  unsigned int c = *chIt;
1918 
1919  const FONT_GLYPH_TYPE* glyph = LookupGlyph( c );
1920  // Debug: show not coded char in the atlas
1921  // Be carefull before allowing the assert: it usually crash kicad
1922  // when the assert is made during a paint event.
1923  // wxASSERT_MSG( glyph, wxString::Format( "missing char in font: code 0x%x <%c>", c, c ) );
1924 
1925  if( !glyph || // Not coded in font
1926  c == '-' || c == '_' ) // Strange size of these 2 chars
1927  {
1928  glyph = defaultGlyph;
1929  }
1930 
1931  if( glyph )
1932  {
1933  textSize.x += glyph->advance;
1934  }
1935  }
1936 
1937  textSize.y = std::max<float>( textSize.y, font_information.max_y - defaultGlyph->miny );
1938  commonOffset = std::min<float>( font_information.max_y - defaultGlyph->maxy, commonOffset );
1939  textSize.y -= commonOffset;
1940 
1941  return std::make_pair( textSize, commonOffset );
1942 }
1943 
1944 
1945 void OPENGL_GAL::onPaint( wxPaintEvent& WXUNUSED( aEvent ) )
1946 {
1947  PostPaint();
1948 }
1949 
1950 
1951 void OPENGL_GAL::skipMouseEvent( wxMouseEvent& aEvent )
1952 {
1953  // Post the mouse event to the event listener registered in constructor, if any
1954  if( mouseListener )
1955  wxPostEvent( mouseListener, aEvent );
1956 }
1957 
1958 
1960 {
1961  if( !IsCursorEnabled() )
1962  return;
1963 
1965 
1966  const int cursorSize = fullscreenCursor ? 8000 : 80;
1967 
1968  VECTOR2D cursorBegin = cursorPosition - cursorSize / ( 2 * worldScale );
1969  VECTOR2D cursorEnd = cursorPosition + cursorSize / ( 2 * worldScale );
1970  VECTOR2D cursorCenter = ( cursorBegin + cursorEnd ) / 2;
1971 
1972  const COLOR4D cColor = getCursorColor();
1973  const COLOR4D color( cColor.r * cColor.a, cColor.g * cColor.a,
1974  cColor.b * cColor.a, 1.0 );
1975 
1976  glActiveTexture( GL_TEXTURE0 );
1977  glDisable( GL_TEXTURE_2D );
1978  glLineWidth( 1.0 );
1979  glColor4d( color.r, color.g, color.b, color.a );
1980 
1981  glBegin( GL_LINES );
1982  glVertex2d( cursorCenter.x, cursorBegin.y );
1983  glVertex2d( cursorCenter.x, cursorEnd.y );
1984 
1985  glVertex2d( cursorBegin.x, cursorCenter.y );
1986  glVertex2d( cursorEnd.x, cursorCenter.y );
1987  glEnd();
1988 }
1989 
1990 
1992 {
1993  wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
1994  wxT( "There are no free slots to store a group" ) );
1995 
1996  while( groups.find( groupCounter ) != groups.end() )
1997  {
1998  groupCounter++;
1999  }
2000 
2001  return groupCounter++;
2002 }
2003 
2004 
2006 {
2007  wxASSERT( IsShownOnScreen() );
2008 
2009  wxASSERT_MSG( isContextLocked, "This should only be called from within a locked context." );
2010 
2011  GLenum err = glewInit();
2012 
2013  if( GLEW_OK != err )
2014  throw std::runtime_error( (const char*) glewGetErrorString( err ) );
2015 
2016  // Check the OpenGL version (minimum 2.1 is required)
2017  if( !GLEW_VERSION_2_1 )
2018  throw std::runtime_error( "OpenGL 2.1 or higher is required!" );
2019 
2020 #if defined (__LINUX__) // calling enableGlDebug crashes opengl on some OS (OSX and some Windows)
2021 #ifdef DEBUG
2022  if( GLEW_ARB_debug_output )
2023  enableGlDebug( true );
2024 #endif
2025 #endif
2026 
2027  // Framebuffers have to be supported
2028  if( !GLEW_EXT_framebuffer_object )
2029  throw std::runtime_error( "Framebuffer objects are not supported!" );
2030 
2031  // Vertex buffer has to be supported
2032  if( !GLEW_ARB_vertex_buffer_object )
2033  throw std::runtime_error( "Vertex buffer objects are not supported!" );
2034 
2035  // Prepare shaders
2037  throw std::runtime_error( "Cannot compile vertex shader!" );
2038 
2040  throw std::runtime_error( "Cannot compile fragment shader!" );
2041 
2042  if( !shader->IsLinked() && !shader->Link() )
2043  throw std::runtime_error( "Cannot link the shaders!" );
2044 
2045  // Check if video card supports textures big enough to fit the font atlas
2046  int maxTextureSize;
2047  glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize );
2048 
2049  if( maxTextureSize < (int) font_image.width || maxTextureSize < (int)font_image.height )
2050  {
2051  // TODO implement software texture scaling
2052  // for bitmap fonts and use a higher resolution texture?
2053  throw std::runtime_error( "Requested texture size is not supported" );
2054  }
2055 
2056  cachedManager = new VERTEX_MANAGER( true );
2057  nonCachedManager = new VERTEX_MANAGER( false );
2058  overlayManager = new VERTEX_MANAGER( false );
2059 
2060  // Make VBOs use shaders
2064 
2065  isInitialized = true;
2066 }
2067 
2068 
2069 // ------------------------------------- // Callback functions for the tesselator // ------------------------------------- // Compare Redbook Chapter 11
2070 void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData )
2071 {
2072  GLdouble* vertex = static_cast<GLdouble*>( aVertexPtr );
2073  OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2074  VERTEX_MANAGER* vboManager = param->vboManager;
2075 
2076  assert( vboManager );
2077  vboManager->Vertex( vertex[0], vertex[1], vertex[2] );
2078 }
2079 
2080 
2081 void CALLBACK CombineCallback( GLdouble coords[3],
2082  GLdouble* vertex_data[4],
2083  GLfloat weight[4], GLdouble** dataOut, void* aData )
2084 {
2085  GLdouble* vertex = new GLdouble[3];
2086  OPENGL_GAL::TessParams* param = static_cast<OPENGL_GAL::TessParams*>( aData );
2087 
2088  // Save the pointer so we can delete it later
2089  param->intersectPoints.push_back( boost::shared_array<GLdouble>( vertex ) );
2090 
2091  memcpy( vertex, coords, 3 * sizeof(GLdouble) );
2092 
2093  *dataOut = vertex;
2094 }
2095 
2096 
2097 void CALLBACK EdgeCallback( GLboolean aEdgeFlag )
2098 {
2099  // This callback is needed to force GLU tesselator to use triangles only
2100 }
2101 
2102 
2103 void CALLBACK ErrorCallback( GLenum aErrorCode )
2104 {
2105  //throw std::runtime_error( std::string( "Tessellation error: " ) +
2106  //std::string( (const char*) gluErrorString( aErrorCode ) );
2107 }
2108 
2109 
2110 static void InitTesselatorCallbacks( GLUtesselator* aTesselator )
2111 {
2112  gluTessCallback( aTesselator, GLU_TESS_VERTEX_DATA, ( void (CALLBACK*)() )VertexCallback );
2113  gluTessCallback( aTesselator, GLU_TESS_COMBINE_DATA, ( void (CALLBACK*)() )CombineCallback );
2114  gluTessCallback( aTesselator, GLU_TESS_EDGE_FLAG, ( void (CALLBACK*)() )EdgeCallback );
2115  gluTessCallback( aTesselator, GLU_TESS_ERROR, ( void (CALLBACK*)() )ErrorCallback );
2116 }
2117 
2118 void OPENGL_GAL::EnableDepthTest( bool aEnabled )
2119 {
2120  cachedManager->EnableDepthTest( aEnabled );
2121  nonCachedManager->EnableDepthTest( aEnabled );
2122  overlayManager->EnableDepthTest( aEnabled );
2123 }
Definition: colors.h:57
double computeMinGridSpacing() const
compute minimum grid spacing from the grid settings
bool IsCursorEnabled() const
Returns information about cursor visibility.
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:82
int gridTick
Every tick line gets the double width.
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
Class UTF8 is an 8 bit string that is assuredly encoded in UTF8, and supplies special conversion supp...
Definition: utf8.h:73
double getWorldPixelSize() const
Definition: opengl_gal.cpp:346
VERTEX_MANAGER * currentManager
Currently used VERTEX_MANAGER (for storing VERTEX_ITEMs)
Definition: opengl_gal.h:301
EDA_TEXT_HJUSTIFY_T GetHorizontalJustify() const
Returns current text horizontal justification setting.
virtual void beginUpdate() override
Definition: opengl_gal.cpp:547
VECTOR2D cursorPosition
Current cursor position (world coordinates)
static SHADER * shader
There is only one shader used for different objects.
Definition: opengl_gal.h:313
bool axesEnabled
Should the axes be drawn.
virtual void DrawBitmap(const BITMAP_BASE &aBitmap) override
Draw a bitmap image.
bool IsLinked() const
Returns true if shaders are linked correctly.
Definition: shader.h:122
EDA_TEXT_VJUSTIFY_T GetVerticalJustify() const
Returns current text vertical justification setting.
virtual void DrawBuffer(unsigned int aBufferHandle) override
Function DrawBuffer() draws the selected buffer to the output buffer.
static wxGLContext * glMainContext
Parent OpenGL context.
Definition: opengl_gal.h:289
void Clear() const
Function Clear() removes all the stored vertices from the container.
double calcAngleStep(double aRadius) const
Compute the angle step when drawing arcs/circles approximated with lines.
Definition: opengl_gal.h:472
std::pair< UTF8, std::vector< bool > > ProcessOverbars(const UTF8 &aText)
Processes a text to extract the raw text and overbar flags.
Definition: text_utils.cpp:27
double layerDepth
The actual layer depth.
void ChangeItemDepth(const VERTEX_ITEM &aItem, GLfloat aDepth) const
Function ChangeItemDepth() changes the depth of all vertices owned by an item.
Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer.
Definition: class_module.h:57
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:121
void PushMatrix()
Function PushMatrix() pushes the current transformation matrix stack.
unsigned int overlayBuffer
Auxiliary rendering target (for menus etc.)
Definition: opengl_gal.h:309
std::deque< boost::shared_array< GLdouble > > & intersectPoints
Intersect points, that have to be freed after tessellation.
Definition: opengl_gal.h:279
virtual void Scale(const VECTOR2D &aScale) override
Scale the context.
virtual void ClearTarget(RENDER_TARGET aTarget) override
Clears the target for rendering.
virtual void Translate(const VECTOR2D &aTranslation) override
Translate the context.
virtual void Begin() override
Function Begin() Call this at the beginning of each frame.
bool Reserve(unsigned int aSize)
Function Reserve() allocates space for vertices, so it will be used with subsequent Vertex() calls...
VECTOR2< T > Resize(T aNewLength) const
Function Resize returns a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:385
RENDER_TARGET currentTarget
Current rendering target.
Definition: opengl_gal.h:310
int PointCount() const
Function PointCount()
bool isBitmapFontInitialized
Is the shader set to use bitmap fonts?
Definition: opengl_gal.h:318
void Use()
Use the shader.
Definition: shader.h:130
void drawPolygon(GLdouble *aPoints, int aPointCount)
Draws a filled polygon.
void SetParameter(int aParameterNumber, float aValue) const
Set a parameter of the shader.
Definition: shader.cpp:138
static GLuint fontTexture
Bitmap font texture handle (shared)
Definition: opengl_gal.h:295
GRID_STYLE gridStyle
Grid display style.
bool LoadShaderFromStrings(SHADER_TYPE aShaderType, Args &&...aArgs)
Add a shader and compile the shader sources.
Definition: shader.h:97
void drawLineQuad(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a quad for the line.
VERTEX_MANAGER * overlayManager
Container for storing overlaid VERTEX_ITEMs.
Definition: opengl_gal.h:304
GAL_DISPLAY_OPTIONS & options
int color
Definition: DXF_plotter.cpp:62
virtual void Restore() override
Restore the context.
void CALLBACK EdgeCallback(GLboolean aEdgeFlag)
virtual void ChangeGroupDepth(int aGroupNumber, int aDepth) override
Changes the depth (Z-axis position) of the group.
const MATRIX3x3D & GetScreenWorldMatrix() const
Get the screen <-> world transformation matrix.
std::unique_ptr< GL_BITMAP_CACHE > bitmapCache
Definition: opengl_gal.h:326
virtual void EnableDepthTest(bool aEnabled=false) override
Parameters passed to the GLU tesselator.
bool Vertex(const VERTEX &aVertex)
Function Vertex() adds a vertex with the given coordinates to the currently set item.
static const COLOR4D BLACK
Definition: color4d.h:297
wxGLCanvas wrapper for HiDPI/Retina support.
virtual int BeginGroup() override
Begin a group.
static void InitTesselatorCallbacks(GLUtesselator *aTesselator)
virtual void DrawPolygon(const std::deque< VECTOR2D > &aPointList) override
Draw a polygon.
Definition: opengl_gal.cpp:908
void Rotate(GLfloat aAngle, GLfloat aX, GLfloat aY, GLfloat aZ)
Function Rotate() multiplies the current matrix by a rotation matrix, so the newly vertices will be r...
bool isContextLocked
Used for assertion checking.
Definition: opengl_gal.h:322
double g
Green component.
Definition: color4d.h:288
Fragment shader.
Definition: shader.h:45
bool Link()
Link the shaders.
Definition: shader.cpp:97
COLOR4D fillColor
The fill color.
VECTOR2D depthRange
Range of the depth.
bool IsTriangulationUpToDate() const
int checkGlError(const std::string &aInfo, bool aThrow)
Checks if one of recent OpenGL operations has failed.
Definition: utils.cpp:30
int OutlineCount() const
Returns the number of outlines in the set
MATRIX3x3D screenWorldMatrix
Screen transformation.
bool globalFlipX
Flag for X axis flipping.
const FONT_GLYPH_TYPE * LookupGlyph(unsigned int aCodepoint)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
static const unsigned int DIRECT_RENDERING
bool isGrouping
Was a group started?
Definition: opengl_gal.h:321
virtual void ComputeWorldScreenMatrix()
Compute the world <-> screen transformation matrix.
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:45
OPENGL_ANTIALIASING_MODE gl_antialiasing_mode
#define abs(a)
Definition: auxiliary.h:84
void UnlockCtx(wxGLContext *aContext)
Function UnlockCtx allows other canvases to bind an OpenGL context.
void enableGlDebug(bool aEnable)
Enables/disables OpenGL driver messages output.
Definition: utils.cpp:140
virtual void ResizeScreen(int aWidth, int aHeight) override
Resizes the canvas.
void unlockContext(int aClientCookie) override
Definition: opengl_gal.cpp:533
void CALLBACK VertexCallback(GLvoid *aVertexPtr, void *aData)
virtual void Transform(const MATRIX3x3D &aTransformation) override
Transform the context.
uni_iter ubegin() const
Function ubegin returns a uni_iter initialized to the start of "this" UTF8 byte sequence.
Definition: utf8.h:285
virtual void SetLineWidth(float aLineWidth)
Set the line width.
VERTEX_MANAGER * nonCachedManager
Container for storing non-cached VERTEX_ITEMs.
Definition: opengl_gal.h:303
double b
Blue component.
Definition: color4d.h:289
Auxiliary rendering target (noncached)
Definition: definitions.h:42
static int instanceCounter
GL GAL instance counter.
Definition: opengl_gal.h:291
This file contains miscellaneous commonly used macros and functions.
VERTEX_MANAGER * vboManager
Manager used for storing new vertices.
Definition: opengl_gal.h:276
T m_data[3][3]
Definition: matrix3x3.h:64
void SetAntialiasingMode(OPENGL_ANTIALIASING_MODE aMode)
virtual void DrawArcSegment(const VECTOR2D &aCenterPoint, double aRadius, double aStartAngle, double aEndAngle, double aWidth) override
Draw an arc segment.
Definition: opengl_gal.cpp:751
virtual RENDER_TARGET GetTarget() const override
Gets the currently used target for rendering.
static const int CURVE_POINTS
The number of points for curve approximation.
Definition: opengl_gal.h:287
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:46
virtual void endUpdate() override
Definition: opengl_gal.cpp:563
static GL_CONTEXT_MANAGER & Get()
Function Get returns the GL_CONTEXT_MANAGER instance (singleton).
double Angle() const
Function Angle computes the angle of the vector.
Definition: vector2d.h:306
void SetGridColor(const COLOR4D &aGridColor)
Set the grid color.
virtual void Save() override
Save the context.
void init()
Basic OpenGL initialization.
double a
Alpha component.
Definition: color4d.h:290
void Unmap()
Function Unmap() unmaps vertex buffer.
OPENGL_COMPOSITOR * compositor
Handles multiple rendering targets.
Definition: opengl_gal.h:307
bool fullscreenCursor
Shape of the cursor (fullscreen or small cross)
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:292
bool isStrokeEnabled
Are the outlines stroked ?
VECTOR2< double > VECTOR2D
Definition: vector2d.h:586
COLOR4D getCursorColor() const
Gets the actual cursor color to draw.
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
GROUPS_MAP groups
Stores informations about VBO objects (groups)
Definition: opengl_gal.h:299
virtual void Present() override
Function Present() Call this to present the output buffer to the screen.
std::pair< VECTOR2D, float > computeBitmapTextSize(const UTF8 &aText) const
Computes a size of text drawn using bitmap font with current text setting applied.
virtual void RestoreScreen() override
Restore the screen contents.
unsigned int mainBuffer
Main rendering target.
Definition: opengl_gal.h:308
virtual bool Show(bool aShow) override
Shows/hides the GAL canvas.
virtual void ClearBuffer(const COLOR4D &aColor) override
Function ClearBuffer() clears the selected buffer (set by the SetBuffer() function).
Class SHAPE_POLY_SET.
virtual void EndGroup() override
End the group.
COLOR4D axesColor
Color of the axes.
virtual void DeleteGroup(int aGroupNumber) override
Delete the group from the memory.
wxEvtHandler * mouseListener
Definition: opengl_gal.h:292
void SetAxesColor(const COLOR4D &aAxesColor)
Set the axes color.
void EndDrawing() const
Function EndDrawing() finishes drawing operations.
bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions) override
Function updatedGalDisplayOptions.
Definition: opengl_gal.cpp:326
virtual void DrawGrid() override
>
COLOR4D strokeColor
The color of the outlines.
float gridLineWidth
Line width of the grid.
void Translate(GLfloat aX, GLfloat aY, GLfloat aZ)
Function Translate() multiplies the current matrix by a translation matrix, so newly vertices will be...
static const int glAttributes[]
Definition: opengl_gal.cpp:66
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a line.
Definition: opengl_gal.cpp:572
double worldUnitLength
The unit length of the world coordinates [inch].
const TRIANGULATED_POLYGON * TriangulatedPolygon(int aIndex) const
Items that may change while the view stays the same (noncached)
Definition: definitions.h:43
void Map()
Function Map() maps vertex buffer.
void onPaint(wxPaintEvent &aEvent)
This is the OnPaint event handler.
int AddParameter(const std::string &aParameterName)
Add a parameter to the parameter queue.
Definition: shader.cpp:125
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
virtual void endDrawing() override
Definition: opengl_gal.cpp:488
const VECTOR2D & GetGlyphSize() const
virtual void DrawGroup(int aGroupNumber) override
Draw the stored group.
void Deactivate()
Deactivate the shader and use the default OpenGL program.
Definition: shader.h:139
bool isFramebufferInitialized
Are the framebuffers initialized?
Definition: opengl_gal.h:316
virtual void Resize(unsigned int aWidth, unsigned int aHeight) override
Function Resize() clears the state of COMPOSITOR, so it has to be reinitialized again with the new di...
VECTOR2D gridOrigin
The grid origin.
wxSize GetSizePixels() const
Function GetSizePixels.
Definition: bitmap_base.h:133
virtual void ClearCache() override
Delete all data created during caching of graphic items.
FONT_IMAGE_TYPE font_image
Definition: gl_resources.cpp:4
unsigned char pixels[1024 *1024 *3]
Definition: gl_resources.h:38
void drawTriangulatedPolyset(const SHAPE_POLY_SET &aPoly)
Draws a set of polygons with a cached triangulation.
Definition: opengl_gal.cpp:942
OPENGL_ANTIALIASING_MODE GetAntialiasingMode() const
FONT_INFO_TYPE font_information
Definition: gl_resources.cpp:3
MATRIX3x3D worldScreenMatrix
World transformation.
Vertex shader.
Definition: shader.h:44
virtual void SetBuffer(unsigned int aBufferHandle) override
Function SetBuffer() sets the selected buffer as the rendering target.
Class SHADER provides the access to the OpenGL shaders.
Definition: shader.h:74
string & err
Definition: json11.cpp:598
void lockContext(int aClientCookie) override
Private: use GAL_CONTEXT_LOCKER RAII object.
Definition: opengl_gal.cpp:523
void BeginDrawing() const
Function BeginDrawing() prepares buffers and items to start drawing.
void Color(const COLOR4D &aColor)
Function Color() changes currently used color that will be applied to newly added vertices...
GLUtesselator * tesselator
The tessellator.
Definition: opengl_gal.h:349
Use dots for the grid.
Use small cross instead of dots for the grid.
std::map< const BITMAP_BASE *, CACHED_BITMAP > m_bitmaps
Definition: opengl_gal.cpp:96
virtual void Rotate(double aAngle) override
Rotate the context.
virtual bool updatedGalDisplayOptions(const GAL_DISPLAY_OPTIONS &aOptions)
Function updatedGalDisplayOptions.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Function LockCtx sets a context as current and prevents other canvases from switching it...
void drawStrokedSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a stroked semicircle.
#define H(x, y, z)
Definition: md5_hash.cpp:17
void PopMatrix()
Function PopMatrix() pops the current transformation matrix stack.
float lineWidth
The line width.
bool isFillEnabled
Is filling of graphic objects enabled ?
void blitCursor()
Blits cursor into the current screen.
VERTEX_MANAGER * cachedManager
Container for storing cached VERTEX_ITEMs.
Definition: opengl_gal.h:302
class uni_iter is a non-mutating iterator that walks through unicode code points in the UTF8 encoded ...
Definition: utf8.h:207
virtual void Initialize() override
Function Reset() performs primary initialiation, necessary to use the object.
virtual void DrawPolyline(const std::deque< VECTOR2D > &aPointList) override
Draw a polyline.
Definition: opengl_gal.cpp:885
virtual void DrawSegment(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint, double aWidth) override
Draw a rounded segment.
Definition: opengl_gal.cpp:580
GLint ufm_worldPixelSize
Definition: opengl_gal.h:324
std::deque< boost::shared_array< GLdouble > > tessIntersects
Storage for intersecting points.
Definition: opengl_gal.h:351
virtual void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, double aStartAngle, double aEndAngle) override
Draw an arc.
Definition: opengl_gal.cpp:685
void CALLBACK ErrorCallback(GLenum aErrorCode)
virtual void SaveScreen() override
Save the screen contents.
double msecs() const
Definition: profile.h:124
virtual void SetTarget(RENDER_TARGET aTarget) override
Sets the target for rendering.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
uni_iter uend() const
Function uend returns a uni_iter initialized to the end of "this" UTF8 byte sequence.
Definition: utf8.h:294
bool isInitialized
Basic initialization flag, has to be done when the window is visible.
Definition: opengl_gal.h:319
COLOR4D gridColor
Color of the grid.
const int scale
void ChangeItemColor(const VERTEX_ITEM &aItem, const COLOR4D &aColor) const
Function ChangeItemColor() changes the color of all vertices owned by an item.
int GetPPI() const
Definition: bitmap_base.h:145
void PostPaint()
Function PostPaint posts an event to m_paint_listener.
Definition: opengl_gal.h:251
bool gridVisibility
Should the grid be shown.
void drawSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a semicircle.
Main rendering target (cached)
Definition: definitions.h:41
void EnableDepthTest(bool aEnabled)
Function EnableDepthTest() Enables/disables Z buffer depth test.
unsigned int TriangulatedPolyCount() const
Returns the number of triangulated polygons
#define max(a, b)
Definition: auxiliary.h:86
bool globalFlipY
Flag for Y axis flipping.
Class SHAPE_LINE_CHAIN.
unsigned int groupCounter
Counter used for generating keys for groups.
Definition: opengl_gal.h:300
double worldScale
The scale factor world->screen.
int drawBitmapChar(unsigned long aChar)
Draws a single character using bitmap font.
void FinishItem() const
Function FinishItem() does the cleaning after adding an item.
VECTOR2D gridSize
The grid size.
virtual float GetBackingScaleFactor() const
size_t i
Definition: json11.cpp:597
const glm::mat4 & GetTransformation() const
void Scale(GLfloat aX, GLfloat aY, GLfloat aZ)
Function Scale() multiplies the current matrix by a scaling matrix, so the newly vertices will be sca...
virtual void ClearScreen() override
Clear the screen.
VECTOR2I screenSize
Screen size in screen coordinates.
#define SWAP(varA, condition, varB)
Swap the variables if a condition is met.
Definition: definitions.h:31
unsigned int getNewGroupNumber()
Returns a valid key that can be used as a new group number.
wxGLContext * glPrivContext
Canvas-specific OpenGL context.
Definition: opengl_gal.h:290
virtual void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint) override
Draw a rectangle.
Definition: opengl_gal.cpp:847
virtual void Flush() override
Force all remaining objects to be drawn.
bool IsClosed() const
Function IsClosed()
virtual unsigned int GetBuffer() const override
Function GetBuffer() returns currently used buffer handle.
virtual void DrawCursor(const VECTOR2D &aCursorPosition) override
Draw the cursor.
virtual unsigned int CreateBuffer() override
Function CreateBuffer() prepares a new buffer that may be used as a rendering target.
void drawPolyline(const std::function< VECTOR2D(int)> &aPointGetter, int aPointCount)
Generic way of drawing a polyline stored in different containers.
void DestroyCtx(wxGLContext *aContext)
Function DestroyCtx destroys a managed OpenGL context.
void CALLBACK CombineCallback(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **dataOut, void *aData)
void Shader(GLfloat aShaderType, GLfloat aParam1=0.0f, GLfloat aParam2=0.0f, GLfloat aParam3=0.0f)
Function Shader() changes currently used shader and its parameters that will be applied to newly adde...
double r
Red component.
Definition: color4d.h:287
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
RENDER_TARGET
RENDER_TARGET: Possible rendering targets.
Definition: definitions.h:39
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=NULL)
Function CreateCtx creates a managed OpenGL context.
virtual void BitmapText(const wxString &aText, const VECTOR2D &aPosition, double aRotationAngle) override
Draws a text using a bitmap font.
bool IsTextMirrored() const
Returns true if text should displayed mirrored.
#define CALLBACK
Definition: opengl_gal.h:48
virtual ~OPENGL_GAL()
Definition: opengl_gal.cpp:281
bool IsVisible() const override
>
Definition: opengl_gal.h:99
void DrawItem(const VERTEX_ITEM &aItem) const
Function DrawItem() draws an item to the buffer.
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
virtual void beginDrawing() override
Definition: opengl_gal.cpp:352
virtual void DrawCircle(const VECTOR2D &aCenterPoint, double aRadius) override
Draw a circle using world coordinates.
Definition: opengl_gal.cpp:625
static bool isBitmapFontLoaded
Is the bitmap font texture loaded?
Definition: opengl_gal.h:317
int SegmentCount() const
Function SegmentCount()
virtual void DrawCurve(const VECTOR2D &startPoint, const VECTOR2D &controlPointA, const VECTOR2D &controlPointB, const VECTOR2D &endPoint) override
Draw a cubic bezier spline.
void skipMouseEvent(wxMouseEvent &aEvent)
Skip the mouse event to the parent.
void drawFilledSemiCircle(const VECTOR2D &aCenterPoint, double aRadius, double aAngle)
Draw a filled semicircle.
virtual void ChangeGroupColor(int aGroupNumber, const COLOR4D &aNewColor) override
Changes the color used to draw the group.
#define min(a, b)
Definition: auxiliary.h:85
Class GAL is the abstract interface for drawing on a 2D-surface.
void SetShader(SHADER &aShader) const
Function SetShader() sets a shader program that is going to be used during rendering.
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39
void drawBitmapOverbar(double aLength, double aHeight)
Draws an overbar over the currently drawn text.