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