OpenCPN Partial API docs
glChartCanvas.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  * Authors: David Register
5  * Sean D'Epagnier
6  *
7  ***************************************************************************
8  * Copyright (C) 2014 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  ***************************************************************************
25  */
26 
27 // For compilers that support precompilation, includes "wx.h".
28 #include <wx/wxprec.h>
29 
30 #ifndef WX_PRECOMP
31 #include <wx/wx.h>
32 #endif // precompiled headers
33 
34 #include "dychart.h"
35 
36 #include <algorithm>
37 #include <stdint.h>
38 #include <vector>
39 
40 #include <wx/arrimpl.cpp>
41 #include <wx/brush.h>
42 #include <wx/colour.h>
43 #include <wx/dcmemory.h>
44 #include <wx/dynarray.h>
45 #include <wx/event.h>
46 #include <wx/font.h>
47 #include <wx/gdicmn.h>
48 #include <wx/glcanvas.h>
49 #include <wx/image.h>
50 #include <wx/jsonval.h>
51 #include <wx/log.h>
52 #include <wx/pen.h>
53 #include <wx/progdlg.h>
54 #include <wx/stopwatch.h>
55 #include <wx/string.h>
56 #include <wx/tokenzr.h>
57 #include <wx/utils.h>
58 #include <wx/window.h>
59 
60 #include "model/own_ship.h"
61 #include "model/route.h"
62 #include "model/routeman.h"
63 #include "model/track.h"
64 
65 #include "ais.h"
66 #include "chartbase.h"
67 #include "chart_ctx_factory.h"
68 #include "chartdb.h"
69 #include "chartimg.h"
70 #include "chcanv.h"
71 #include "ChInfoWin.h"
72 #include "cm93.h" // for chart outline draw
73 #include "color_handler.h"
74 #include "compass.h"
75 #include "config.h"
76 #include "emboss_data.h"
77 #include "FontMgr.h"
78 #include "glChartCanvas.h"
79 #include "glTexCache.h"
80 #include "gshhs.h"
81 #include "lz4.h"
82 #include "mbtiles.h"
83 #include "mipmap/mipmap.h"
84 #include "navutil.h"
85 #include "OCPNPlatform.h"
86 #include "piano.h"
87 #include "pluginmanager.h"
88 #include "Quilt.h"
89 #include "RolloverWin.h"
90 #include "route_gui.h"
91 #include "route_point_gui.h"
92 #include "s52plib.h"
93 #include "s57chart.h" // for ArrayOfS57Obj
94 #include "tcmgr.h"
95 #include "TexFont.h"
96 #include "thumbwin.h"
97 #include "toolbar.h"
98 #include "track_gui.h"
99 #include "MUIBar.h"
100 #include "iENCToolbar.h"
101 #include "shapefile_basemap.h"
102 
103 #ifdef USE_ANDROID_GLES2
104 #include <GLES2/gl2.h>
105 #include "linmath.h"
106 #include "shaders.h"
107 #endif
108 
109 #ifdef __ANDROID__
110 #include "androidUTIL.h"
111 #elif defined(__WXQT__) || defined(__WXGTK__)
112 #include <GL/glx.h>
113 #endif
114 
115 #ifndef GL_ETC1_RGB8_OES
116 #define GL_ETC1_RGB8_OES 0x8D64
117 #endif
118 
119 #ifndef GL_DEPTH_STENCIL_ATTACHMENT
120 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
121 #endif
122 
123 #if defined(__UNIX__) && !defined(__WXOSX__)
124 // high resolution stopwatch for profiling
125 class OCPNStopWatch {
126  public:
127  OCPNStopWatch() { Reset(); }
128  void Reset() { clock_gettime(CLOCK_REALTIME, &tp); }
129 
130  double GetTime() {
131  timespec tp_end;
132  clock_gettime(CLOCK_REALTIME, &tp_end);
133  return (tp_end.tv_sec - tp.tv_sec) * 1.e3 +
134  (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
135  }
136 
137  private:
138  timespec tp;
139 };
140 #endif
141 
142 #if defined(__ANDROID__)
143 #include "androidUTIL.h"
144 #elif defined(__WXQT__) || defined(__WXGTK__) || defined(FLATPAK)
145 #include <GL/glew.h>
146 #endif
147 
148 
149 #ifndef GL_ETC1_RGB8_OES
150 #define GL_ETC1_RGB8_OES 0x8D64
151 #endif
152 
153 
154 #include "lz4.h"
155 
156 #ifdef __ANDROID__
157 // arm gcc compiler has a lot of trouble passing doubles as function aruments.
158 // We don't really need double precision here, so fix with a (faster) macro.
159 extern "C" void glOrthof(float left, float right, float bottom, float top,
160  float near, float far);
161 #define glOrtho(a, b, c, d, e, f) \
162  ; \
163  glOrthof(a, b, c, d, e, f);
164 
165 #endif
166 
167 #include "cm93.h" // for chart outline draw
168 #include "s57chart.h" // for ArrayOfS57Obj
169 #include "s52plib.h"
170 
171 #ifdef USE_ANDROID_GLES2
172 #include <GLES2/gl2.h>
173 #endif
174 
175 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
176 #include "linmath.h"
177 #include "shaders.h"
178 #endif
179 
180 extern bool GetMemoryStatus(int *mem_total, int *mem_used);
181 
182 extern s52plib *ps52plib;
183 extern bool g_bopengl;
184 extern bool g_bDebugOGL;
185 extern bool g_bSoftwareGL;
186 extern ocpnFloatingToolbarDialog *g_MainToolbar;
187 extern iENCToolbar *g_iENCToolbar;
188 extern bool g_bShowChartBar;
189 extern glTextureManager *g_glTextureManager;
190 extern bool b_inCompressAllCharts;
191 
192 extern GLenum g_texture_rectangle_format;
193 
194 extern int g_memCacheLimit;
195 extern ColorScheme global_color_scheme;
196 extern bool g_bquiting;
197 extern ThumbWin *pthumbwin;
198 extern int g_mipmap_max_level;
199 
200 extern int g_OwnShipIconType;
201 
202 extern ChartDB *ChartData;
203 
204 extern PlugInManager *g_pi_manager;
205 
206 extern RouteList *pRouteList;
207 extern std::vector<Track*> g_TrackList;
208 extern bool b_inCompressAllCharts;
209 extern bool g_bGLexpert;
210 extern bool g_bcompression_wait;
211 extern float g_ShipScaleFactorExp;
212 
213 float g_GLMinCartographicLineWidth;
214 
215 extern bool g_fog_overzoom;
216 extern double g_overzoom_emphasis_base;
217 extern bool g_oz_vector_scale;
218 extern TCMgr *ptcmgr;
219 extern int g_nCPUCount;
220 extern bool g_running;
221 
222 extern unsigned int g_canvasConfig;
223 extern ChartCanvas *g_focusCanvas;
224 extern ChartCanvas *g_overlayCanvas;
225 extern BasePlatform *g_BasePlatform;
226 extern bool g_PrintingInProgress;
227 
228 ocpnGLOptions g_GLOptions;
229 
230 wxColor s_regionColor;
231 extern ShapeBaseChartSet gShapeBasemap;
232 
233 // For VBO(s)
234 bool g_b_EnableVBO;
235 bool g_b_needFinish; // Need glFinish() call on each frame?
236 
237 // MacOS has some missing parts:
238 #ifndef APIENTRY
239 #define APIENTRY
240 #endif
241 #ifndef APIENTRYP
242 #define APIENTRYP APIENTRY *
243 #endif
244 #ifndef GLAPI
245 #define GLAPI extern
246 #endif
247 
248 #ifndef GL_COMPRESSED_RGB_FXT1_3DFX
249 #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
250 #endif
251 
252 PFNGLGENFRAMEBUFFERSEXTPROC s_glGenFramebuffers;
253 PFNGLGENRENDERBUFFERSEXTPROC s_glGenRenderbuffers;
254 PFNGLFRAMEBUFFERTEXTURE2DEXTPROC s_glFramebufferTexture2D;
255 PFNGLBINDFRAMEBUFFEREXTPROC s_glBindFramebuffer;
256 PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC s_glFramebufferRenderbuffer;
257 PFNGLRENDERBUFFERSTORAGEEXTPROC s_glRenderbufferStorage;
258 PFNGLBINDRENDERBUFFEREXTPROC s_glBindRenderbuffer;
259 PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC s_glCheckFramebufferStatus;
260 PFNGLDELETEFRAMEBUFFERSEXTPROC s_glDeleteFramebuffers;
261 PFNGLDELETERENDERBUFFERSEXTPROC s_glDeleteRenderbuffers;
262 
263 PFNGLCOMPRESSEDTEXIMAGE2DPROC s_glCompressedTexImage2D;
264 PFNGLGETCOMPRESSEDTEXIMAGEPROC s_glGetCompressedTexImage;
265 
266 // Vertex Buffer Object (VBO) support
267 PFNGLGENBUFFERSPROC s_glGenBuffers;
268 PFNGLBINDBUFFERPROC s_glBindBuffer;
269 PFNGLBUFFERDATAPROC s_glBufferData;
270 PFNGLDELETEBUFFERSPROC s_glDeleteBuffers;
271 
272 #ifndef USE_ANDROID_GLES2
273 //#define glDeleteFramebuffers(a, b) (s_glDeleteFramebuffers)(a, b);
274 //#define glDeleteRenderbuffers(a, b) (s_glDeleteRenderbuffers)(a, b);
275 #endif
276 
277 typedef void(APIENTRYP PFNGLGETBUFFERPARAMETERIV)(GLenum target, GLenum value,
278  GLint *data);
279 PFNGLGETBUFFERPARAMETERIV s_glGetBufferParameteriv;
280 
281 #include <wx/arrimpl.cpp>
282 // WX_DEFINE_OBJARRAY( ArrayOfTexDescriptors );
283 
284 GLuint g_raster_format = GL_RGB;
285 long g_tex_mem_used;
286 
287 bool b_timeGL;
288 wxStopWatch g_glstopwatch;
289 double g_gl_ms_per_frame;
290 
291 int g_tile_size;
292 int g_uncompressed_tile_size;
293 
294 extern wxProgressDialog *pprog;
295 extern bool b_skipout;
296 extern wxSize pprog_size;
297 extern int pprog_count;
298 extern int pprog_threads;
299 extern MyFrame *gFrame;
300 
301 //#if defined(__MSVC__) && !defined(ocpnUSE_GLES) /* this compiler doesn't
302 // support vla */ const #endif extern int g_mipmap_max_level;
303 int panx, pany;
304 
305 bool glChartCanvas::s_b_useScissorTest;
306 bool glChartCanvas::s_b_useStencil;
307 bool glChartCanvas::s_b_useStencilAP;
308 bool glChartCanvas::s_b_useFBO;
309 
310 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
311 static int s_tess_vertex_idx;
312 static int s_tess_vertex_idx_this;
313 static int s_tess_buf_len;
314 static GLfloat *s_tess_work_buf;
315 GLenum s_tess_mode;
316 static int s_nvertex;
317 static vec4 s_tess_color;
318 ViewPort s_tessVP;
319 static ocpnDC *s_pdc;
320 #endif
321 
322 #if 0
323 /* for debugging */
324 static void print_region(OCPNRegion &Region)
325 {
326  OCPNRegionIterator upd ( Region );
327  while ( upd.HaveRects() )
328  {
329  wxRect rect = upd.GetRect();
330  printf("[(%d, %d) (%d, %d)] ", rect.x, rect.y, rect.width, rect.height);
331  upd.NextRect();
332  }
333 }
334 
335 #endif
336 
337 GLboolean QueryExtension(const char *extName) {
338  /*
339  ** Search for extName in the extensions string. Use of strstr()
340  ** is not sufficient because extension names can be prefixes of
341  ** other extension names. Could use strtok() but the constant
342  ** string returned by glGetString might be in read-only memory.
343  */
344  char *p;
345  char *end;
346  int extNameLen;
347 
348  extNameLen = strlen(extName);
349 
350  p = (char *)glGetString(GL_EXTENSIONS);
351  if (NULL == p) {
352  return GL_FALSE;
353  }
354 
355  end = p + strlen(p);
356 
357  while (p < end) {
358  int n = strcspn(p, " ");
359  if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
360  return GL_TRUE;
361  }
362  p += (n + 1);
363  }
364  return GL_FALSE;
365 }
366 
367 
368 int test_attribs[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE,
369  16, WX_GL_STENCIL_SIZE, 8,
370  0};
371 
372 glTestCanvas::glTestCanvas(wxWindow *parent)
373  : wxGLCanvas(parent, wxID_ANY, test_attribs, wxDefaultPosition,
374  wxSize(2, 2)) {}
375 
376 // This attribute set works OK with vesa software only OpenGL renderer
377 int attribs[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE,
378  16, WX_GL_STENCIL_SIZE, 8,
379  0};
380 BEGIN_EVENT_TABLE(glChartCanvas, wxGLCanvas)
381 EVT_PAINT(glChartCanvas::OnPaint)
382 EVT_ACTIVATE(glChartCanvas::OnActivate) EVT_SIZE(glChartCanvas::OnSize)
383  EVT_MOUSE_EVENTS(glChartCanvas::MouseEvent) END_EVENT_TABLE()
384 
385  glChartCanvas::glChartCanvas(wxWindow *parent, wxGLCanvas *share)
386  : wxGLCanvas(parent, wxID_ANY, attribs, wxDefaultPosition, wxSize(256, 256),
387  wxFULL_REPAINT_ON_RESIZE | wxBG_STYLE_CUSTOM, _T(""))
388 
389 {
390  m_pParentCanvas = dynamic_cast<ChartCanvas *>(parent);
391 
392  Init();
393 }
394 
395 std::unordered_map<wxPenStyle, std::array<wxDash, 2>> glChartCanvas::dash_map = {
396  {wxPENSTYLE_DOT, {1, 1}},
397  {wxPENSTYLE_LONG_DASH, {5, 5}},
398  {wxPENSTYLE_SHORT_DASH, {1, 5}},
399  {wxPENSTYLE_DOT_DASH, {5, 1}},
400 };
401 
402 void glChartCanvas::Init() {
403  m_bsetup = false;
404 
405  // m_pParentCanvas = dynamic_cast<ChartCanvas *>( GetParent() );
406 
407  SetBackgroundStyle(wxBG_STYLE_CUSTOM); // on WXMSW, this prevents flashing
408 
409  m_cache_current_ch = NULL;
410 
411  m_b_paint_enable = true;
412  m_in_glpaint = false;
413 
414  m_cache_tex[0] = m_cache_tex[1] = 0;
415  m_cache_page = 0;
416 
417  m_b_BuiltFBO = false;
418  m_b_DisableFBO = false;
419 
420  ownship_tex = 0;
421  ownship_color = -1;
422 
423  m_piano_tex = 0;
424 
425  m_binPinch = false;
426  m_binPan = false;
427  m_bpinchGuard = false;
428  m_binGesture = false;
429 
430  b_timeGL = true;
431  m_last_render_time = -1;
432 
433  m_LRUtime = 0;
434 
435  m_tideTex = 0;
436  m_currentTex = 0;
437 
438  m_gldc.SetGLCanvas(this);
439  m_gldc.SetDPIFactor(g_BasePlatform->GetDisplayDIPMult(GetParent()));
440 
441  m_displayScale = 1.0;
442 #if defined(__WXOSX__) || defined(__WXGTK3__)
443  // Support scaled HDPI displays.
444  m_displayScale = GetContentScaleFactor();
445 #endif
446 
447 #ifdef __ANDROID__
448  // Create/connect a dynamic event handler slot for gesture and some timer
449  // events
450  Connect(
451  wxEVT_QT_PANGESTURE,
452  (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::OnEvtPanGesture,
453  NULL, this);
454 
455  Connect(
456  wxEVT_QT_PINCHGESTURE,
457  (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::OnEvtPinchGesture,
458  NULL, this);
459 
460  Connect(GESTURE_EVENT_TIMER, wxEVT_TIMER,
461  (wxObjectEventFunction)(
462  wxEventFunction)&glChartCanvas::onGestureTimerEvent,
463  NULL, this);
464 
465  Connect(GESTURE_FINISH_TIMER, wxEVT_TIMER,
466  (wxObjectEventFunction)(
467  wxEventFunction)&glChartCanvas::onGestureFinishTimerEvent,
468  NULL, this);
469 
470  Connect(
471  ZOOM_TIMER, wxEVT_TIMER,
472  (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::onZoomTimerEvent,
473  NULL, this);
474 
475  m_gestureEeventTimer.SetOwner(this, GESTURE_EVENT_TIMER);
476  m_gestureFinishTimer.SetOwner(this, GESTURE_FINISH_TIMER);
477  zoomTimer.SetOwner(this, ZOOM_TIMER);
478 
479 #ifdef USE_ANDROID_GLES2
480 // Connect(
481 // TEX_FADE_TIMER, wxEVT_TIMER,
482 // (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::onFadeTimerEvent,
483 // NULL, this);
484 // m_fadeTimer.SetOwner(this, TEX_FADE_TIMER);
485 #endif
486 
487  m_bgestureGuard = false;
488 
489 #endif
490 
491 // Gesture support for platforms other than Android
492 #ifdef HAVE_WX_GESTURE_EVENTS
493  if (!EnableTouchEvents(wxTOUCH_ZOOM_GESTURE |
494  wxTOUCH_PRESS_GESTURES)) {
495  wxLogError("Failed to enable touch events");
496  }
497 
498  Bind(wxEVT_GESTURE_ZOOM, &ChartCanvas::OnZoom, m_pParentCanvas);
499 
500  Bind(wxEVT_LONG_PRESS, &ChartCanvas::OnLongPress, m_pParentCanvas);
501  Bind(wxEVT_PRESS_AND_TAP, &ChartCanvas::OnPressAndTap, m_pParentCanvas);
502 
503  Bind(wxEVT_RIGHT_UP, &ChartCanvas::OnRightUp, m_pParentCanvas);
504  Bind(wxEVT_RIGHT_DOWN, &ChartCanvas::OnRightDown, m_pParentCanvas);
505 
506  Bind(wxEVT_LEFT_UP, &ChartCanvas::OnLeftUp, m_pParentCanvas);
507  Bind(wxEVT_LEFT_DOWN, &ChartCanvas::OnLeftDown, m_pParentCanvas);
508 
509  Bind(wxEVT_MOUSEWHEEL, &ChartCanvas::OnWheel, m_pParentCanvas);
510  Bind(wxEVT_MOTION, &ChartCanvas::OnMotion, m_pParentCanvas);
511 #endif /* HAVE_WX_GESTURE_EVENTS */
512 
513  if (!g_glTextureManager) g_glTextureManager = new glTextureManager;
514 
515 }
516 
517 glChartCanvas::~glChartCanvas() {
518 #ifdef __ANDROID__
519  unloadShaders();
520 #endif
521 }
522 
523 void glChartCanvas::FlushFBO(void) {
524  if (m_bsetup) BuildFBO();
525 }
526 
527 void glChartCanvas::OnActivate(wxActivateEvent &event) {
528  m_pParentCanvas->OnActivate(event);
529 }
530 
531 void glChartCanvas::OnSize(wxSizeEvent &event) {
532 #if 0
533 #ifdef __ANDROID__
534  if(!g_running){
535  wxLogMessage(_T("Got OnSize event while NOT running"));
536  event.Skip();
537  qDebug() << "OnSizeB";
538 
539  return;
540  }
541 #endif
542 #endif
543 
544  if (!IsShown()) return;
545 
546  SetCurrent(*m_pcontext);
547 
548  if (!g_bopengl) {
549  SetSize(GetSize().x, GetSize().y);
550  event.Skip();
551  return;
552  }
553 
554  // this is also necessary to update the context on some platforms
555  // OnSize can be called with a different OpenGL context (when a plugin uses a
556  // different GL context).
557  if (m_bsetup && m_pcontext && IsShown()) {
558  SetCurrent(*m_pcontext);
559  }
560 
561  //SetSize(m_pParentCanvas->GetClientSize());
562 
563  if (m_bsetup) {
564  wxLogMessage(_T("BuildFBO 3"));
565  BuildFBO();
566  }
567 
568 
569  // Set the shader viewport transform matrix
570  ViewPort *vp = m_pParentCanvas->GetpVP();
571  mat4x4 m;
572  mat4x4_identity(m);
573  mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
574  2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
575  1.0);
576  mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform, -vp->pix_width / 2,
577  -vp->pix_height / 2, 0);
578 }
579 
580 void glChartCanvas::MouseEvent(wxMouseEvent &event) {
581  if (m_pParentCanvas->MouseEventOverlayWindows(event)) return;
582 
583 #ifndef __ANDROID__
584  if (m_pParentCanvas->MouseEventSetup(event))
585  return; // handled, no further action required
586 
587  bool obj_proc = m_pParentCanvas->MouseEventProcessObjects(event);
588 
589  if (!obj_proc && !m_pParentCanvas->singleClickEventIsValid)
590  m_pParentCanvas->MouseEventProcessCanvas(event);
591 
592  if (!g_btouch) m_pParentCanvas->SetCanvasCursor(event);
593 
594 #else
595 
596  if (m_bgestureGuard) {
597  m_pParentCanvas->r_rband.x = 0; // turn off rubberband temporarily
598 
599  // Sometimes we get a Gesture Pan start on a simple tap operation.
600  // When this happens, we usually get no Gesture Finished event.
601  // So, we need to process the next LeftUp event normally, to handle things
602  // like Measure and Route Create.
603 
604  // Allow LeftUp() event through if the pan action is very small
605  // Otherwise, drop the LeftUp() event, since it is not wanted for a Pan
606  // Gesture.
607  if (event.LeftUp()) {
608  // qDebug() << panx << pany;
609  if ((abs(panx) > 2) || (abs(pany) > 2)) {
610  return;
611  } else { // Cancel the in=process Gesture state
612  m_gestureEeventTimer.Start(10, wxTIMER_ONE_SHOT); // Short Circuit
613  }
614  } else
615  return;
616  }
617 
618  if (m_pParentCanvas->MouseEventSetup(event, false)) {
619  if (!event.LeftDClick()) {
620  return; // handled, no further action required
621  }
622  }
623 
624  if (m_binPan && event.RightDown()) {
625  qDebug() << "Skip right on pan";
626  return;
627  } else {
628  bool obj_proc = m_pParentCanvas->MouseEventProcessObjects(event);
629 
630  if (!obj_proc && !m_pParentCanvas->singleClickEventIsValid) {
631  if (!m_bgestureGuard)
632  m_pParentCanvas->MouseEventProcessCanvas(
633  event); // This is where a physical mouse gets processed, if
634  // detected
635  }
636  }
637 
638 #endif
639 }
640 
641 #ifndef GL_MAX_RENDERBUFFER_SIZE
642 #define GL_MAX_RENDERBUFFER_SIZE 0x84E8
643 #endif
644 
645 #ifndef USE_ANDROID_GLES2
646 bool glChartCanvas::buildFBOSize(int fboSize) {
647  bool retVal = true;
648  if (IsShown()) SetCurrent(*m_pcontext);
649 
650  if (m_b_BuiltFBO) {
651  glDeleteTextures(2, m_cache_tex);
652  glDeleteFramebuffers(1, &m_fb0);
653  glDeleteRenderbuffers(1, &m_renderbuffer);
654  m_b_BuiltFBO = false;
655  }
656 
657  if (m_b_DisableFBO) return false;
658 
659 #ifdef __ANDROID__
660  // We use the smallest possible (POT) FBO
661  int rb_x = GetSize().x;
662  int rb_y = GetSize().y;
663  int i = 1;
664  while (i < rb_x) i <<= 1;
665  rb_x = i;
666 
667  i = 1;
668  while (i < rb_y) i <<= 1;
669  rb_y = i;
670 
671  m_cache_tex_x = wxMax(rb_x, rb_y);
672  m_cache_tex_y = wxMax(rb_x, rb_y);
673 
674 #else
675  m_cache_tex_x = GetSize().x * m_displayScale;
676  m_cache_tex_y = GetSize().y * m_displayScale;
677 #endif
678 
679  int err = GL_NO_ERROR;
680  GLint params;
681  glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &params);
682 
683  err = glGetError();
684  if (err == GL_INVALID_ENUM) {
685  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &params);
686  err = glGetError();
687  }
688 
689  if (err == GL_NO_ERROR) {
690  if (fboSize > params) {
691  wxLogMessage(
692  _T(" OpenGL-> Requested Framebuffer size exceeds ")
693  _T("GL_MAX_RENDERBUFFER_SIZE"));
694  return false;
695  }
696  }
697 
698  glGenFramebuffers(1, &m_fb0);
699  err = glGetError();
700  if (err) {
701  wxString msg;
702  msg.Printf(_T(" OpenGL-> Framebuffer GenFramebuffers error: %08X"),
703  err);
704  wxLogMessage(msg);
705  retVal = false;
706  }
707 
708  glGenRenderbuffers(1, &m_renderbuffer);
709  err = glGetError();
710  if (err) {
711  wxString msg;
712  msg.Printf(_T(" OpenGL-> Framebuffer GenRenderbuffers error: %08X"),
713  err);
714  wxLogMessage(msg);
715  retVal = false;
716  }
717 
718  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
719  err = glGetError();
720  if (err) {
721  wxString msg;
722  msg.Printf(_T(" OpenGL-> Framebuffer BindFramebuffers error: %08X"),
723  err);
724  wxLogMessage(msg);
725  retVal = false;
726  }
727 
728  // initialize color textures
729  glGenTextures(2, m_cache_tex);
730  for (int i = 0; i < 2; i++) {
731  glBindTexture(g_texture_rectangle_format, m_cache_tex[i]);
732  glTexParameterf(g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
733  GL_NEAREST);
734  glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
735  GL_NEAREST);
736  glTexImage2D(g_texture_rectangle_format, 0, GL_RGBA, m_cache_tex_x,
737  m_cache_tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
738  }
739 
740  glBindRenderbuffer(GL_RENDERBUFFER_EXT, m_renderbuffer);
741 
742  if (m_b_useFBOStencil) {
743  // initialize composite depth/stencil renderbuffer
744  glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
745  m_cache_tex_x, m_cache_tex_y);
746 
747  int err = glGetError();
748  if (err) {
749  wxString msg;
750  msg.Printf(_T(" OpenGL-> glRenderbufferStorage error: %08X"), err);
751  wxLogMessage(msg);
752  }
753 
754  glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
755  GL_RENDERBUFFER_EXT, m_renderbuffer);
756  err = glGetError();
757  if (err) {
758  wxString msg;
759  msg.Printf(
760  _T(" OpenGL-> glFramebufferRenderbuffer depth error: %08X"), err);
761  wxLogMessage(msg);
762  }
763 
764  glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
765  GL_RENDERBUFFER_EXT, m_renderbuffer);
766  err = glGetError();
767  if (err) {
768  wxString msg;
769  msg.Printf(
770  _T(" OpenGL-> glFramebufferRenderbuffer stencil error: %08X"),
771  err);
772  wxLogMessage(msg);
773  }
774 
775  } else {
776  GLenum depth_format = GL_DEPTH_COMPONENT24;
777 
778  // Need to check for availability of 24 bit depth buffer extension on
779  // GLES
780 #ifdef ocpnUSE_GLES
781  if (!QueryExtension("GL_OES_depth24")) depth_format = GL_DEPTH_COMPONENT16;
782 #endif
783 
784  // initialize depth renderbuffer
785  glRenderbufferStorage(GL_RENDERBUFFER_EXT, depth_format, m_cache_tex_x,
786  m_cache_tex_y);
787  int err = glGetError();
788  if (err) {
789  wxString msg;
790  msg.Printf(
791  _T(" OpenGL-> Framebuffer Depth Buffer Storage error: %08X"),
792  err);
793  wxLogMessage(msg);
794  retVal = false;
795  }
796 
797  glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
798  GL_RENDERBUFFER_EXT, m_renderbuffer);
799 
800  err = glGetError();
801  if (err) {
802  wxString msg;
803  msg.Printf(
804  _T(" OpenGL-> Framebuffer Depth Buffer Attach error: %08X"), err);
805  wxLogMessage(msg);
806  retVal = false;
807  }
808  }
809 
810  glBindTexture(GL_TEXTURE_2D, 0);
811  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
812 
813  // Check framebuffer completeness at the end of initialization.
814  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
815 
816  glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
817  g_texture_rectangle_format, m_cache_tex[0], 0);
818 
819  GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
820 
821  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
822 
823  if (fb_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
824  wxString msg;
825  msg.Printf(_T(" OpenGL-> buildFBOSize->Framebuffer Incomplete: %08X"),
826  fb_status);
827  wxLogMessage(msg);
828  retVal = false;
829  }
830 
831  return retVal;
832 }
833 #endif
834 
835 #ifdef USE_ANDROID_GLES2
836 bool glChartCanvas::buildFBOSize(int fboSize) {
837  bool retVal = true;
838 
839  // We use the smallest possible (POT) FBO
840  int rb_x = GetSize().x;
841  int rb_y = GetSize().y;
842  int i = 1;
843  while (i < rb_x) i <<= 1;
844  rb_x = i;
845 
846  i = 1;
847  while (i < rb_y) i <<= 1;
848  rb_y = i;
849 
850  m_cache_tex_x = wxMax(rb_x, rb_y);
851  m_cache_tex_y = wxMax(rb_x, rb_y);
852 
853  // qDebug() << "FBO Size: " << GetSize().x << GetSize().y << m_cache_tex_x;
854 
855  int err = GL_NO_ERROR;
856  GLint params;
857  glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &params);
858 
859  err = glGetError();
860  if (err == GL_INVALID_ENUM) {
861  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &params);
862  err = glGetError();
863  }
864 
865  if (err == GL_NO_ERROR) {
866  if (fboSize > params) {
867  wxLogMessage(
868  _T(" OpenGL-> Requested Framebuffer size exceeds ")
869  _T("GL_MAX_RENDERBUFFER_SIZE"));
870  return false;
871  }
872  }
873 
874  glGenFramebuffers(1, &m_fb0);
875  err = glGetError();
876  if (err) {
877  wxString msg;
878  msg.Printf(_T(" OpenGL-> Framebuffer GenFramebuffers error: %08X"),
879  err);
880  wxLogMessage(msg);
881  retVal = false;
882  }
883 
884  glGenRenderbuffers(1, &m_renderbuffer);
885  err = glGetError();
886  if (err) {
887  wxString msg;
888  msg.Printf(_T(" OpenGL-> Framebuffer GenRenderbuffers error: %08X"),
889  err);
890  wxLogMessage(msg);
891  retVal = false;
892  }
893 
894  glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
895  err = glGetError();
896  if (err) {
897  wxString msg;
898  msg.Printf(_T(" OpenGL-> Framebuffer BindFramebuffers error: %08X"),
899  err);
900  wxLogMessage(msg);
901  retVal = false;
902  }
903 
904  // initialize color textures
905  glGenTextures(2, m_cache_tex);
906  for (int i = 0; i < 2; i++) {
907  glBindTexture(g_texture_rectangle_format, m_cache_tex[i]);
908  glTexParameterf(g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
909  GL_NEAREST);
910  glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
911  GL_NEAREST);
912  glTexImage2D(g_texture_rectangle_format, 0, GL_RGBA, m_cache_tex_x,
913  m_cache_tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
914  }
915 
916  glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
917 
918  // initialize composite depth/stencil renderbuffer
919  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, m_cache_tex_x,
920  m_cache_tex_y);
921 
922  err = glGetError();
923  if (err) {
924  wxString msg;
925  msg.Printf(_T(" OpenGL-> glRenderbufferStorage error: %08X"), err);
926  wxLogMessage(msg);
927  }
928 
929  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
930  GL_RENDERBUFFER, m_renderbuffer);
931  err = glGetError();
932  if (err) {
933  wxString msg;
934  msg.Printf(_T(" OpenGL-> glFramebufferRenderbuffer depth error: %08X"),
935  err);
936  wxLogMessage(msg);
937  }
938 
939  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
940  GL_RENDERBUFFER, m_renderbuffer);
941  err = glGetError();
942  if (err) {
943  wxString msg;
944  msg.Printf(
945  _T(" OpenGL-> glFramebufferRenderbuffer stencil error: %08X"), err);
946  wxLogMessage(msg);
947  }
948 
949  glBindTexture(GL_TEXTURE_2D, 0);
950  glBindFramebuffer(GL_FRAMEBUFFER, 0);
951 
952  // Check framebuffer completeness at the end of initialization.
953  glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
954 
955  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
956  g_texture_rectangle_format, m_cache_tex[0], 0);
957 
958  GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
959 
960  glBindFramebuffer(GL_FRAMEBUFFER, 0);
961 
962  if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
963  wxString msg;
964  msg.Printf(
965  _T(" OpenGL-> buildFBOSize->Framebuffer Incomplete: %08X %08X"),
966  fb_status);
967  wxLogMessage(msg);
968  retVal = false;
969  }
970 
971  return retVal;
972 }
973 #endif
974 
975 void glChartCanvas::BuildFBO() {
976  if (m_b_BuiltFBO) {
977  // return;
978  glDeleteTextures(2, m_cache_tex);
979  glDeleteFramebuffers(1, &m_fb0);
980  glDeleteRenderbuffers(1, &m_renderbuffer);
981  m_b_BuiltFBO = false;
982  }
983 
984  if (m_b_DisableFBO) return;
985 
986  // int initialSize = 2048;
987  int gl_width, gl_height;
988  m_pParentCanvas->GetClientSize(&gl_width, &gl_height);
989  int initialSize = NextPow2(gl_width * m_displayScale);
990 
991 #ifdef __ANDROID__
992  // Some low mem-spec devices have trouble with 2048 FBO size.
993  // Detect here, and choose 1024 size instead
994  wxString info = androidGetDeviceInfo();
995 
996  if (wxNOT_FOUND != info.Find(_T("GT-S6312"))) initialSize = 1024;
997 #endif
998 
999  if (!buildFBOSize(initialSize)) {
1000  glDeleteTextures(2, m_cache_tex);
1001  glDeleteFramebuffers(1, &m_fb0);
1002  glDeleteRenderbuffers(1, &m_renderbuffer);
1003 
1004  if (!buildFBOSize(1024)) {
1005  wxLogMessage(_T("BuildFBO C"));
1006 
1007  m_b_DisableFBO = true;
1008  wxLogMessage(_T("OpenGL-> FBO Framebuffer unavailable"));
1009  m_b_BuiltFBO = false;
1010 
1011  return;
1012  }
1013  }
1014 
1015  // All OK
1016 
1017  wxString msg;
1018  msg.Printf(_T("OpenGL-> Framebuffer OK, size = %d"), m_cache_tex_x);
1019  wxLogMessage(msg);
1020 
1021  /* invalidate cache */
1022  Invalidate();
1023 
1024  glClear(GL_COLOR_BUFFER_BIT);
1025  m_b_BuiltFBO = true;
1026 
1027  return;
1028 }
1029 
1030 void glChartCanvas::SetupOpenGL() {
1031  SetCurrent(*m_pcontext);
1032 
1033  char *str = (char *)glGetString(GL_RENDERER);
1034  if (str == NULL) {
1035  // perhaps we should edit the config and turn off opengl now
1036  wxLogMessage(_T("Failed to initialize OpenGL"));
1037  exit(1);
1038  }
1039 
1040  char render_string[80];
1041  strncpy(render_string, str, 79);
1042  m_renderer = wxString(render_string, wxConvUTF8);
1043 
1044  wxString msg;
1045  if (g_bSoftwareGL) msg.Printf(_T("OpenGL-> Software OpenGL"));
1046  msg.Printf(_T("OpenGL-> Renderer String: "));
1047  msg += m_renderer;
1048  wxLogMessage(msg);
1049 
1050  if (ps52plib) ps52plib->SetGLRendererString(m_renderer);
1051 
1052  char version_string[80];
1053  strncpy(version_string, (char *)glGetString(GL_VERSION), 79);
1054  msg.Printf(_T("OpenGL-> Version reported: "));
1055  m_version = wxString(version_string, wxConvUTF8);
1056  msg += m_version;
1057  wxLogMessage(msg);
1058 
1059  char GLSL_version_string[80];
1060  strncpy(GLSL_version_string, (char *)glGetString(GL_SHADING_LANGUAGE_VERSION), 79);
1061  msg.Printf(_T("OpenGL-> GLSL Version reported: "));
1062  m_GLSLversion = wxString(GLSL_version_string, wxConvUTF8);
1063  msg += m_GLSLversion;
1064  wxLogMessage(msg);
1065 
1066 #ifndef __ANDROID__
1067 #ifndef __WXOSX__
1068  GLenum err = glewInit();
1069 #ifdef GLEW_ERROR_NO_GLX_DISPLAY
1070  if (GLEW_OK != err && GLEW_ERROR_NO_GLX_DISPLAY != err)
1071 #else
1072  if (GLEW_OK != err)
1073 #endif
1074  {
1075  printf("GLEW init failed: %s\n", glewGetErrorString(err));
1076  exit(1);
1077  }
1078  else
1079  {
1080  wxLogMessage("GLEW init success!n");
1081  }
1082 #endif
1083 #endif
1084 
1085  const GLubyte *ext_str = glGetString(GL_EXTENSIONS);
1086  m_extensions = wxString((const char *)ext_str, wxConvUTF8);
1087 
1088  // Set the minimum line width
1089  GLint parms[2];
1090 #ifndef USE_ANDROID_GLES2
1091  glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0]);
1092 #else
1093  glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
1094 #endif
1095  g_GLMinSymbolLineWidth = wxMax(parms[0], 1);
1096  g_GLMinCartographicLineWidth = wxMax(parms[0], 1);
1097 
1098  // Some GL renderers do a poor job of Anti-aliasing very narrow line
1099  // widths. This is most evident on rendered symbols which have horizontal
1100  // or vertical line segments Detect this case, and adjust the render
1101  // parameters.
1102 
1103  if (m_renderer.Upper().Find(_T("MESA")) != wxNOT_FOUND) {
1104  GLfloat parf;
1105  glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &parf);
1106 
1107  g_GLMinSymbolLineWidth = wxMax(((float)parms[0] + parf), 1);
1108  }
1109 
1110  s_b_useScissorTest = true;
1111  // the radeon x600 driver has buggy scissor test
1112  if (GetRendererString().Find(_T("RADEON X600")) != wxNOT_FOUND)
1113  s_b_useScissorTest = false;
1114 
1115  if (GetRendererString().Find(_T("GeForce")) !=
1116  wxNOT_FOUND) // GeForce GTX 1070
1117  s_b_useScissorTest = false;
1118 
1119  bool bad_stencil_code = false;
1120 
1121  // And for the lousy Unichrome drivers, too
1122  if (GetRendererString().Find(_T("UniChrome")) != wxNOT_FOUND)
1123  bad_stencil_code = true;
1124 
1125  // And for the lousy Mali drivers, too
1126  if (GetRendererString().Find(_T("Mali")) != wxNOT_FOUND)
1127  bad_stencil_code = true;
1128 
1129  // Stencil buffer test
1130  glEnable(GL_STENCIL_TEST);
1131  GLboolean stencil = glIsEnabled(GL_STENCIL_TEST);
1132  int sb;
1133  glGetIntegerv(GL_STENCIL_BITS, &sb);
1134  // printf("Stencil Buffer Available: %d\nStencil bits: %d\n", stencil,
1135  // sb);
1136  glDisable(GL_STENCIL_TEST);
1137 
1138  s_b_useStencil = false;
1139  if (stencil && (sb == 8)) s_b_useStencil = true;
1140 
1141  if (QueryExtension("GL_ARB_texture_non_power_of_two"))
1142  g_texture_rectangle_format = GL_TEXTURE_2D;
1143  else if (QueryExtension("GL_OES_texture_npot"))
1144  g_texture_rectangle_format = GL_TEXTURE_2D;
1145  else if (QueryExtension("GL_ARB_texture_rectangle"))
1146  g_texture_rectangle_format = GL_TEXTURE_RECTANGLE_ARB;
1147  wxLogMessage(wxString::Format(_T("OpenGL-> Texture rectangle format: %x"),
1148  g_texture_rectangle_format));
1149 
1150 #ifdef __ANDROID__
1151  g_texture_rectangle_format = GL_TEXTURE_2D;
1152 #endif
1153 
1154  // VBO??
1155  g_b_EnableVBO = true;
1156 
1157 #ifdef __ANDROID__
1158  g_b_EnableVBO = false;
1159 #endif
1160 
1161  if (g_b_EnableVBO)
1162  wxLogMessage(_T("OpenGL-> Using Vertexbuffer Objects"));
1163  else
1164  wxLogMessage(_T("OpenGL-> Vertexbuffer Objects unavailable"));
1165 
1166  // Can we use the stencil buffer in a FBO?
1167 #ifdef ocpnUSE_GLES
1168  m_b_useFBOStencil = QueryExtension("GL_OES_packed_depth_stencil");
1169 #else
1170  m_b_useFBOStencil = QueryExtension("GL_EXT_packed_depth_stencil") == GL_TRUE;
1171 #endif
1172 
1173 #ifndef USE_ANDROID_GLES2
1174  // On Intel Graphics platforms, don't use stencil buffer at all
1175  if (bad_stencil_code) s_b_useStencil = false;
1176 #endif
1177 
1178  g_GLOptions.m_bUseCanvasPanning = false;
1179 
1180  // TODO
1181  // Temporarily disable FBO on Windows, pending implementation of MSAA to buffers
1182 #ifdef __WXMSW__
1183  //m_b_DisableFBO = true;
1184 #endif
1185 
1186  // Accelerated pan is not used for MacOS Retina display
1187  // So there is no advantage to using FBO
1188  if (m_displayScale > 1)
1189  m_b_DisableFBO = true;
1190 
1191  // Maybe build FBO(s)
1192  BuildFBO();
1193 
1194 #ifndef __ANDROID__
1195 
1196  if (m_b_BuiltFBO) {
1197  // Check framebuffer completeness at the end of initialization.
1198  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
1199 
1200  glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
1201  g_texture_rectangle_format, m_cache_tex[0], 0);
1202 
1203  GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
1204  glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
1205 
1206  if (fb_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
1207  wxString msg;
1208  msg.Printf(_T(" OpenGL-> Framebuffer Incomplete: %08X"), fb_status);
1209  wxLogMessage(msg);
1210  m_b_DisableFBO = true;
1211  BuildFBO();
1212  }
1213  }
1214 #endif
1215 
1216  if (m_b_BuiltFBO && !m_b_useFBOStencil) s_b_useStencil = false;
1217 
1218  // If stencil seems to be a problem, force use of depth buffer clipping for
1219  // Area Patterns
1220  s_b_useStencilAP = s_b_useStencil & !bad_stencil_code;
1221 
1222 #ifdef USE_ANDROID_GLES2
1223  s_b_useStencilAP = s_b_useStencil; // required for GLES2 platform
1224 #endif
1225 
1226  // Check and determine if GLSL is to be used
1227  m_bUseGLSL = true;
1228 
1229  if (m_b_BuiltFBO) {
1230  wxLogMessage(_T("OpenGL-> Using Framebuffer Objects"));
1231 
1232  if (m_b_useFBOStencil)
1233  wxLogMessage(_T("OpenGL-> Using FBO Stencil buffer"));
1234  else
1235  wxLogMessage(_T("OpenGL-> FBO Stencil buffer unavailable"));
1236  } else
1237  wxLogMessage(_T("OpenGL-> Framebuffer Objects unavailable"));
1238 
1239  if (s_b_useStencil)
1240  wxLogMessage(_T("OpenGL-> Using Stencil buffer clipping"));
1241  else
1242  wxLogMessage(_T("OpenGL-> Using Depth buffer clipping"));
1243 
1244  if (s_b_useScissorTest && s_b_useStencil)
1245  wxLogMessage(_T("OpenGL-> Using Scissor Clipping"));
1246 
1247  /* we upload non-aligned memory */
1248  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1249 
1250  MipMap_ResolveRoutines();
1251  SetupCompression();
1252 
1253  wxString lwmsg;
1254  lwmsg.Printf(_T("OpenGL-> Minimum cartographic line width: %4.1f"),
1255  g_GLMinCartographicLineWidth);
1256  wxLogMessage(lwmsg);
1257  lwmsg.Printf(_T("OpenGL-> Minimum symbol line width: %4.1f"),
1258  g_GLMinSymbolLineWidth);
1259  wxLogMessage(lwmsg);
1260 
1261 
1262  if (!g_bGLexpert)
1263  g_GLOptions.m_bUseAcceleratedPanning = !m_b_DisableFBO && m_b_BuiltFBO;
1264 
1265 #ifdef USE_ANDROID_GLES2
1266  g_GLOptions.m_bUseAcceleratedPanning = true;
1267 #endif
1268 
1269  if (1) // for now upload all levels
1270  {
1271  int max_level = 0;
1272  int tex_dim = g_GLOptions.m_iTextureDimension;
1273  for (int dim = tex_dim; dim > 0; dim /= 2) max_level++;
1274  g_mipmap_max_level = max_level - 1;
1275  }
1276 
1277  // Android, even though using GLES, does not require all levels.
1278 #ifdef __ANDROID__
1279  g_mipmap_max_level = 4;
1280 #endif
1281 
1282  s_b_useFBO = m_b_BuiltFBO;
1283 
1284  // Inform the S52 PLIB of options determined
1285  if (ps52plib)
1286  ps52plib->SetGLOptions(s_b_useStencil, s_b_useStencilAP, s_b_useScissorTest,
1287  s_b_useFBO, g_b_EnableVBO,
1288  g_texture_rectangle_format,
1289  g_GLMinCartographicLineWidth,
1290  g_GLMinSymbolLineWidth );
1291 
1292  m_bsetup = true;
1293 
1294  SendJSONConfigMessage();
1295 }
1296 
1297 void glChartCanvas::SendJSONConfigMessage() {
1298  if (g_pi_manager) {
1299  wxJSONValue v;
1300  v[_T("setupComplete")] = m_bsetup;
1301  v[_T("useStencil")] = s_b_useStencil;
1302  v[_T("useStencilAP")] = s_b_useStencilAP;
1303  v[_T("useScissorTest")] = s_b_useScissorTest;
1304  v[_T("useFBO")] = s_b_useFBO;
1305  v[_T("useVBO")] = g_b_EnableVBO;
1306  v[_T("TextureRectangleFormat")] = g_texture_rectangle_format;
1307  wxString msg_id(_T("OCPN_OPENGL_CONFIG"));
1308  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
1309  }
1310 }
1311 void glChartCanvas::SetupCompression() {
1312  int dim = g_GLOptions.m_iTextureDimension;
1313 
1314 #ifdef __WXMSW__
1315  if (!::IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
1316  wxLogMessage(_T("OpenGL-> SSE2 Instruction set not available"));
1317  goto no_compression;
1318  }
1319 #endif
1320 
1321  g_uncompressed_tile_size = dim * dim * 4; // stored as 32bpp in vram
1322  if (!g_GLOptions.m_bTextureCompression) goto no_compression;
1323 
1324  g_raster_format = GL_RGB;
1325 
1326  // On GLES, we prefer OES_ETC1 compression, if available
1327 #ifdef ocpnUSE_GLES
1328  if (QueryExtension("GL_OES_compressed_ETC1_RGB8_texture")) {
1329  g_raster_format = GL_ETC1_RGB8_OES;
1330 
1331  wxLogMessage(_T("OpenGL-> Using oes etc1 compression"));
1332  }
1333 #endif
1334 
1335  if (GL_RGB == g_raster_format) {
1336  /* because s3tc is patented, many foss drivers disable
1337  support by default, however the extension dxt1 allows
1338  us to load this texture type which is enough because we
1339  compress in software using libsquish for superior quality anyway */
1340 
1341  if ((QueryExtension("GL_EXT_texture_compression_s3tc") ||
1342  QueryExtension("GL_EXT_texture_compression_dxt1"))
1343  ) {
1344  /* buggy opensource nvidia driver, renders incorrectly,
1345  workaround is to use format with alpha... */
1346  if (GetRendererString().Find(_T("Gallium")) != wxNOT_FOUND &&
1347  GetRendererString().Find(_T("NV")) != wxNOT_FOUND)
1348  g_raster_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1349  else
1350  g_raster_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1351 
1352  wxLogMessage(_T("OpenGL-> Using s3tc dxt1 compression"));
1353  } else if (QueryExtension("GL_3DFX_texture_compression_FXT1")
1354  ) {
1355  g_raster_format = GL_COMPRESSED_RGB_FXT1_3DFX;
1356 
1357  wxLogMessage(_T("OpenGL-> Using 3dfx fxt1 compression"));
1358  } else {
1359  wxLogMessage(_T("OpenGL-> No Useable compression format found"));
1360  goto no_compression;
1361  }
1362  }
1363 
1364 #ifdef ocpnUSE_GLES /* gles doesn't have GetTexLevelParameter */
1365  g_tile_size = 512 * 512 / 2; /* 4bpp */
1366 #else
1367  /* determine compressed size of a level 0 single tile */
1368  GLuint texture;
1369  glGenTextures(1, &texture);
1370  glBindTexture(GL_TEXTURE_2D, texture);
1371  glTexImage2D(GL_TEXTURE_2D, 0, g_raster_format, dim, dim, 0, GL_RGB,
1372  GL_UNSIGNED_BYTE, NULL);
1373  glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE,
1374  &g_tile_size);
1375  glDeleteTextures(1, &texture);
1376 #endif
1377 
1378  /* disable texture compression if the tile size is 0 */
1379  if (g_tile_size == 0) goto no_compression;
1380 
1381  wxLogMessage(wxString::Format(
1382  _T("OpenGL-> Compressed tile size: %dkb (%d:1)"), g_tile_size / 1024,
1383  g_uncompressed_tile_size / g_tile_size));
1384  return;
1385 
1386 no_compression:
1387  g_GLOptions.m_bTextureCompression = false;
1388 
1389  g_tile_size = g_uncompressed_tile_size;
1390  wxLogMessage(wxString::Format(_T("OpenGL-> Not Using compression")));
1391 }
1392 
1393 void glChartCanvas::OnPaint(wxPaintEvent &event) {
1394  wxPaintDC dc(this);
1395 
1396  if (!m_pcontext) return;
1397 
1398  Show(g_bopengl);
1399  if (!g_bopengl) {
1400  event.Skip();
1401  return;
1402  }
1403 
1404  SetCurrent(*m_pcontext);
1405 
1406  if (!m_bsetup) {
1407  SetupOpenGL();
1408 
1409  if (ps52plib) ps52plib->FlushSymbolCaches(ChartCtxFactory());
1410 
1411  m_bsetup = true;
1412  // g_bDebugOGL = true;
1413  }
1414 
1415  // Paint updates may have been externally disabled (temporarily, to avoid
1416  // Yield() recursion performance loss)
1417  if (!m_b_paint_enable) return;
1418  // Recursion test, sometimes seen on GTK systems when wxBusyCursor is
1419  // activated
1420  if (m_in_glpaint) return;
1421 
1422  // If necessary, reconfigure the S52 PLIB
1423  m_pParentCanvas->UpdateCanvasS52PLIBConfig();
1424 
1425  // if( m_pParentCanvas->VPoint.b_quilt ){ // quilted
1426  // if( !m_pParentCanvas->m_pQuilt ||
1427  // !m_pParentCanvas->m_pQuilt->IsComposed() )
1428  // return; // not ready
1429  //
1430  // if(m_pParentCanvas->m_pQuilt->IsQuiltVector()){
1431  // if(ps52plib->GetStateHash() !=
1432  // m_pParentCanvas->m_s52StateHash){
1433  // m_pParentCanvas->UpdateS52State();
1434  // m_pParentCanvas->m_s52StateHash =
1435  // ps52plib->GetStateHash();
1436  // }
1437  // }
1438  // }
1439 
1440  m_in_glpaint++;
1441  Render();
1442  m_in_glpaint--;
1443 }
1444 
1445 // These routines allow reusable coordinates
1446 bool glChartCanvas::HasNormalizedViewPort(const ViewPort &vp) {
1447  return false;
1448 #ifndef USE_ANDROID_GLES2
1449  return vp.m_projection_type == PROJECTION_MERCATOR ||
1450  vp.m_projection_type == PROJECTION_POLAR ||
1451  vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1452 #else
1453  return false;
1454 #endif
1455 }
1456 
1457 /* adjust the opengl transformation matrix so that
1458  points plotted using the identity viewport are correct.
1459  and all rotation translation and scaling is now done in opengl
1460 
1461  a central lat and lon of 0, 0 can be used, however objects on the far side of
1462  the world can be up to 3 meters off because limited floating point precision,
1463  and if the points cross 180 longitude then two passes will be required to
1464  render them correctly */
1465 #define NORM_FACTOR 4096.0
1466 void glChartCanvas::MultMatrixViewPort(ViewPort &vp, float lat, float lon) {
1467 #ifndef USE_ANDROID_GLES2
1468 
1469  wxPoint2DDouble point;
1470 
1471  switch (vp.m_projection_type) {
1472  case PROJECTION_MERCATOR:
1473  case PROJECTION_EQUIRECTANGULAR:
1474  case PROJECTION_WEB_MERCATOR:
1475  // m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, lat, lon, &point);
1476  point = vp.GetDoublePixFromLL(lat, lon);
1477  glTranslated(point.m_x, point.m_y, 0);
1478  glScaled(vp.view_scale_ppm / NORM_FACTOR, vp.view_scale_ppm / NORM_FACTOR,
1479  1);
1480  break;
1481 
1482  case PROJECTION_POLAR:
1483  // m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, vp.clat > 0 ? 90 : -90,
1484  // vp.clon, &point);
1485  point = vp.GetDoublePixFromLL(vp.clat > 0 ? 90 : -90, vp.clon);
1486  glTranslated(point.m_x, point.m_y, 0);
1487  glRotatef(vp.clon - lon, 0, 0, vp.clat);
1488  glScalef(vp.view_scale_ppm / NORM_FACTOR, vp.view_scale_ppm / NORM_FACTOR,
1489  1);
1490  glTranslatef(-vp.pix_width / 2, -vp.pix_height / 2, 0);
1491  break;
1492 
1493  default:
1494  printf("ERROR: Unhandled projection\n");
1495  }
1496 
1497  double rotation = vp.rotation;
1498 
1499  if (rotation) glRotatef(rotation * 180 / PI, 0, 0, 1);
1500 #endif
1501 }
1502 
1503 ViewPort glChartCanvas::NormalizedViewPort(const ViewPort &vp, float lat,
1504  float lon) {
1505  ViewPort cvp = vp;
1506 
1507  switch (vp.m_projection_type) {
1508  case PROJECTION_MERCATOR:
1509  case PROJECTION_EQUIRECTANGULAR:
1510  case PROJECTION_WEB_MERCATOR:
1511  cvp.clat = lat;
1512  break;
1513 
1514  case PROJECTION_POLAR:
1515  cvp.clat = vp.clat > 0 ? 90 : -90; // either north or south polar
1516  break;
1517 
1518  default:
1519  printf("ERROR: Unhandled projection\n");
1520  }
1521 
1522  cvp.clon = lon;
1523  cvp.view_scale_ppm = NORM_FACTOR;
1524  cvp.rotation = cvp.skew = 0;
1525  return cvp;
1526 }
1527 
1528 bool glChartCanvas::CanClipViewport(const ViewPort &vp) {
1529  return vp.m_projection_type == PROJECTION_MERCATOR ||
1530  vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1531  vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1532 }
1533 
1534 ViewPort glChartCanvas::ClippedViewport(const ViewPort &vp,
1535  const LLRegion &region) {
1536  if (!CanClipViewport(vp)) return vp;
1537 
1538  ViewPort cvp = vp;
1539  LLBBox bbox = region.GetBox();
1540 
1541  if (!bbox.GetValid()) return vp;
1542 
1543  /* region.GetBox() will always try to give coordinates from -180 to 180 but in
1544  the case where the viewport crosses the IDL, we actually want the clipped
1545  viewport to use coordinates outside this range to ensure the logic in the
1546  various rendering routines works the same here (with accelerated panning)
1547  as it does without, so we can adjust the coordinates here */
1548 
1549  if (bbox.GetMaxLon() < cvp.GetBBox().GetMinLon()) {
1550  bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() + 360, bbox.GetMaxLat(),
1551  bbox.GetMaxLon() + 360);
1552  cvp.SetBBoxDirect(bbox);
1553  } else if (bbox.GetMinLon() > cvp.GetBBox().GetMaxLon()) {
1554  bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() - 360, bbox.GetMaxLat(),
1555  bbox.GetMaxLon() - 360);
1556  cvp.SetBBoxDirect(bbox);
1557  } else
1558  cvp.SetBBoxDirect(bbox);
1559 
1560  return cvp;
1561 }
1562 
1563 void glChartCanvas::DrawStaticRoutesTracksAndWaypoints(ViewPort &vp) {
1564  if (!m_pParentCanvas->m_bShowNavobjects) return;
1565  ocpnDC dc(*this);
1566 
1567  for (Track* pTrackDraw : g_TrackList) {
1568  /* defer rendering active tracks until later */
1569  ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1570  if (pActiveTrack && pActiveTrack->IsRunning()) continue;
1571 
1572  TrackGui(*pTrackDraw).Draw(m_pParentCanvas, dc, vp, vp.GetBBox());
1573  }
1574 
1575  for (wxRouteListNode *node = pRouteList->GetFirst(); node;
1576  node = node->GetNext()) {
1577  Route *pRouteDraw = node->GetData();
1578 
1579  if (!pRouteDraw) continue;
1580 
1581  /* defer rendering active routes until later */
1582  if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) continue;
1583 
1584  /* defer rendering routes being edited until later */
1585  if (pRouteDraw->m_bIsBeingEdited) continue;
1586 
1587  RouteGui(*pRouteDraw).DrawGL(vp, m_pParentCanvas, dc);
1588 // pRouteDraw->DrawGL(vp, m_pParentCanvas, dc);
1589  }
1590 
1591  /* Waypoints not drawn as part of routes, and not being edited */
1592  if (vp.GetBBox().GetValid() && pWayPointMan) {
1593  for (wxRoutePointListNode *pnode =
1594  pWayPointMan->GetWaypointList()->GetFirst();
1595  pnode; pnode = pnode->GetNext()) {
1596  RoutePoint *pWP = pnode->GetData();
1597  if (pWP && (!pWP->m_bRPIsBeingEdited) && (!pWP->m_bIsInRoute))
1598  if (vp.GetBBox().ContainsMarge(pWP->m_lat, pWP->m_lon, .5))
1599  RoutePointGui(*pWP).DrawGL(vp, m_pParentCanvas, dc);
1600 // pWP->DrawGL(vp, m_pParentCanvas, dc);
1601  }
1602  }
1603 }
1604 
1605 void glChartCanvas::DrawDynamicRoutesTracksAndWaypoints(ViewPort &vp) {
1606  ocpnDC dc(*this);
1607 
1608  for (Track* pTrackDraw : g_TrackList) {
1609  ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1610  if (pActiveTrack && pActiveTrack->IsRunning())
1611  TrackGui(*pTrackDraw).Draw(m_pParentCanvas, dc, vp, vp.GetBBox());
1612  // We need Track::Draw() to dynamically render last (ownship) point.
1613  }
1614 
1615  for (wxRouteListNode *node = pRouteList->GetFirst(); node;
1616  node = node->GetNext()) {
1617  Route *pRouteDraw = node->GetData();
1618 
1619  int drawit = 0;
1620  if (!pRouteDraw) continue;
1621 
1622  /* Active routes */
1623  if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) drawit++;
1624 
1625  /* Routes being edited */
1626  if (pRouteDraw->m_bIsBeingEdited) drawit++;
1627 
1628  /* Routes Selected */
1629  if (pRouteDraw->IsSelected()) drawit++;
1630 
1631  if (drawit) {
1632  const LLBBox &vp_box = vp.GetBBox(), &test_box = pRouteDraw->GetBBox();
1633  if (!vp_box.IntersectOut(test_box))
1634  RouteGui(*pRouteDraw).DrawGL(vp, m_pParentCanvas, dc);
1635 // pRouteDraw->DrawGL(vp, m_pParentCanvas, dc);
1636  }
1637  }
1638 
1639  /* Waypoints not drawn as part of routes, which are being edited right now */
1640  if (vp.GetBBox().GetValid() && pWayPointMan) {
1641  for (wxRoutePointListNode *pnode =
1642  pWayPointMan->GetWaypointList()->GetFirst();
1643  pnode; pnode = pnode->GetNext()) {
1644  RoutePoint *pWP = pnode->GetData();
1645  if (pWP && pWP->m_bRPIsBeingEdited && !pWP->m_bIsInRoute)
1646  RoutePointGui(*pWP).DrawGL(vp, m_pParentCanvas, dc);
1647 // pWP->DrawGL(vp, m_pParentCanvas, dc);
1648  }
1649  }
1650 }
1651 
1652 static void GetLatLonCurveDist(const ViewPort &vp, float &lat_dist,
1653  float &lon_dist) {
1654  // This really could use some more thought, and possibly split at different
1655  // intervals based on chart skew and other parameters to optimize performance
1656  switch (vp.m_projection_type) {
1657  case PROJECTION_TRANSVERSE_MERCATOR:
1658  lat_dist = 4, lon_dist = 1;
1659  break;
1660  case PROJECTION_POLYCONIC:
1661  lat_dist = 2, lon_dist = 1;
1662  break;
1663  case PROJECTION_ORTHOGRAPHIC:
1664  lat_dist = 2, lon_dist = 2;
1665  break;
1666  case PROJECTION_POLAR:
1667  lat_dist = 180, lon_dist = 1;
1668  break;
1669  case PROJECTION_STEREOGRAPHIC:
1670  case PROJECTION_GNOMONIC:
1671  lat_dist = 2, lon_dist = 1;
1672  break;
1673  case PROJECTION_EQUIRECTANGULAR:
1674  // this is suboptimal because we don't care unless there is
1675  // a change in both lat AND lon (skewed chart)
1676  lat_dist = 2, lon_dist = 360;
1677  break;
1678  default:
1679  lat_dist = 180, lon_dist = 360;
1680  }
1681 }
1682 
1683 void glChartCanvas::RenderChartOutline(ocpnDC &dc, int dbIndex, ViewPort &vp) {
1684  if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_PLUGIN &&
1685  !ChartData->IsChartAvailable(dbIndex))
1686  return;
1687 
1688  /* quick bounds check */
1689  LLBBox box;
1690  ChartData->GetDBBoundingBox(dbIndex, box);
1691  if (!box.GetValid()) return;
1692 
1693  // Don't draw an outline in the case where the chart covers the entire world
1694  // */
1695  if (box.GetLonRange() == 360) return;
1696 
1697  LLBBox vpbox = vp.GetBBox();
1698 
1699  double lon_bias = 0;
1700  // chart is outside of viewport lat/lon bounding box
1701  if (box.IntersectOutGetBias(vp.GetBBox(), lon_bias)) return;
1702 
1703 
1704  wxColour color;
1705  if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
1706  color = GetGlobalColor(_T ( "YELO1" ));
1707  else if (ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
1708  color = GetGlobalColor(_T ( "GREEN2" ));
1709  else
1710  color = GetGlobalColor(_T ( "UINFR" ));
1711 
1712 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1713  float plylat, plylon;
1714 
1715  if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
1716 
1717  glColor3ub(color.Red(), color.Green(), color.Blue());
1718  glLineWidth(g_GLMinSymbolLineWidth);
1719 
1720  float lat_dist, lon_dist;
1721  GetLatLonCurveDist(vp, lat_dist, lon_dist);
1722 
1723  // Are there any aux ply entries?
1724  int nAuxPlyEntries = ChartData->GetnAuxPlyEntries(dbIndex), nPly;
1725  int j = 0;
1726  do {
1727  if (nAuxPlyEntries)
1728  nPly = ChartData->GetDBAuxPlyPoint(dbIndex, 0, j, 0, 0);
1729  else
1730  nPly = ChartData->GetDBPlyPoint(dbIndex, 0, &plylat, &plylon);
1731 
1732  bool begin = false, sml_valid = false;
1733  double sml[2];
1734  float lastplylat = 0.0;
1735  float lastplylon = 0.0;
1736  // modulo is undefined for zero (compiler can use a div operation)
1737  int modulo = (nPly == 0) ? 1 : nPly;
1738  for (int i = 0; i < nPly + 1; i++) {
1739  if (nAuxPlyEntries)
1740  ChartData->GetDBAuxPlyPoint(dbIndex, i % modulo, j, &plylat, &plylon);
1741  else
1742  ChartData->GetDBPlyPoint(dbIndex, i % modulo, &plylat, &plylon);
1743 
1744  plylon += lon_bias;
1745 
1746  if (lastplylon - plylon > 180)
1747  lastplylon -= 360;
1748  else if (lastplylon - plylon < -180)
1749  lastplylon += 360;
1750 
1751  int splits;
1752  if (i == 0)
1753  splits = 1;
1754  else {
1755  int lat_splits = floor(fabs(plylat - lastplylat) / lat_dist);
1756  int lon_splits = floor(fabs(plylon - lastplylon) / lon_dist);
1757  splits = wxMax(lat_splits, lon_splits) + 1;
1758  }
1759 
1760  double smj[2];
1761  if (splits != 1) {
1762  // must perform border interpolation in mercator space as this is what
1763  // the charts use
1764  toSM(plylat, plylon, 0, 0, smj + 0, smj + 1);
1765  if (!sml_valid) toSM(lastplylat, lastplylon, 0, 0, sml + 0, sml + 1);
1766  }
1767 
1768  for (double c = 0; c < splits; c++) {
1769  double lat, lon;
1770  if (c == splits - 1)
1771  lat = plylat, lon = plylon;
1772  else {
1773  double d = (double)(c + 1) / splits;
1774  fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
1775  0, 0, &lat, &lon);
1776  }
1777 
1778  wxPoint2DDouble s;
1779  m_pParentCanvas->GetDoubleCanvasPointPix(lat, lon, &s);
1780  if (!std::isnan(s.m_x)) {
1781  if (!begin) {
1782  begin = true;
1783  glBegin(GL_LINE_STRIP);
1784  }
1785  glVertex2f(s.m_x, s.m_y);
1786  } else if (begin) {
1787  glEnd();
1788  begin = false;
1789  }
1790  }
1791  if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
1792  lastplylat = plylat, lastplylon = plylon;
1793  }
1794 
1795  if (begin) glEnd();
1796 
1797  } while (++j < nAuxPlyEntries); // There are no aux Ply Point entries
1798 
1799  glDisable(GL_LINE_SMOOTH);
1800  // glDisable( GL_BLEND );
1801 
1802 #else
1803  double nominal_line_width_pix =
1804  wxMax(2.0, floor(m_pParentCanvas->GetPixPerMM() / 4));
1805 
1806  if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
1807  dc.SetPen(wxPen(GetGlobalColor(_T ( "YELO1" )), nominal_line_width_pix,
1808  wxPENSTYLE_SOLID));
1809 
1810  else if (ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
1811  dc.SetPen(wxPen(GetGlobalColor(_T ( "UINFG" )), nominal_line_width_pix,
1812  wxPENSTYLE_SOLID));
1813 
1814  else
1815  dc.SetPen(wxPen(GetGlobalColor(_T ( "UINFR" )), nominal_line_width_pix,
1816  wxPENSTYLE_SOLID));
1817 
1818  float plylat1, plylon1;
1819  int pixx1, pixy1;
1820 
1821  // Are there any aux ply entries?
1822  int nAuxPlyEntries = ChartData->GetnAuxPlyEntries(dbIndex);
1823  if (0 == nAuxPlyEntries) // There are no aux Ply Point entries
1824  {
1825  wxPoint r, r1;
1826  std::vector<int> points_vector;
1827 
1828  std::vector<float> vec = ChartData->GetReducedPlyPoints(dbIndex);
1829  int nPly = vec.size() / 2;
1830 
1831  if (nPly == 0) return;
1832 
1833  for (int i = 0; i < nPly; i++) {
1834  plylon1 = vec[i * 2];
1835  plylat1 = vec[i * 2 + 1];
1836 
1837  m_pParentCanvas->GetCanvasPointPix(plylat1, plylon1, &r1);
1838  pixx1 = r1.x;
1839  pixy1 = r1.y;
1840 
1841  points_vector.push_back(pixx1);
1842  points_vector.push_back(pixy1);
1843  }
1844 
1845  ChartData->GetDBPlyPoint(dbIndex, 0, &plylat1, &plylon1);
1846  plylon1 += lon_bias;
1847 
1848  m_pParentCanvas->GetCanvasPointPix(vec[1], vec[0], &r1);
1849  pixx1 = r1.x;
1850  pixy1 = r1.y;
1851 
1852  points_vector.push_back(pixx1);
1853  points_vector.push_back(pixy1);
1854 
1855  if (points_vector.size()) {
1856  std::vector<int>::iterator it = points_vector.begin();
1857  dc.DrawLines(points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
1858  }
1859  }
1860 
1861  else // Use Aux PlyPoints
1862  {
1863  wxPoint r, r1;
1864 
1865  for (int j = 0; j < nAuxPlyEntries; j++) {
1866  std::vector<int> points_vector;
1867 
1868  std::vector<float> vec = ChartData->GetReducedAuxPlyPoints(dbIndex, j);
1869  int nAuxPly = vec.size() / 2;
1870 
1871  if (nAuxPly == 0) continue;
1872 
1873  for (int i = 0; i < nAuxPly; i++) {
1874  plylon1 = vec[i * 2];
1875  plylat1 = vec[i * 2 + 1];
1876 
1877  m_pParentCanvas->GetCanvasPointPix(plylat1, plylon1, &r1);
1878  pixx1 = r1.x;
1879  pixy1 = r1.y;
1880 
1881  points_vector.push_back(pixx1);
1882  points_vector.push_back(pixy1);
1883  }
1884 
1885  m_pParentCanvas->GetCanvasPointPix(vec[1], vec[0], &r1);
1886  pixx1 = r1.x;
1887  pixy1 = r1.y;
1888 
1889  points_vector.push_back(pixx1);
1890  points_vector.push_back(pixy1);
1891 
1892  if (points_vector.size()) {
1893  std::vector<int>::iterator it = points_vector.begin();
1894  dc.DrawLines(points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
1895  }
1896  }
1897  }
1898 
1899 #endif
1900 }
1901 
1902 extern void CalcGridSpacing(float WindowDegrees, float &MajorSpacing,
1903  float &MinorSpacing);
1904 extern wxString CalcGridText(float latlon, float spacing, bool bPostfix);
1905 void glChartCanvas::GridDraw() {
1906  if (!m_pParentCanvas->m_bDisplayGrid) return;
1907 
1908  ViewPort &vp = m_pParentCanvas->GetVP();
1909 
1910  if (!vp.IsValid() || !vp.GetBBox().GetValid()) return;
1911 
1912  // TODO: make minor grid work all the time
1913  bool minorgrid =
1914  fabs(vp.rotation) < 0.0001 && vp.m_projection_type == PROJECTION_MERCATOR;
1915 
1916  double nlat, elon, slat, wlon;
1917  float lat, lon;
1918  float gridlatMajor, gridlatMinor, gridlonMajor, gridlonMinor;
1919  wxCoord w, h;
1920 
1921  wxColour GridColor = GetGlobalColor(_T ( "SNDG1" ));
1922 
1923  if (!m_gridfont.IsBuilt()) {
1924  double dpi_factor = g_BasePlatform->GetDisplayDIPMult(this);
1925  wxFont *dFont = FontMgr::Get().GetFont(_("GridText"), 0);
1926  wxFont font = *dFont;
1927  int font_size = wxMax(10, dFont->GetPointSize());
1928  font.SetPointSize(font_size * m_displayScale);
1929  font.SetWeight(wxFONTWEIGHT_NORMAL);
1930 
1931  m_gridfont.SetContentScaleFactor(OCPN_GetDisplayContentScaleFactor());
1932  m_gridfont.Build(font, 1, dpi_factor);
1933  }
1934  m_gridfont.SetColor(GridColor);
1935 
1936  w = vp.pix_width;
1937  h = vp.pix_height;
1938 
1939  LLBBox llbbox = vp.GetBBox();
1940  nlat = llbbox.GetMaxLat();
1941  slat = llbbox.GetMinLat();
1942  elon = llbbox.GetMaxLon();
1943  wlon = llbbox.GetMinLon();
1944 
1945  // calculate distance between latitude grid lines
1946  CalcGridSpacing(vp.view_scale_ppm, gridlatMajor, gridlatMinor);
1947  CalcGridSpacing(vp.view_scale_ppm, gridlonMajor, gridlonMinor);
1948 
1949  // if it is known the grid has straight lines it's a bit faster
1950  bool straight_latitudes = vp.m_projection_type == PROJECTION_MERCATOR ||
1951  vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1952  vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1953  bool straight_longitudes = vp.m_projection_type == PROJECTION_MERCATOR ||
1954  vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1955  vp.m_projection_type == PROJECTION_POLAR ||
1956  vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1957 
1958  double latmargin;
1959  if (straight_latitudes)
1960  latmargin = 0;
1961  else
1962  latmargin = gridlatMajor / 2; // don't draw on poles
1963 
1964  slat = wxMax(slat, -90 + latmargin);
1965  nlat = wxMin(nlat, 90 - latmargin);
1966 
1967  float startlat = ceil(slat / gridlatMajor) * gridlatMajor;
1968  float startlon = ceil(wlon / gridlonMajor) * gridlonMajor;
1969  float curved_step = wxMin(sqrt(5e-3 / vp.view_scale_ppm), 3);
1970 
1971  ocpnDC gldc(*this);
1972  wxPen *pen = wxThePenList->FindOrCreatePen(GridColor, g_GLMinSymbolLineWidth,
1973  wxPENSTYLE_SOLID);
1974  gldc.SetPen(*pen);
1975 
1976  // Draw Major latitude grid lines and text
1977 
1978  // calculate position of first major latitude grid line
1979  float lon_step = elon - wlon;
1980  if (!straight_latitudes) lon_step /= ceil(lon_step / curved_step);
1981 
1982  for (lat = startlat; lat < nlat; lat += gridlatMajor) {
1983  wxPoint2DDouble r, s;
1984  s.m_x = NAN;
1985 
1986  for (lon = wlon; lon < elon + lon_step / 2; lon += lon_step) {
1987  m_pParentCanvas->GetDoubleCanvasPointPix(lat, lon, &r);
1988  if (!std::isnan(s.m_x) && !std::isnan(r.m_x)) {
1989  gldc.DrawLine(s.m_x, s.m_y, r.m_x, r.m_y, false);
1990  }
1991  s = r;
1992  }
1993  }
1994 
1995  if (minorgrid) {
1996  // draw minor latitude grid lines
1997  for (lat = ceil(slat / gridlatMinor) * gridlatMinor; lat < nlat;
1998  lat += gridlatMinor) {
1999  wxPoint r;
2000  m_pParentCanvas->GetCanvasPointPix(lat, (elon + wlon) / 2, &r);
2001  gldc.DrawLine(0, r.y, 10, r.y, true);
2002  gldc.DrawLine(w - 10, r.y, w, r.y, false);
2003 
2004  lat = lat + gridlatMinor;
2005  }
2006  }
2007 
2008  // draw major longitude grid lines
2009  float lat_step = nlat - slat;
2010  if (!straight_longitudes) lat_step /= ceil(lat_step / curved_step);
2011 
2012  for (lon = startlon; lon < elon; lon += gridlonMajor) {
2013  wxPoint2DDouble r, s;
2014  s.m_x = NAN;
2015  for (lat = slat; lat < nlat + lat_step / 2; lat += lat_step) {
2016  m_pParentCanvas->GetDoubleCanvasPointPix(lat, lon, &r);
2017 
2018  if (!std::isnan(s.m_x) && !std::isnan(r.m_x)) {
2019  gldc.DrawLine(s.m_x, s.m_y, r.m_x, r.m_y, false);
2020  }
2021  s = r;
2022  }
2023  }
2024 
2025  if (minorgrid) {
2026  // draw minor longitude grid lines
2027  for (lon = ceil(wlon / gridlonMinor) * gridlonMinor; lon < elon;
2028  lon += gridlonMinor) {
2029  wxPoint r;
2030  m_pParentCanvas->GetCanvasPointPix((nlat + slat) / 2, lon, &r);
2031  gldc.DrawLine(r.x, 0, r.x, 10, false);
2032  gldc.DrawLine(r.x, h - 10, r.x, h, false);
2033  }
2034  }
2035 
2036  // draw text labels
2037  if( abs(vp.rotation) < .1){
2038  glEnable(GL_TEXTURE_2D);
2039  glEnable(GL_BLEND);
2040  for (lat = startlat; lat < nlat; lat += gridlatMajor) {
2041  if (fabs(lat - wxRound(lat)) < 1e-5) lat = wxRound(lat);
2042 
2043  wxString st =
2044  CalcGridText(lat, gridlatMajor, true); // get text for grid line
2045  int iy;
2046  m_gridfont.GetTextExtent(st, 0, &iy);
2047 
2048  if (straight_latitudes) {
2049  wxPoint r, s;
2050  m_pParentCanvas->GetCanvasPointPix(lat, elon, &r);
2051  m_pParentCanvas->GetCanvasPointPix(lat, wlon, &s);
2052 
2053  float x = 0, y = -1;
2054  y = (float)(r.y * s.x - s.y * r.x) / (s.x - r.x);
2055  if (y < 0 || y > h) {
2056  y = h - iy;
2057  x = (float)(r.x * s.y - s.x * r.y + (s.x - r.x) * y) / (s.y - r.y);
2058  }
2059 
2060  m_gridfont.RenderString(st, x, y);
2061  } else {
2062  // iteratively attempt to find where the latitude line crosses x=0
2063  wxPoint2DDouble r;
2064  double y1, y2, lat1, lon1, lat2, lon2;
2065 
2066  y1 = 0, y2 = vp.pix_height;
2067  double error = vp.pix_width, lasterror;
2068  int maxiters = 10;
2069  do {
2070  m_pParentCanvas->GetCanvasPixPoint(0, y1, lat1, lon1);
2071  m_pParentCanvas->GetCanvasPixPoint(0, y2, lat2, lon2);
2072 
2073  double y = y1 + (lat1 - lat) * (y2 - y1) / (lat1 - lat2);
2074 
2075  m_pParentCanvas->GetDoubleCanvasPointPix(
2076  lat, lon1 + (y1 - y) * (lon2 - lon1) / (y1 - y2), &r);
2077 
2078  if (fabs(y - y1) < fabs(y - y2))
2079  y1 = y;
2080  else
2081  y2 = y;
2082 
2083  lasterror = error;
2084  error = fabs(r.m_x);
2085  if (--maxiters == 0) break;
2086  } while (error > 1 && error < lasterror);
2087 
2088  if (error < 1 && r.m_y >= 0 && r.m_y <= vp.pix_height - iy)
2089  r.m_x = 0;
2090  else
2091  // just draw at center longitude
2092  m_pParentCanvas->GetDoubleCanvasPointPix(lat, vp.clon, &r);
2093 
2094  m_gridfont.RenderString(st, r.m_x, r.m_y);
2095  }
2096  }
2097 
2098  for (lon = startlon; lon < elon; lon += gridlonMajor) {
2099  if (fabs(lon - wxRound(lon)) < 1e-5) lon = wxRound(lon);
2100 
2101  wxPoint r, s;
2102  m_pParentCanvas->GetCanvasPointPix(nlat, lon, &r);
2103  m_pParentCanvas->GetCanvasPointPix(slat, lon, &s);
2104 
2105  float xlon = lon;
2106  if (xlon > 180.0)
2107  xlon -= 360.0;
2108  else if (xlon <= -180.0)
2109  xlon += 360.0;
2110 
2111  wxString st = CalcGridText(xlon, gridlonMajor, false);
2112  int ix;
2113  m_gridfont.GetTextExtent(st, &ix, 0);
2114 
2115  if (straight_longitudes) {
2116  float x = -1, y = 0;
2117  x = (float)(r.x * s.y - s.x * r.y) / (s.y - r.y);
2118  if (x < 0 || x > w) {
2119  x = w - ix;
2120  y = (float)(r.y * s.x - s.y * r.x + (s.y - r.y) * x) / (s.x - r.x);
2121  }
2122 
2123  m_gridfont.RenderString(st, x, y);
2124  } else {
2125  // iteratively attempt to find where the latitude line crosses x=0
2126  wxPoint2DDouble r;
2127  double x1, x2, lat1, lon1, lat2, lon2;
2128 
2129  x1 = 0, x2 = vp.pix_width;
2130  double error = vp.pix_height, lasterror;
2131  do {
2132  m_pParentCanvas->GetCanvasPixPoint(x1, 0, lat1, lon1);
2133  m_pParentCanvas->GetCanvasPixPoint(x2, 0, lat2, lon2);
2134 
2135  double x = x1 + (lon1 - lon) * (x2 - x1) / (lon1 - lon2);
2136 
2137  m_pParentCanvas->GetDoubleCanvasPointPix(
2138  lat1 + (x1 - x) * (lat2 - lat1) / (x1 - x2), lon, &r);
2139 
2140  if (fabs(x - x1) < fabs(x - x2))
2141  x1 = x;
2142  else
2143  x2 = x;
2144 
2145  lasterror = error;
2146  error = fabs(r.m_y);
2147  } while (error > 1 && error < lasterror);
2148 
2149  if (error < 1 && r.m_x >= 0 && r.m_x <= vp.pix_width - ix)
2150  r.m_y = 0;
2151  else
2152  // failure, instead just draw the text at center latitude
2153  m_pParentCanvas->GetDoubleCanvasPointPix(
2154  wxMin(wxMax(vp.clat, slat), nlat), lon, &r);
2155 
2156  m_gridfont.RenderString(st, r.m_x, r.m_y);
2157  }
2158  }
2159 
2160  glDisable(GL_TEXTURE_2D);
2161  glDisable(GL_BLEND);
2162  }
2163 }
2164 
2165 void glChartCanvas::DrawEmboss(ocpnDC &dc, emboss_data *emboss) {
2166  if (!emboss) return;
2167 
2168  int w = emboss->width, h = emboss->height;
2169 
2170  glEnable(GL_TEXTURE_2D);
2171 
2172  // render using opengl and alpha blending
2173  if (!emboss->gltexind) { /* upload to texture */
2174 
2175  emboss->glwidth = NextPow2(emboss->width);
2176  emboss->glheight = NextPow2(emboss->height);
2177 
2178  /* convert to luminance alpha map */
2179  int size = emboss->glwidth * emboss->glheight;
2180  char *data = new char[2 * size];
2181  for (int i = 0; i < h; i++) {
2182  for (int j = 0; j < emboss->glwidth; j++) {
2183  if (j < w) {
2184  data[2 * ((i * emboss->glwidth) + j)] =
2185  (char)(emboss->pmap[(i * w) + j] > 0 ? 0 : 255);
2186  data[2 * ((i * emboss->glwidth) + j) + 1] =
2187  (char)abs((emboss->pmap[(i * w) + j]));
2188  }
2189  }
2190  }
2191 
2192  glGenTextures(1, &emboss->gltexind);
2193  glBindTexture(GL_TEXTURE_2D, emboss->gltexind);
2194  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, emboss->glwidth,
2195  emboss->glheight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
2196  data);
2197  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2198  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2199 
2200  delete[] data;
2201  }
2202 
2203  glBindTexture(GL_TEXTURE_2D, emboss->gltexind);
2204 
2205  glEnable(GL_BLEND);
2206 
2207  int x = emboss->x, y = emboss->y;
2208 
2209  float wp = (float)w / emboss->glwidth;
2210  float hp = (float)h / emboss->glheight;
2211 
2212  float coords[8];
2213  float uv[8];
2214 
2215  // normal uv
2216  uv[0] = 0;
2217  uv[1] = 0;
2218  uv[2] = wp;
2219  uv[3] = 0;
2220  uv[4] = wp;
2221  uv[5] = hp;
2222  uv[6] = 0;
2223  uv[7] = hp;
2224 
2225  // pixels
2226  coords[0] = 0;
2227  coords[1] = 0;
2228  coords[2] = w;
2229  coords[3] = 0;
2230  coords[4] = w;
2231  coords[5] = h;
2232  coords[6] = 0;
2233  coords[7] = h;
2234 
2235  // FIXME(dave) Find a way to make this thing a little transparaent
2236  RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(), x, y, 0);
2237 
2238  glDisable(GL_BLEND);
2239  glDisable(GL_TEXTURE_2D);
2240 }
2241 
2242 void glChartCanvas::ShipDraw(ocpnDC &dc) {
2243  if (!m_pParentCanvas->GetVP().IsValid()) return;
2244  wxPoint lGPSPoint, lShipMidPoint, GPSOffsetPixels(0, 0);
2245 
2246  // COG/SOG may be undefined in NMEA data stream
2247  float pCog = std::isnan(gCog) ? 0 : gCog;
2248  float pSog = std::isnan(gSog) ? 0 : gSog;
2249 
2250  m_pParentCanvas->GetCanvasPointPix(gLat, gLon, &lGPSPoint);
2251  lShipMidPoint = lGPSPoint;
2252 
2253  // Draw the icon rotated to the COG
2254  // or to the Hdt if available
2255  float icon_hdt = pCog;
2256  if (!std::isnan(gHdt)) icon_hdt = gHdt;
2257 
2258  // COG may be undefined in NMEA data stream
2259  if (std::isnan(icon_hdt)) icon_hdt = 0.0;
2260 
2261  // Calculate the ownship drawing angle icon_rad using an assumed 10 minute
2262  // predictor
2263  double osd_head_lat, osd_head_lon;
2264  wxPoint osd_head_point;
2265 
2266  ll_gc_ll(gLat, gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat,
2267  &osd_head_lon);
2268 
2269  m_pParentCanvas->GetCanvasPointPix(osd_head_lat, osd_head_lon,
2270  &osd_head_point);
2271 
2272  float icon_rad = atan2f((float)(osd_head_point.y - lShipMidPoint.y),
2273  (float)(osd_head_point.x - lShipMidPoint.x));
2274  icon_rad += (float)PI;
2275 
2276  if (pSog < 0.2)
2277  icon_rad =
2278  ((icon_hdt + 90.) * PI / 180.) + m_pParentCanvas->GetVP().rotation;
2279 
2280  // Another draw test ,based on pixels, assuming the ship icon is a fixed
2281  // nominal size and is just barely outside the viewport ....
2282  BoundingBox bb_screen(0, 0, m_pParentCanvas->GetVP().pix_width,
2283  m_pParentCanvas->GetVP().pix_height);
2284 
2285  // TODO: fix to include actual size of boat that will be rendered
2286  int img_height = 0;
2287 
2288  if (bb_screen.PointInBox(lShipMidPoint, 20)) {
2289  if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
2290  if (g_GLOptions.m_GLPolygonSmoothing) glEnable(GL_POLYGON_SMOOTH);
2291 
2292  if (m_pParentCanvas->GetVP().chart_scale >
2293  300000) // According to S52, this should be 50,000
2294  {
2295  float scale_factor = 1.0;
2296  // Scale the generic icon to ChartScaleFactor, slightly softened....
2297  if ((g_ChartScaleFactorExp > 1.0) && (g_OwnShipIconType == 0))
2298  scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.1;
2299 
2300  float nominal_ownship_size_mm = m_pParentCanvas->m_display_size_mm / 44.0;
2301  nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2302  nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2303 
2304  scale_factor *= m_pParentCanvas->GetContentScaleFactor();
2305 
2306  float nominal_ownship_size_pixels =
2307  wxMax(20.0, m_pParentCanvas->GetPixPerMM() *
2308  nominal_ownship_size_mm); // nominal length, but not
2309  // less than 20 pixel
2310  float v = (nominal_ownship_size_pixels * scale_factor) / 3;
2311 
2312  wxPen ppSmallScaleShip;
2313  if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2314  ppSmallScaleShip = wxPen(GetGlobalColor(_T ( "URED" )), v / 5, wxPENSTYLE_SOLID);
2315  else
2316  ppSmallScaleShip = wxPen(GetGlobalColor(_T ( "YELO1" )), v / 5, wxPENSTYLE_SOLID);
2317  dc.SetPen(ppSmallScaleShip);
2318 
2319  dc.SetBrush(wxBrush(GetGlobalColor(_T ( "URED" )), wxBRUSHSTYLE_TRANSPARENT));
2320 
2321  // start with cross
2322  dc.DrawLine((-v * 1.2) + lShipMidPoint.x, lShipMidPoint.y,
2323  (v * 1.2) + lShipMidPoint.x, lShipMidPoint.y);
2324  dc.DrawLine(lShipMidPoint.x, (-v * 1.2) + lShipMidPoint.y,
2325  lShipMidPoint.x, (v * 1.2) + lShipMidPoint.y);
2326 
2327  // Two circles
2328  dc.StrokeCircle(lShipMidPoint.x, lShipMidPoint.y, v);
2329  dc.StrokeCircle(lShipMidPoint.x, lShipMidPoint.y, 0.6 * v);
2330  img_height = 20;
2331  } else {
2332  int draw_color = SHIP_INVALID;
2333  if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2334  draw_color = SHIP_NORMAL;
2335  else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2336  draw_color = SHIP_LOWACCURACY;
2337 
2338  if (!ownship_tex ||
2339  (draw_color !=
2340  ownship_color)) { /* initial run, create texture for ownship,
2341  also needed at colorscheme changes (not
2342  implemented) */
2343 
2344  ownship_color = draw_color;
2345 
2346  if (ownship_tex) glDeleteTextures(1, &ownship_tex);
2347 
2348  glGenTextures(1, &ownship_tex);
2349  glBindTexture(GL_TEXTURE_2D, ownship_tex);
2350 
2351  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2352  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2353 
2354  wxImage image;
2355  if (m_pParentCanvas->m_pos_image_user) {
2356  switch (draw_color) {
2357  case SHIP_INVALID:
2358  image = *m_pParentCanvas->m_pos_image_user_grey;
2359  break;
2360  case SHIP_NORMAL:
2361  image = *m_pParentCanvas->m_pos_image_user;
2362  break;
2363  case SHIP_LOWACCURACY:
2364  image = *m_pParentCanvas->m_pos_image_user_yellow;
2365  break;
2366  }
2367  } else {
2368  switch (draw_color) {
2369  case SHIP_INVALID:
2370  image = *m_pParentCanvas->m_pos_image_grey;
2371  break;
2372  case SHIP_NORMAL:
2373  image = *m_pParentCanvas->m_pos_image_red;
2374  break;
2375  case SHIP_LOWACCURACY:
2376  image = *m_pParentCanvas->m_pos_image_yellow;
2377  break;
2378  }
2379  }
2380 
2381  int w = image.GetWidth(), h = image.GetHeight();
2382  int glw = NextPow2(w), glh = NextPow2(h);
2383  ownship_size = wxSize(w, h);
2384  ownship_tex_size = wxSize(glw, glh);
2385 
2386  unsigned char *d = image.GetData();
2387  unsigned char *a = image.GetAlpha();
2388  unsigned char *e = new unsigned char[4 * w * h];
2389 
2390  if (d && e && a) {
2391  for (int p = 0; p < w * h; p++) {
2392  e[4 * p + 0] = d[3 * p + 0];
2393  e[4 * p + 1] = d[3 * p + 1];
2394  e[4 * p + 2] = d[3 * p + 2];
2395  e[4 * p + 3] = a[p];
2396  }
2397  }
2398  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glw, glh, 0, GL_RGBA,
2399  GL_UNSIGNED_BYTE, 0);
2400 
2401  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
2402  e);
2403  delete[] e;
2404  }
2405 
2406  /* establish ship color */
2407 #ifndef USE_ANDROID_GLES2
2408  if (m_pParentCanvas->m_pos_image_user)
2409  glColor4ub(255, 255, 255, 255);
2410  else if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2411  glColor4ub(255, 0, 0, 255);
2412  else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2413  glColor4ub(255, 255, 0, 255);
2414  else
2415  glColor4ub(128, 128, 128, 255);
2416 #endif
2417  float scale_factor_y = 1.0;
2418  float scale_factor_x = 1.0;
2419 
2420  int ownShipWidth = 22; // Default values from s_ownship_icon
2421  int ownShipLength = 84;
2422  lShipMidPoint = lGPSPoint;
2423 
2424  /* scaled ship? */
2425  if (g_OwnShipIconType != 0)
2426  m_pParentCanvas->ComputeShipScaleFactor(
2427  icon_hdt, ownShipWidth, ownShipLength, lShipMidPoint,
2428  GPSOffsetPixels, lGPSPoint, scale_factor_x, scale_factor_y);
2429 
2430  glEnable(GL_BLEND);
2431 
2432  int x = lShipMidPoint.x, y = lShipMidPoint.y;
2433 
2434  // Scale the generic icon to ChartScaleFactor, slightly softened....
2435  if ((g_ShipScaleFactorExp > 1.0) && (g_OwnShipIconType == 0)) {
2436  scale_factor_x = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2437  scale_factor_y = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2438  }
2439 
2440  // Correct for scaled displays, e.g. Retina
2441  scale_factor_x *= m_pParentCanvas->GetContentScaleFactor();
2442  scale_factor_y *= m_pParentCanvas->GetContentScaleFactor();
2443 
2444  // Set the size of the little circle showing the GPS reference position
2445  // Set a default early, adjust later based on render type
2446  float gps_circle_radius = 3.0;
2447 
2448  if (g_OwnShipIconType == 0) { // Default Bitmap
2449 
2450  glEnable(GL_TEXTURE_2D);
2451  glBindTexture(GL_TEXTURE_2D, ownship_tex);
2452 
2453  // We choose to render the ownship bitmap at roughly the same size( in
2454  // pixels ) as the DC mode renderer.
2455  // For ultra-high definition displays, we clamp the actual on-screen
2456  // size to be no smaller than 7.0 mm Similarly, for lo-res displays, we
2457  // limit the actual size to be no larger than 15 mm maximum.
2458 
2459  // Get bitmap height in pixels
2460  int image_height_bitmap = m_pParentCanvas->m_pos_image_red->GetHeight();
2461  if (m_pParentCanvas->m_pos_image_user)
2462  image_height_bitmap = m_pParentCanvas->m_pos_image_user->GetHeight();
2463 
2464  float nominal_ownship_size_mm =
2465  image_height_bitmap / m_pParentCanvas->GetPixPerMM();
2466 
2467  nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2468  nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2469 
2470  float nominal_ownship_size_pixels =
2471  m_pParentCanvas->GetPixPerMM() * nominal_ownship_size_mm;
2472 
2473 
2474  if (m_pParentCanvas->GetContentScaleFactor() == 1.0) {
2475  nominal_ownship_size_pixels = wxMax(
2476  20.0, nominal_ownship_size_pixels); // not less than 20 pixel
2477  }
2478 
2479  float h = nominal_ownship_size_pixels * scale_factor_y;
2480  float w = nominal_ownship_size_pixels * scale_factor_x *
2481  ownship_size.x / ownship_size.y;
2482  float glw = ownship_tex_size.x, glh = ownship_tex_size.y;
2483  float u = ownship_size.x / glw, v = ownship_size.y / glh;
2484 
2485  // printf("%g %g %g %g %g %g %g\n",
2486  // nominal_ownship_size_mm, nominal_ownship_size_pixels,
2487  // h, w, u, v, m_pParentCanvas->m_display_size_mm);
2488  // tweak GPS reference point indicator size
2489  gps_circle_radius = w / 5;
2490 
2491  float uv[8], coords[8];
2492  uv[0] = 0;
2493  uv[1] = 0;
2494  uv[2] = u;
2495  uv[3] = 0;
2496  uv[4] = u;
2497  uv[5] = v;
2498  uv[6] = 0;
2499  uv[7] = v;
2500 
2501  coords[0] = -w / 2;
2502  coords[1] = -h / 2;
2503  coords[2] = w / 2;
2504  coords[3] = -h / 2;
2505  coords[4] = w / 2;
2506  coords[5] = h / 2;
2507  coords[6] = -w / 2;
2508  coords[7] = h / 2;
2509 
2510  RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(), x, y,
2511  icon_rad - PI / 2);
2512 
2513  glDisable(GL_TEXTURE_2D);
2514  } else if (g_OwnShipIconType == 1) { // Scaled Bitmap
2515 
2516  glEnable(GL_TEXTURE_2D);
2517  glBindTexture(GL_TEXTURE_2D, ownship_tex);
2518 
2519  float nominal_ownship_size_pixels_y = 84;
2520  float nominal_ownship_size_pixels_x = 22;
2521 
2522  float h = nominal_ownship_size_pixels_y * scale_factor_y;
2523  float w = nominal_ownship_size_pixels_x * scale_factor_x;
2524 
2525  float u = (float)ownship_size.x / ownship_tex_size.x,
2526  v = (float)ownship_size.y / ownship_tex_size.y;
2527 
2528  // tweak GPS reference point indicator size
2529  gps_circle_radius = w / 5;
2530 
2531  float uv[8], coords[8];
2532  uv[0] = 0;
2533  uv[1] = 0;
2534  uv[2] = u;
2535  uv[3] = 0;
2536  uv[4] = u;
2537  uv[5] = v;
2538  uv[6] = 0;
2539  uv[7] = v;
2540 
2541  coords[0] = -w / 2;
2542  coords[1] = -h / 2;
2543  coords[2] = w / 2;
2544  coords[3] = -h / 2;
2545  coords[4] = w / 2;
2546  coords[5] = h / 2;
2547  coords[6] = -w / 2;
2548  coords[7] = h / 2;
2549 
2550  RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(), x, y,
2551  icon_rad - PI / 2);
2552 
2553  glDisable(GL_TEXTURE_2D);
2554  } else if (g_OwnShipIconType == 2) { // Scaled Vector
2555  // static const GLint s_ownship_icon[] = { 5, -42, 11,
2556  // -28, 11, 42, -11, 42,
2557  // -11, -28, -5,
2558  // -42, -11, 0,
2559  // 11, 0, 0, 42,
2560  // 0, -42 };
2561 
2562  wxPoint shipPoints[6];
2563 
2564  wxColour colour = m_pParentCanvas->ShipColor();
2565  wxPen ppPen(*wxBLACK, 1);
2566  wxBrush ppBrush(colour);
2567  dc.SetPen(ppPen);
2568  dc.SetBrush(ppBrush);
2569 
2570  shipPoints[0].x = 0 * scale_factor_x;
2571  shipPoints[0].y = -28 * scale_factor_y;
2572  shipPoints[1].x = 11 * scale_factor_x;
2573  shipPoints[1].y = -28 * scale_factor_y;
2574  shipPoints[2].x = 11 * scale_factor_x;
2575  shipPoints[2].y = 42 * scale_factor_y;
2576  shipPoints[3].x = 0 * scale_factor_x;
2577  shipPoints[3].y = 42 * scale_factor_y;
2578  dc.DrawPolygon(4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1,
2579  icon_rad - PI / 2);
2580 
2581  shipPoints[0].x = 0 * scale_factor_x;
2582  shipPoints[0].y = -42 * scale_factor_y;
2583  shipPoints[1].x = 5 * scale_factor_x;
2584  shipPoints[1].y = -42 * scale_factor_y;
2585  shipPoints[2].x = 11 * scale_factor_x;
2586  shipPoints[2].y = -28 * scale_factor_y;
2587  shipPoints[3].x = 0 * scale_factor_x;
2588  shipPoints[3].y = -28 * scale_factor_y;
2589  dc.DrawPolygon(4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1,
2590  icon_rad - PI / 2);
2591 
2592  shipPoints[0].x = 0 * scale_factor_x;
2593  shipPoints[0].y = -28 * scale_factor_y;
2594  shipPoints[1].x = -11 * scale_factor_x;
2595  shipPoints[1].y = -28 * scale_factor_y;
2596  shipPoints[2].x = -11 * scale_factor_x;
2597  shipPoints[2].y = 42 * scale_factor_y;
2598  shipPoints[3].x = 0 * scale_factor_x;
2599  shipPoints[3].y = 42 * scale_factor_y;
2600  dc.DrawPolygon(4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1,
2601  icon_rad - PI / 2);
2602 
2603  shipPoints[0].x = 0 * scale_factor_x;
2604  shipPoints[0].y = -42 * scale_factor_y;
2605  shipPoints[1].x = -5 * scale_factor_x;
2606  shipPoints[1].y = -42 * scale_factor_y;
2607  shipPoints[2].x = -11 * scale_factor_x;
2608  shipPoints[2].y = -28 * scale_factor_y;
2609  shipPoints[3].x = 0 * scale_factor_x;
2610  shipPoints[3].y = -28 * scale_factor_y;
2611  dc.DrawPolygon(4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1,
2612  icon_rad - PI / 2);
2613 
2614  // draw with cross
2615  double p1x = -11 * scale_factor_x;
2616  double p2x = 11 * scale_factor_x;
2617  double p1y = 0;
2618  double p2y = 0;
2619  double p1xr =
2620  ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2621  double p2xr =
2622  ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2623  double p1yr =
2624  ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2625  double p2yr =
2626  ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2627  dc.DrawLine(p1xr + lShipMidPoint.x, p1yr + lShipMidPoint.y,
2628  p2xr + lShipMidPoint.x, p2yr + lShipMidPoint.y);
2629 
2630  p1x = 0;
2631  p2x = 0;
2632  p1y = -42 * scale_factor_y;
2633  p2y = 42 * scale_factor_y;
2634  p1xr = ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2635  p2xr = ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2636  p1yr = ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2637  p2yr = ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2638  dc.DrawLine(p1xr + lShipMidPoint.x, p1yr + lShipMidPoint.y,
2639  p2xr + lShipMidPoint.x, p2yr + lShipMidPoint.y);
2640  }
2641 
2642  img_height = ownShipLength * scale_factor_y;
2643 
2644  // Reference point, where the GPS antenna is
2645  if (m_pParentCanvas->m_pos_image_user) gps_circle_radius = 1;
2646 
2647  float cx = lGPSPoint.x, cy = lGPSPoint.y;
2648  wxPen ppPen1(GetGlobalColor(_T ( "UBLCK" )), 1, wxPENSTYLE_SOLID);
2649  dc.SetPen(ppPen1);
2650  dc.SetBrush(wxBrush(GetGlobalColor(_T ( "CHWHT" ))));
2651 
2652  dc.StrokeCircle(cx, cy, gps_circle_radius);
2653  }
2654 
2655  // glDisableClientState(GL_VERTEX_ARRAY);
2656  glDisable(GL_LINE_SMOOTH);
2657  glDisable(GL_POLYGON_SMOOTH);
2658  glDisable(GL_BLEND);
2659  }
2660 
2661  m_pParentCanvas->ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels,
2662  lGPSPoint);
2663 }
2664 
2665 void glChartCanvas::DrawFloatingOverlayObjects(ocpnDC &dc) {
2666  ViewPort &vp = m_pParentCanvas->GetVP();
2667 
2668  // Draw any active or selected routes now
2669  extern Routeman *g_pRouteMan;
2670  // extern Track *g_pActiveTrack;
2671  Route *active_route = g_pRouteMan->GetpActiveRoute();
2672 
2673  // if( active_route ) active_route->DrawGL( vp, region );
2674  // if( g_pActiveTrack ) g_pActiveTrack->Draw( dc, vp );
2675  // if( m_pParentCanvas->m_pSelectedRoute )
2676  // m_pParentCanvas->m_pSelectedRoute->DrawGL( vp, region );
2677 
2678  GridDraw();
2679 
2680  g_overlayCanvas = m_pParentCanvas;
2681  if (g_pi_manager) {
2682  g_pi_manager->SendViewPortToRequestingPlugIns(vp);
2683  g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2684  m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_LEGACY);
2685  }
2686 
2687  // all functions called with m_pParentCanvas-> are still slow because they go
2688  // through ocpndc
2689  AISDrawAreaNotices(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2690 
2691  m_pParentCanvas->DrawAnchorWatchPoints(dc);
2692  AISDraw(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2693  ShipDraw(dc);
2694  m_pParentCanvas->AlertDraw(dc);
2695 
2696  m_pParentCanvas->RenderVisibleSectorLights(dc);
2697 
2698  m_pParentCanvas->RenderRouteLegs(dc);
2699  m_pParentCanvas->RenderShipToActive(dc, true);
2700  m_pParentCanvas->ScaleBarDraw(dc);
2701  s57_DrawExtendedLightSectorsGL(dc, m_pParentCanvas->VPoint,
2702  m_pParentCanvas->extendedSectorLegs);
2703  if (g_pi_manager) {
2704  g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2705  m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_SHIPS);
2706  }
2707 }
2708 
2709 void glChartCanvas::DrawChartBar(ocpnDC &dc) {
2710  if (m_pParentCanvas->GetPiano()){
2711 
2712  int canvas_height = GetClientSize().y;
2713  canvas_height *= m_displayScale;
2714 
2715  m_pParentCanvas->GetPiano()->DrawGL(
2716  canvas_height -
2717  m_pParentCanvas->GetPiano()->GetHeight());
2718  }
2719 }
2720 
2721 void glChartCanvas::DrawQuiting() {
2722 #ifndef USE_ANDROID_GLES2
2723  GLubyte pattern[8][8];
2724  for (int y = 0; y < 8; y++)
2725  for (int x = 0; x < 8; x++) pattern[y][x] = (y == x) * 255;
2726 
2727  glEnable(GL_BLEND);
2728  glEnable(GL_TEXTURE_2D);
2729  glBindTexture(GL_TEXTURE_2D, 0);
2730 
2731  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2732  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2733  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2734 
2735  glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 8, 8, 0, GL_ALPHA, GL_UNSIGNED_BYTE,
2736  pattern);
2737  glColor3f(0, 0, 0);
2738 
2739  float x = GetSize().x, y = GetSize().y;
2740  float u = x / 8, v = y / 8;
2741 
2742  glBegin(GL_QUADS);
2743  glTexCoord2f(0, 0);
2744  glVertex2f(0, 0);
2745  glTexCoord2f(0, v);
2746  glVertex2f(0, y);
2747  glTexCoord2f(u, v);
2748  glVertex2f(x, y);
2749  glTexCoord2f(u, 0);
2750  glVertex2f(x, 0);
2751  glEnd();
2752 
2753  glDisable(GL_TEXTURE_2D);
2754  glDisable(GL_BLEND);
2755 #endif
2756 }
2757 
2758 void glChartCanvas::DrawCloseMessage(wxString msg) {
2759 #ifndef USE_ANDROID_GLES2
2760 
2761  if (1) {
2762  wxFont *pfont = FontMgr::Get().FindOrCreateFont(
2763  12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
2764 
2765  TexFont texfont;
2766 
2767  texfont.Build(*pfont, 1, 1);
2768  int w, h;
2769  texfont.GetTextExtent(msg, &w, &h);
2770  h += 2;
2771  int yp = m_pParentCanvas->GetVP().pix_height / 2;
2772  int xp = (m_pParentCanvas->GetVP().pix_width - w) / 2;
2773 
2774  glColor3ub(243, 229, 47);
2775 
2776  glBegin(GL_QUADS);
2777  glVertex2i(xp, yp);
2778  glVertex2i(xp + w, yp);
2779  glVertex2i(xp + w, yp + h);
2780  glVertex2i(xp, yp + h);
2781  glEnd();
2782 
2783  glEnable(GL_BLEND);
2784 
2785  glColor3ub(0, 0, 0);
2786  glEnable(GL_TEXTURE_2D);
2787  texfont.RenderString(msg, xp, yp);
2788  glDisable(GL_TEXTURE_2D);
2789  glDisable(GL_BLEND);
2790  }
2791 #endif
2792 }
2793 
2794 GLShaderProgram *pStaticShader;
2795 
2796 static std::list<double *> combine_work_data;
2797 static void combineCallbackD(GLdouble coords[3], GLdouble *vertex_data[4],
2798  GLfloat weight[4], GLdouble **dataOut) {
2799  double *vertex = new double[3];
2800  combine_work_data.push_back(vertex);
2801  memcpy(vertex, coords, 3 * (sizeof *coords));
2802  *dataOut = vertex;
2803 }
2804 
2805 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2806 void vertexCallbackD_GLSL(GLvoid *vertex) {
2807  // Grow the work buffer if necessary
2808  if (s_tess_vertex_idx > s_tess_buf_len - 8) {
2809  int new_buf_len = s_tess_buf_len + 100;
2810  GLfloat *tmp = s_tess_work_buf;
2811 
2812  s_tess_work_buf =
2813  (GLfloat *)realloc(s_tess_work_buf, new_buf_len * sizeof(GLfloat));
2814  if (NULL == s_tess_work_buf) {
2815  free(tmp);
2816  tmp = NULL;
2817  } else
2818  s_tess_buf_len = new_buf_len;
2819  }
2820 
2821  GLdouble *pointer = (GLdouble *)vertex;
2822 
2823  s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[0];
2824  s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[1];
2825 
2826  s_nvertex++;
2827 }
2828 
2829 void beginCallbackD_GLSL(GLenum mode) {
2830  s_tess_vertex_idx_this = s_tess_vertex_idx;
2831  s_tess_mode = mode;
2832  s_nvertex = 0;
2833 }
2834 
2835 void endCallbackD_GLSL() {
2836  GLShaderProgram *shader = pStaticShader;
2837  shader->Bind();
2838 
2839  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)s_tessVP.vp_matrix_transform);
2840 
2841  mat4x4 identityMatrix;
2842  mat4x4_identity(identityMatrix);
2843  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)identityMatrix);
2844 
2845  // Use color stored in static variable.
2846  float colorv[4];
2847  colorv[0] = s_regionColor.Red() / float(256);
2848  colorv[1] = s_regionColor.Green() / float(256);
2849  colorv[2] = s_regionColor.Blue() / float(256);
2850  colorv[3] = s_regionColor.Alpha() / float(256);
2851  shader->SetUniform4fv("color", colorv);
2852 
2853  float *bufPt = &s_tess_work_buf[s_tess_vertex_idx_this];
2854  shader->SetAttributePointerf("position", bufPt);
2855 
2856  glDrawArrays(s_tess_mode, 0, s_nvertex);
2857 
2858  shader->UnBind();
2859 
2860 }
2861 #else
2862 void vertexCallbackD(GLvoid *vertex)
2863 {
2864  glVertex3dv( (GLdouble *)vertex);
2865 }
2866 
2867 void beginCallbackD( GLenum mode)
2868 {
2869  glBegin( mode );
2870 }
2871 
2872 void endCallbackD()
2873 {
2874  glEnd();
2875 }
2876 
2877 #endif
2878 
2879 void glChartCanvas::DrawRegion(ViewPort &vp, const LLRegion &region) {
2880  float lat_dist, lon_dist;
2881  GetLatLonCurveDist(vp, lat_dist, lon_dist);
2882 
2883  GLUtesselator *tobj = gluNewTess();
2884  if (!pStaticShader)
2885  pStaticShader = GetStaticTriShader();
2886 
2887 
2888 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2889  gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD_GLSL);
2890  gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD_GLSL);
2891  gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD_GLSL);
2892  gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2893  s_tessVP = vp;
2894 
2895 #else
2896  gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD);
2897  gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD);
2898  gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD);
2899  gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2900 #endif
2901 
2902  gluTessNormal(tobj, 0, 0, 1);
2903 
2904  gluTessBeginPolygon(tobj, NULL);
2905  for (std::list<poly_contour>::const_iterator i = region.contours.begin();
2906  i != region.contours.end(); i++) {
2907  gluTessBeginContour(tobj);
2908  contour_pt l = *i->rbegin();
2909  double sml[2];
2910  bool sml_valid = false;
2911  for (poly_contour::const_iterator j = i->begin(); j != i->end(); j++) {
2912  int lat_splits = floor(fabs(j->y - l.y) / lat_dist);
2913  int lon_splits = floor(fabs(j->x - l.x) / lon_dist);
2914  int splits = wxMax(lat_splits, lon_splits) + 1;
2915 
2916  double smj[2];
2917  if (splits != 1) {
2918  // must perform border interpolation in mercator space as this is what
2919  // the charts use
2920  toSM(j->y, j->x, 0, 0, smj + 0, smj + 1);
2921  if (!sml_valid) toSM(l.y, l.x, 0, 0, sml + 0, sml + 1);
2922  }
2923 
2924  for (int i = 0; i < splits; i++) {
2925  double lat, lon;
2926  if (i == splits - 1)
2927  lat = j->y, lon = j->x;
2928  else {
2929  double d = (double)(i + 1) / splits;
2930  fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
2931  0, 0, &lat, &lon);
2932  }
2933  wxPoint2DDouble q = vp.GetDoublePixFromLL(lat, lon);
2934  if (std::isnan(q.m_x)) continue;
2935 
2936  double *p = new double[6];
2937 
2938  //p[0] = q.m_x, p[1] = q.m_y, p[2] = 0;
2939  // It is reasonable to use wxRound() here,
2940  // since we are working with pixel coordinates at this point
2941  p[0] = wxRound(q.m_x), p[1] = wxRound(q.m_y), p[2] = 0;
2942 
2943  //wxPoint pt = vp.GetPixFromLL(lat, lon);
2944  //p[0] = pt.x, p[1] = pt.y, p[2] = 0;
2945 
2946  gluTessVertex(tobj, p, p);
2947  combine_work_data.push_back(p);
2948  }
2949  l = *j;
2950 
2951  if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
2952  }
2953  gluTessEndContour(tobj);
2954  }
2955  gluTessEndPolygon(tobj);
2956 
2957  gluDeleteTess(tobj);
2958 
2959  for (std::list<double *>::iterator i = combine_work_data.begin();
2960  i != combine_work_data.end(); i++)
2961  delete[] * i;
2962  combine_work_data.clear();
2963 }
2964 
2965 /* set stencil buffer to clip in this region, and optionally clear using the
2966  * current color */
2967 void glChartCanvas::SetClipRegion(ViewPort &vp, const LLRegion &region) {
2968  glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // disable color buffer
2969 
2970  if (s_b_useStencil) {
2971  // Create a stencil buffer for clipping to the region
2972  glEnable(GL_STENCIL_TEST);
2973  glStencilMask(0x1); // write only into bit 0 of the stencil buffer
2974  glClear(GL_STENCIL_BUFFER_BIT);
2975 
2976  // We are going to write "1" into the stencil buffer wherever the region
2977  // is valid
2978  glStencilFunc(GL_ALWAYS, 1, 1);
2979  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2980  }
2981 //#ifndef USE_ANDROID_GLES2
2982 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
2983 
2984  else // Use depth buffer for clipping
2985  {
2986  glEnable(GL_DEPTH_TEST); // to enable writing to the depth buffer
2987  glDepthFunc(GL_ALWAYS); // to ensure everything you draw passes
2988  glDepthMask(GL_TRUE); // to allow writes to the depth buffer
2989 
2990  glClear(GL_DEPTH_BUFFER_BIT); // for a fresh start
2991 
2992  // Decompose the region into rectangles, and draw as quads
2993  // With z = 1
2994  // dep buffer clear = 1
2995  // 1 makes 0 in dep buffer, works
2996  // 0 make .5 in depth buffer
2997  // -1 makes 1 in dep buffer
2998 
2999  // Depth buffer runs from 0 at z = 1 to 1 at z = -1
3000  // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
3001  // Subsequent drawing at z=0 (depth = 0.5) will pass if using
3002  // glDepthFunc(GL_GREATER);
3003  glTranslatef(0, 0, .5);
3004  }
3005 #endif
3006 
3007  s_regionColor = wxColor(0,0,0,255);
3008  DrawRegion(vp, region);
3009 
3010  if (s_b_useStencil) {
3011  // Now set the stencil ops to subsequently render only where the stencil
3012  // bit is "1"
3013  glStencilFunc(GL_EQUAL, 1, 1);
3014  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
3015  }
3016 //#ifndef USE_ANDROID_GLES2
3017 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3018  else {
3019  glDepthFunc(GL_GREATER); // Set the test value
3020  glDepthMask(GL_FALSE); // disable depth buffer
3021  glTranslatef(0, 0, -.5); // reset translation
3022  }
3023 #endif
3024  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // re-enable color buffer
3025 }
3026 
3027 void glChartCanvas::SetClipRect(const ViewPort &vp, const wxRect &rect,
3028  bool b_clear) {
3029  /* for some reason this causes an occasional bug in depth mode, I cannot
3030  seem to solve it yet, so for now: */
3031  if (s_b_useStencil && s_b_useScissorTest) {
3032  wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
3033  if (rect != vp_rect) {
3034  glEnable(GL_SCISSOR_TEST);
3035  glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3036  rect.height);
3037  }
3038 #ifndef USE_ANDROID_GLES2
3039 #endif
3040  return;
3041  }
3042 }
3043 
3044 void glChartCanvas::DisableClipRegion() {
3045  glDisable(GL_SCISSOR_TEST);
3046  glDisable(GL_STENCIL_TEST);
3047  glDisable(GL_DEPTH_TEST);
3048 }
3049 
3050 void glChartCanvas::Invalidate() {
3051  /* should probably use a different flag for this */
3052  m_cache_vp.Invalidate();
3053 }
3054 
3055 void glChartCanvas::RenderRasterChartRegionGL(ChartBase *chart, ViewPort &vp,
3056  LLRegion &region) {
3057  ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB *>(chart);
3058  if (!pBSBChart) return;
3059 
3060  if (b_inCompressAllCharts)
3061  return; // don't want multiple texfactories to exist
3062 
3063  // Look for the texture factory for this chart
3064  wxString key = chart->GetHashKey();
3065 
3066  glTexFactory *pTexFact;
3067  ChartPathHashTexfactType &hash = g_glTextureManager->m_chart_texfactory_hash;
3068  ChartPathHashTexfactType::iterator ittf = hash.find(key);
3069 
3070  // Not Found ?
3071  if (ittf == hash.end()) {
3072  hash[key] = new glTexFactory(chart, g_raster_format);
3073  hash[key]->SetHashKey(key);
3074  }
3075 
3076  pTexFact = hash[key];
3077  pTexFact->SetLRUTime(++m_LRUtime);
3078 
3079  // for small scales, don't use normalized coordinates for accuracy (difference
3080  // is up to 3 meters error)
3081  bool use_norm_vp =
3082  glChartCanvas::HasNormalizedViewPort(vp) && pBSBChart->GetPPM() < 1;
3083  pTexFact->PrepareTiles(vp, use_norm_vp, pBSBChart);
3084 
3085  // For underzoom cases, we will define the textures as having their base
3086  // levels equivalent to a level "n" mipmap, where n is calculated, and is
3087  // always binary This way we can avoid loading much texture memory
3088 
3089  int base_level;
3090  if (vp.m_projection_type == PROJECTION_MERCATOR &&
3091  chart->GetChartProjectionType() == PROJECTION_MERCATOR) {
3092  double scalefactor = pBSBChart->GetRasterScaleFactor(vp);
3093  base_level = log(scalefactor) / log(2.0);
3094 
3095  if (base_level < 0) /* for overzoom */
3096  base_level = 0;
3097  if (base_level > g_mipmap_max_level) base_level = g_mipmap_max_level;
3098  } else
3099  base_level = 0; // base level should be computed per tile, for now load all
3100 
3101  /* setup opengl parameters */
3102  glEnable(GL_TEXTURE_2D);
3103 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3104  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3105 
3106  glEnableClientState(GL_VERTEX_ARRAY);
3107  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3108 
3109  if (use_norm_vp) {
3110  glPushMatrix();
3111  double lat, lon;
3112  pTexFact->GetCenter(lat, lon);
3113  MultMatrixViewPort(vp, lat, lon);
3114  }
3115 #endif
3116 
3117  LLBBox box = region.GetBox();
3118  int numtiles;
3119  int mem_used = 0;
3120  if (g_memCacheLimit > 0) {
3121  // GetMemoryStatus is slow on linux
3122  GetMemoryStatus(0, &mem_used);
3123  }
3124 
3125  glTexTile **tiles = pTexFact->GetTiles(numtiles);
3126  for (int i = 0; i < numtiles; i++) {
3127  glTexTile *tile = tiles[i];
3128  if (region.IntersectOut(tile->box)) {
3129  /* user setting is in MB while we count exact bytes */
3130  bool bGLMemCrunch =
3131  g_tex_mem_used > g_GLOptions.m_iTextureMemorySize * 1024 * 1024;
3132  if (bGLMemCrunch) pTexFact->DeleteTexture(tile->rect);
3133  } else {
3134  bool texture = pTexFact->PrepareTexture(base_level, tile->rect,
3135  global_color_scheme, mem_used);
3136 
3137  float *coords;
3138  if (use_norm_vp)
3139  coords = tile->m_coords;
3140  else {
3141  coords = new float[2 * tile->m_ncoords];
3142  for (int i = 0; i < tile->m_ncoords; i++) {
3143  wxPoint2DDouble p = vp.GetDoublePixFromLL(tile->m_coords[2 * i + 0],
3144  tile->m_coords[2 * i + 1]);
3145  coords[2 * i + 0] = p.m_x;
3146  coords[2 * i + 1] = p.m_y;
3147  }
3148  }
3149 
3150 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3151  RenderTextures(m_gldc, coords, tile->m_texcoords, 4, m_pParentCanvas->GetpVP());
3152 #else
3153  if (!texture) { // failed to load, draw red
3154  glDisable(GL_TEXTURE_2D);
3155  glColor3f(1, 0, 0);
3156  }
3157 
3158  glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tile->m_texcoords);
3159  glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
3160  glDrawArrays(GL_QUADS, 0, tile->m_ncoords);
3161 #endif
3162  if (!texture) glEnable(GL_TEXTURE_2D);
3163 
3164  if (!use_norm_vp) delete[] coords;
3165  }
3166  }
3167 
3168  glDisable(GL_TEXTURE_2D);
3169 
3170 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3171  if (use_norm_vp) glPopMatrix();
3172 
3173  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3174  glDisableClientState(GL_VERTEX_ARRAY);
3175 #endif
3176 }
3177 
3178 void glChartCanvas::RenderQuiltViewGL(ViewPort &vp,
3179  const OCPNRegion &rect_region) {
3180  if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3181  m_pParentCanvas->m_pQuilt->IsBusy())
3182  return;
3183 
3184  // render the quilt
3185  ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3186 
3187  // Check the first, smallest scale chart
3188  if (chart) {
3189  // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp )
3190  // )
3191  // chart = NULL;
3192  }
3193 
3194  LLRegion region = vp.GetLLRegion(rect_region);
3195 
3196  LLRegion rendered_region;
3197  while (chart) {
3198  // This test does not need to be done for raster charts, since
3199  // we can assume that texture binding is acceptably fast regardless of the
3200  // render region, and that the quilt zoom methods choose a reasonable
3201  // reference chart.
3202  if (chart->GetChartFamily() != CHART_FAMILY_RASTER) {
3203  // if( ! m_pParentCanvas->IsChartLargeEnoughToRender(
3204  // chart, vp ) ) {
3205  // chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3206  // continue;
3207  // }
3208  }
3209 
3210  QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3211  if (pqp->b_Valid) {
3212  LLRegion get_region = pqp->ActiveRegion;
3213  bool b_rendered = false;
3214 
3215  if (!pqp->b_overlay) {
3216  get_region.Intersect(region);
3217  if (!get_region.Empty()) {
3218  if (chart->GetChartFamily() == CHART_FAMILY_RASTER) {
3219  ChartBaseBSB *Patch_Ch_BSB = dynamic_cast<ChartBaseBSB *>(chart);
3220  if (Patch_Ch_BSB ) {
3221 
3222  SetClipRegion(vp, get_region /*pqp->quilt_region*/);
3223  RenderRasterChartRegionGL(chart, vp, pqp->ActiveRegion);
3224  DisableClipRegion();
3225 
3226  b_rendered = true;
3227  } else if (chart->GetChartType() == CHART_TYPE_MBTILES) {
3228  SetClipRegion(vp, pqp->ActiveRegion /*pqp->quilt_region*/);
3229  chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3230  get_region);
3231  DisableClipRegion();
3232  }
3233 
3234  } else if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3235  if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
3236  RenderNoDTA(vp, get_region);
3237  chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3238  get_region);
3239  } else {
3240  s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3241  if (Chs57) {
3242  if (Chs57->m_RAZBuilt) {
3243  RenderNoDTA(vp, get_region);
3244  Chs57->RenderRegionViewOnGLNoText(*m_pcontext, vp,
3245  rect_region, get_region);
3246  DisableClipRegion();
3247  } else {
3248  // The SENC is quesed for building, so..
3249  // Show GSHHS with compatible color scheme in the meantime.
3250  ocpnDC gldc(*this);
3251  const LLRegion &oregion = get_region;
3252  LLBBox box = oregion.GetBox();
3253 
3254  wxPoint p1 =
3255  vp.GetPixFromLL(box.GetMaxLat(), box.GetMinLon());
3256  wxPoint p2 =
3257  vp.GetPixFromLL(box.GetMaxLat(), box.GetMaxLon());
3258  wxPoint p3 =
3259  vp.GetPixFromLL(box.GetMinLat(), box.GetMaxLon());
3260  wxPoint p4 =
3261  vp.GetPixFromLL(box.GetMinLat(), box.GetMinLon());
3262 
3263  wxRect srect(p1.x, p1.y, p3.x - p1.x, p4.y - p2.y);
3264 
3265  bool world = false;
3266  ViewPort cvp = ClippedViewport(vp, get_region);
3267  if (m_pParentCanvas->GetWorldBackgroundChart()) {
3268  SetClipRegion(cvp, get_region);
3269  m_pParentCanvas->GetWorldBackgroundChart()->SetColorsDirect(
3270  GetGlobalColor(_T ( "LANDA" )),
3271  GetGlobalColor(_T ( "DEPMS" )));
3272  RenderWorldChart(gldc, cvp, srect, world);
3273  m_pParentCanvas->GetWorldBackgroundChart()->SetColorScheme(
3274  global_color_scheme);
3275  DisableClipRegion();
3276  }
3277  }
3278  } else {
3279  ChartPlugInWrapper *ChPI =
3280  dynamic_cast<ChartPlugInWrapper *>(chart);
3281  if (ChPI) {
3282  SetClipRegion(vp, get_region);
3283  RenderNoDTA(vp, get_region);
3284  ChPI->RenderRegionViewOnGLNoText(*m_pcontext, vp, rect_region,
3285  get_region);
3286  DisableClipRegion();
3287 
3288  } else {
3289  SetClipRegion(vp, get_region);
3290  RenderNoDTA(vp, get_region);
3291  chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3292  get_region);
3293  DisableClipRegion();
3294  }
3295  }
3296  }
3297  }
3298  }
3299  }
3300 
3301  if (b_rendered) {
3302  // LLRegion get_region = pqp->ActiveRegion;
3303  // get_region.Intersect( Region ); not technically
3304  // required?
3305  // rendered_region.Union(get_region);
3306  }
3307  }
3308 
3309  chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3310  }
3311 
3312  // Render any Overlay patches for s57 charts(cells)
3313  if (m_pParentCanvas->m_pQuilt->HasOverlays()) {
3314  ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3315  while (pch) {
3316  QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3317  if (pqp->b_Valid && pqp->b_overlay &&
3318  pch->GetChartFamily() == CHART_FAMILY_VECTOR) {
3319  LLRegion get_region = pqp->ActiveRegion;
3320 
3321  get_region.Intersect(region);
3322  if (!get_region.Empty()) {
3323  s57chart *Chs57 = dynamic_cast<s57chart *>(pch);
3324  if (Chs57)
3325  Chs57->RenderOverlayRegionViewOnGL(*m_pcontext, vp, rect_region,
3326  get_region);
3327  else {
3328  ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(pch);
3329  if (ChPI) {
3330  ChPI->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3331  get_region);
3332  }
3333  }
3334  }
3335  }
3336 
3337  pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3338  }
3339  }
3340 
3341  // Hilite rollover of standard chart key
3342  ViewPort vph = m_pParentCanvas->GetVP();
3343  for (auto &index : m_pParentCanvas->m_pQuilt->GetHiLiteIndexArray()) {
3344  const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
3345  LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetChartQuiltRegion(cte, vph);
3346 
3347  if (!hiregion.Empty()) {
3348  glEnable(GL_BLEND);
3349 
3350  double hitrans;
3351  switch (global_color_scheme) {
3352  case GLOBAL_COLOR_SCHEME_DAY:
3353  hitrans = .4;
3354  break;
3355  case GLOBAL_COLOR_SCHEME_DUSK:
3356  hitrans = .2;
3357  break;
3358  case GLOBAL_COLOR_SCHEME_NIGHT:
3359  hitrans = .1;
3360  break;
3361  default:
3362  hitrans = .4;
3363  break;
3364  }
3365 
3366 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3367 
3368  glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3369 #else
3370  s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3371 #endif
3372 
3373  DrawRegion(vp, hiregion);
3374 
3375  glDisable(GL_BLEND);
3376  }
3377  }
3378 
3379 #if 0
3380  LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
3381 
3382  if (!hiregion.Empty()) {
3383  glEnable(GL_BLEND);
3384 
3385  double hitrans;
3386  switch (global_color_scheme) {
3387  case GLOBAL_COLOR_SCHEME_DAY:
3388  hitrans = .4;
3389  break;
3390  case GLOBAL_COLOR_SCHEME_DUSK:
3391  hitrans = .2;
3392  break;
3393  case GLOBAL_COLOR_SCHEME_NIGHT:
3394  hitrans = .1;
3395  break;
3396  default:
3397  hitrans = .4;
3398  break;
3399  }
3400 
3401 //#ifndef USE_ANDROID_GLES2
3402 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3403 
3404  glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3405 #else
3406  s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3407 #endif
3408 
3409  DrawRegion(vp, hiregion);
3410 
3411  glDisable(GL_BLEND);
3412  }
3413 #endif
3414 
3415  m_pParentCanvas->m_pQuilt->SetRenderedVP(vp);
3416 }
3417 
3418 void glChartCanvas::RenderQuiltViewGLText(ViewPort &vp,
3419  const OCPNRegion &rect_region) {
3420  if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3421  m_pParentCanvas->m_pQuilt->IsBusy())
3422  return;
3423 
3424  // render the quilt
3425  ChartBase *chart = m_pParentCanvas->m_pQuilt->GetLargestScaleChart();
3426 
3427  LLRegion region = vp.GetLLRegion(rect_region);
3428 
3429  LLRegion rendered_region;
3430  while (chart) {
3431  QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3432  if (pqp->b_Valid) {
3433  LLRegion get_region = pqp->ActiveRegion;
3434 
3435  if (!pqp->b_overlay) {
3436  if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3437  s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3438  if (Chs57) {
3439  Chs57->RenderViewOnGLTextOnly(*m_pcontext, vp);
3440  } else {
3441  ChartPlugInWrapper *ChPI =
3442  dynamic_cast<ChartPlugInWrapper *>(chart);
3443  if (ChPI) {
3444  ChPI->RenderRegionViewOnGLTextOnly(*m_pcontext, vp, rect_region);
3445  }
3446  }
3447  }
3448  }
3449  }
3450 
3451  chart = m_pParentCanvas->m_pQuilt->GetNextSmallerScaleChart();
3452  }
3453 
3454  /*
3455  // Render any Overlay patches for s57 charts(cells)
3456  if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3457  ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3458  while( pch ) {
3459  QuiltPatch *pqp =
3460  m_pParentCanvas->m_pQuilt->GetCurrentPatch(); if( pqp->b_Valid &&
3461  pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) { LLRegion
3462  get_region = pqp->ActiveRegion;
3463 
3464  get_region.Intersect( region );
3465  if( !get_region.Empty() ) {
3466  s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3467  if( Chs57 )
3468  Chs57->RenderOverlayRegionViewOnGL( *m_pcontext,
3469  vp, rect_region, get_region );
3470  }
3471  }
3472 
3473  pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3474  }
3475  }
3476  */
3477 }
3478 
3479 void glChartCanvas::RenderCharts(ocpnDC &dc, const OCPNRegion &rect_region) {
3480  ViewPort &vp = m_pParentCanvas->VPoint;
3481 
3482  // Only for cm93 (not quilted), SetVPParms can change the valid region of the
3483  // chart we need to know this before rendering the chart so we can compute the
3484  // background region and nodta regions correctly. I would prefer to just
3485  // perform this here (or in SetViewPoint) for all vector charts instead of in
3486  // their render routine, but how to handle quilted cases?
3487  if (!vp.b_quilt &&
3488  m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_CM93COMP)
3489  static_cast<cm93compchart *>(m_pParentCanvas->m_singleChart)
3490  ->SetVPParms(vp);
3491 
3492  LLRegion chart_region;
3493  if (!vp.b_quilt &&
3494  (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_PLUGIN)) {
3495  if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3496  CHART_FAMILY_RASTER) {
3497  // We do this the hard way, since PlugIn Raster charts do not understand
3498  // LLRegion yet...
3499  double ll[8];
3500  ChartPlugInWrapper *cpw =
3501  dynamic_cast<ChartPlugInWrapper *>(m_pParentCanvas->m_singleChart);
3502  if (!cpw) return;
3503 
3504  cpw->chartpix_to_latlong(0, 0, ll + 0, ll + 1);
3505  cpw->chartpix_to_latlong(0, cpw->GetSize_Y(), ll + 2, ll + 3);
3506  cpw->chartpix_to_latlong(cpw->GetSize_X(), cpw->GetSize_Y(), ll + 4,
3507  ll + 5);
3508  cpw->chartpix_to_latlong(cpw->GetSize_X(), 0, ll + 6, ll + 7);
3509 
3510  // for now don't allow raster charts to cross both 0 meridian and IDL
3511  // (complicated to deal with)
3512  for (int i = 1; i < 6; i += 2)
3513  if (fabs(ll[i] - ll[i + 2]) > 180) {
3514  // we detect crossing idl here, make all longitudes positive
3515  for (int i = 1; i < 8; i += 2)
3516  if (ll[i] < 0) ll[i] += 360;
3517  break;
3518  }
3519 
3520  chart_region = LLRegion(4, ll);
3521  } else {
3522  Extent ext;
3523  m_pParentCanvas->m_singleChart->GetChartExtent(&ext);
3524 
3525  double ll[8] = {ext.SLAT, ext.WLON, ext.SLAT, ext.ELON,
3526  ext.NLAT, ext.ELON, ext.NLAT, ext.WLON};
3527  chart_region = LLRegion(4, ll);
3528  }
3529  } else
3530  chart_region = vp.b_quilt
3531  ? m_pParentCanvas->m_pQuilt->GetFullQuiltRegion()
3532  : m_pParentCanvas->m_singleChart->GetValidRegion();
3533 
3534  bool world_view = false;
3535  for (OCPNRegionIterator upd(rect_region); upd.HaveRects(); upd.NextRect()) {
3536  wxRect rect = upd.GetRect();
3537  LLRegion background_region = vp.GetLLRegion(rect);
3538  // Remove the valid chart area to find the region NOT covered by the
3539  // charts
3540  background_region.Subtract(chart_region);
3541 
3542  if (!background_region.Empty()) {
3543  ViewPort cvp = ClippedViewport(vp, background_region);
3544  SetClipRect(cvp, rect, false);
3545  RenderWorldChart(dc, cvp, rect, world_view);
3546  DisableClipRegion();
3547  }
3548  }
3549 
3550  if (vp.b_quilt)
3551  RenderQuiltViewGL(vp, rect_region);
3552  else {
3553  LLRegion region = vp.GetLLRegion(rect_region);
3554  if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3555  CHART_FAMILY_RASTER) {
3556  if (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_MBTILES)
3557  m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(
3558  *m_pcontext, vp, rect_region, region);
3559  else
3560  RenderRasterChartRegionGL(m_pParentCanvas->m_singleChart, vp, region);
3561  } else if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3562  CHART_FAMILY_VECTOR) {
3563  chart_region.Intersect(region);
3564  RenderNoDTA(vp, chart_region);
3565  m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(*m_pcontext, vp,
3566  rect_region, region);
3567  }
3568  }
3569  glUseProgram(0);
3570 }
3571 
3572 void glChartCanvas::RenderNoDTA(ViewPort &vp, const LLRegion &region,
3573  int transparency) {
3574  wxColour color = GetGlobalColor(_T ( "NODTA" ));
3575 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3576  if (color.IsOk())
3577  glColor4ub(color.Red(), color.Green(), color.Blue(), transparency);
3578  else
3579  glColor4ub(163, 180, 183, transparency);
3580 
3581  glEnable(GL_BLEND);
3582  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3583 
3584 #else
3585  // Store the color for tesselator callback pickup.
3586  s_regionColor = color;
3587 #endif
3588 
3589  DrawRegion(vp, region);
3590 }
3591 
3592 
3593 /* render world chart, but only in this rectangle */
3594 void glChartCanvas::RenderWorldChart(ocpnDC &dc, ViewPort &vp, wxRect &rect,
3595  bool &world_view) {
3596  // set gl color to water
3597  wxColour water = m_pParentCanvas->pWorldBackgroundChart->water;
3598 
3599  glEnable(GL_SCISSOR_TEST);
3600  glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3601  rect.height);
3602 
3603  // clear background
3604  if (!world_view) {
3605  if (!world_view) {
3606  int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
3607 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3608 
3609  GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
3610  shader->Bind();
3611 
3612  float colorv[4];
3613  colorv[0] = water.Red() / float(256);
3614  colorv[1] = water.Green() / float(256);
3615  colorv[2] = water.Blue() / float(256);
3616  colorv[3] = 1.0;
3617  shader->SetUniform4fv("color", colorv);
3618 
3619  float pf[8];
3620  pf[0] = x2;
3621  pf[1] = y1;
3622  pf[2] = x2;
3623  pf[3] = y2;
3624  pf[4] = x1;
3625  pf[5] = y1;
3626  pf[6] = x1;
3627  pf[7] = y2;
3628  shader->SetAttributePointerf("position", pf);
3629 
3630  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3631 
3632  shader->UnBind();
3633 
3634 #else
3635 #endif
3636  }
3637  }
3638 
3639  //m_pParentCanvas->pWorldBackgroundChart->RenderViewOnDC(dc, vp);
3640  gShapeBasemap.RenderViewOnDC(dc, vp);
3641 
3642  glDisable(GL_SCISSOR_TEST);
3643 
3644 }
3645 
3646 /* these are the overlay objects which move with the charts and
3647  are not frequently updated (not ships etc..)
3648 
3649  many overlay objects are fixed to a geographical location or
3650  grounded as opposed to the floating overlay objects. */
3651 void glChartCanvas::DrawGroundedOverlayObjects(ocpnDC &dc, ViewPort &vp) {
3652  m_pParentCanvas->RenderAllChartOutlines(dc, vp);
3653 
3654  DrawStaticRoutesTracksAndWaypoints(vp);
3655 
3656  DisableClipRegion();
3657 }
3658 
3659 void glChartCanvas::DrawGLTidesInBBox(ocpnDC &dc, LLBBox &BBox) {
3660  // At small scale, we render the Tide icon as a texture for best performance
3661  if (m_pParentCanvas->GetVP().chart_scale > 500000) {
3662  // Prepare the texture if necessary
3663 
3664  if (!m_tideTex) {
3665  wxBitmap bmp = m_pParentCanvas->GetTideBitmap();
3666  if (!bmp.Ok()) return;
3667 
3668  wxImage image = bmp.ConvertToImage();
3669  int w = image.GetWidth(), h = image.GetHeight();
3670 
3671  int tex_w, tex_h;
3672  if (g_texture_rectangle_format == GL_TEXTURE_2D)
3673  tex_w = w, tex_h = h;
3674  else
3675  tex_w = NextPow2(w), tex_h = NextPow2(h);
3676 
3677  m_tideTexWidth = tex_w;
3678  m_tideTexHeight = tex_h;
3679 
3680  unsigned char *d = image.GetData();
3681  unsigned char *a = image.GetAlpha();
3682 
3683  unsigned char mr, mg, mb;
3684  if (!a) image.GetOrFindMaskColour(&mr, &mg, &mb);
3685 
3686  unsigned char *e = new unsigned char[4 * w * h];
3687  if (e && d) {
3688  for (int y = 0; y < h; y++)
3689  for (int x = 0; x < w; x++) {
3690  unsigned char r, g, b;
3691  int off = (y * w + x);
3692  r = d[off * 3 + 0];
3693  g = d[off * 3 + 1];
3694  b = d[off * 3 + 2];
3695 
3696  e[off * 4 + 0] = r;
3697  e[off * 4 + 1] = g;
3698  e[off * 4 + 2] = b;
3699 
3700  e[off * 4 + 3] =
3701  a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
3702  }
3703  }
3704 
3705  glGenTextures(1, &m_tideTex);
3706 
3707  glBindTexture(GL_TEXTURE_2D, m_tideTex);
3708  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3709  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3710 
3711  if (g_texture_rectangle_format == GL_TEXTURE_2D)
3712  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
3713  GL_UNSIGNED_BYTE, e);
3714  else {
3715  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA,
3716  GL_UNSIGNED_BYTE, 0);
3717  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
3718  e);
3719  }
3720 
3721  delete[] e;
3722  }
3723 
3724  // Texture is ready
3725 
3726  glBindTexture(GL_TEXTURE_2D, m_tideTex);
3727  glEnable(GL_TEXTURE_2D);
3728  glEnable(GL_BLEND);
3729 
3730 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3731 #else
3732  for (int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++) {
3733  const IDX_entry *pIDX = ptcmgr->GetIDX_entry(i);
3734 
3735  char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3736  if ((type == 't') || (type == 'T')) // only Tides
3737  {
3738  double lon = pIDX->IDX_lon;
3739  double lat = pIDX->IDX_lat;
3740 
3741  if (BBox.Contains(lat, lon)) {
3742  wxPoint r;
3743  m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
3744 
3745  float xp = r.x;
3746  float yp = r.y;
3747 
3748  double scale = 1.0;
3749 #ifdef __ANDROID__
3750  scale *= getAndroidDisplayDensity();
3751 #endif
3752  double width2 = scale * m_tideTexWidth / 2;
3753  double height2 = scale * m_tideTexHeight / 2;
3754 
3755  float coords[8];
3756  float uv[8];
3757 
3758  // normal uv
3759  uv[0] = 0;
3760  uv[1] = 0;
3761  uv[2] = 0;
3762  uv[3] = 1;
3763  uv[4] = 1;
3764  uv[5] = 1;
3765  uv[6] = 1;
3766  uv[7] = 0;
3767 
3768  coords[0] = xp - width2;
3769  coords[1] = yp - height2;
3770  coords[2] = xp - width2;
3771  coords[3] = yp + height2;
3772  coords[4] = xp + width2;
3773  coords[5] = yp + height2;
3774  coords[6] = xp + width2;
3775  coords[7] = yp - height2;
3776 
3777  RenderTextures(dc, coords, uv, 4, m_pParentCanvas->GetpVP());
3778  }
3779  } // type 'T"
3780  } // loop
3781 
3782 #endif
3783 
3784  glDisable(GL_TEXTURE_2D);
3785  glDisable(GL_BLEND);
3786  glBindTexture(GL_TEXTURE_2D, 0);
3787  } else
3788  m_pParentCanvas->DrawAllTidesInBBox(dc, BBox);
3789 }
3790 
3791 void glChartCanvas::DrawGLCurrentsInBBox(ocpnDC &dc, LLBBox &BBox) {
3792  m_pParentCanvas->DrawAllCurrentsInBBox(dc, BBox);
3793 }
3794 
3795 void glChartCanvas::SetColorScheme(ColorScheme cs) {
3796  if (!m_bsetup) return;
3797 
3798  glDeleteTextures(1, &m_tideTex);
3799  glDeleteTextures(1, &m_currentTex);
3800  m_tideTex = 0;
3801  m_currentTex = 0;
3802  ownship_color = -1;
3803 }
3804 
3805 void glChartCanvas::RenderGLAlertMessage() {
3806  if (!m_pParentCanvas->GetAlertString().IsEmpty()) {
3807  wxString msg = m_pParentCanvas->GetAlertString();
3808 
3809  wxFont *pfont = GetOCPNScaledFont(_("Dialog"));
3810  m_gldc.SetFont(*pfont);
3811 
3812  int w, h;
3813  wxScreenDC sdc;
3814  sdc.GetTextExtent(msg, &w, &h, NULL, NULL, pfont);
3815 
3816  h += 2;
3817  w += 4;
3818  int yp =
3819  m_pParentCanvas->VPoint.pix_height - GetChartbarHeight() - h - (h / 4);
3820 
3821  wxRect sbr = m_pParentCanvas->GetScaleBarRect();
3822  int xp = sbr.x + sbr.width + 5;
3823 
3824  wxPen ppPen1(GetGlobalColor(_T ( "UBLCK" )), 1, wxPENSTYLE_SOLID);
3825  m_gldc.SetPen(ppPen1);
3826  m_gldc.SetBrush(wxBrush(GetGlobalColor(_T ( "YELO1" ))));
3827 
3828  m_gldc.DrawRectangle(xp, yp, w, h);
3829 
3830  m_gldc.DrawText(msg, xp, yp);
3831  }
3832 }
3833 
3834 unsigned long quiltHash;
3835 int refChartIndex;
3836 
3837 int n_render;
3838 void glChartCanvas::Render() {
3839  if (!m_bsetup || !m_pParentCanvas->m_pQuilt ||
3840  (m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_pQuilt) ||
3841  (!m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_singleChart)) {
3842 #ifdef __WXGTK__ // for some reason in gtk, a swap is needed here to get an
3843  // initial screen update
3844  SwapBuffers();
3845 #endif
3846  if(!g_PrintingInProgress) return;
3847  }
3848 
3849 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3850  loadShaders(GetCanvasIndex());
3851  configureShaders(m_pParentCanvas->VPoint);
3852 #endif
3853 
3854 #ifdef USE_ANDROID_GLES2
3855 
3856  OCPNStopWatch sw;
3857 
3858  if (m_binPinch) return;
3859 
3860  // qDebug() << "Render" << m_pParentCanvas->m_canvasIndex << GetPosition().x
3861  // << GetSize().x << m_pParentCanvas->GetPosition().x << m_pcontext;
3862 
3863  // if(m_pParentCanvas->m_canvasIndex == 0) return;
3864 
3865  // Do any setup required...
3866 
3867  bool recompose = false;
3868  if (m_pParentCanvas->VPoint.b_quilt && m_pParentCanvas->m_pQuilt &&
3869  !m_pParentCanvas->m_pQuilt->IsComposed()) {
3870  if (m_pParentCanvas->VPoint.IsValid()) {
3871  m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
3872  m_pParentCanvas->UpdateCanvasControlBar();
3873  recompose = true;
3874  } else
3875  return;
3876  }
3877 
3878  // Check to see if the Compose() call forced a SENC build.
3879  // If so, zoom the canvas just slightly to force a deferred redraw of the
3880  // full screen.
3881  if (sw.GetTime() > 2000) { // long enough to detect SENC build.
3882  m_pParentCanvas->ZoomCanvas(1.0001, false);
3883  }
3884 
3885  // qDebug() << "RenderTime1" << sw.GetTime();
3886 
3887  s_tess_vertex_idx = 0;
3888  quiltHash = m_pParentCanvas->m_pQuilt->GetXStackHash();
3889  refChartIndex = m_pParentCanvas->m_pQuilt->GetRefChartdbIndex();
3890 
3891 #endif
3892 
3893 #ifdef __WXOSX__
3894  // Support scaled HDPI displays.
3895  m_displayScale = GetContentScaleFactor();
3896 #endif
3897 
3898  m_last_render_time = wxDateTime::Now().GetTicks();
3899 
3900  // we don't care about jobs that are now off screen
3901  // clear out and it will be repopulated during render
3902  if (g_GLOptions.m_bTextureCompression &&
3903  !g_GLOptions.m_bTextureCompressionCaching)
3904  g_glTextureManager->ClearJobList();
3905 
3906  wxPaintDC(this);
3907 
3908  ocpnDC gldc(*this);
3909 
3910  int gl_width, gl_height;
3911  gl_width = m_pParentCanvas->VPoint.pix_width;
3912  gl_height = m_pParentCanvas->VPoint.pix_height;
3913 
3914  // Take a copy for use later by DC
3915  m_glcanvas_width = gl_width;
3916  m_glcanvas_height = gl_height;
3917 
3918  // Avoid some harmonic difficulties with odd-size glCanvas
3919  bool b_odd = false;
3920  if (gl_height & 1) {
3921  gl_height -= 1;
3922  ViewPort *vp = m_pParentCanvas->GetpVP();
3923  vp->pix_height = gl_height;
3924  b_odd = true;
3925  }
3926 
3927  if (gl_width & 1) {
3928  gl_width -= 1;
3929  ViewPort *vp = m_pParentCanvas->GetpVP();
3930  vp->pix_width = gl_width;
3931  b_odd = true;
3932  }
3933 
3934  // Set the shader viewport transform matrix
3935  // Using the adjusted dimensions
3936  if (b_odd) {
3937  ViewPort *vp = m_pParentCanvas->GetpVP();
3938  mat4x4 m;
3939  mat4x4_identity(m);
3940  mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
3941  2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
3942  1.0);
3943  mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
3944  -vp->pix_width / 2, -vp->pix_height / 2, 0);
3945  }
3946 
3947  ViewPort VPoint = m_pParentCanvas->VPoint;
3948 
3949  OCPNRegion screen_region(wxRect(0, 0, gl_width, gl_height));
3950  glViewport(0, 0, (GLint)gl_width, (GLint)gl_height);
3951 
3952 // #ifndef USE_ANDROID_GLES2
3953 #if !defined(USE_ANDROID_GLES2)
3954  glMatrixMode(GL_PROJECTION);
3955  glLoadIdentity();
3956 
3957  glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
3958  glMatrixMode(GL_MODELVIEW);
3959  glLoadIdentity();
3960 #endif
3961 
3962  if (s_b_useStencil) {
3963  glEnable(GL_STENCIL_TEST);
3964  glStencilMask(0xff);
3965  glClear(GL_STENCIL_BUFFER_BIT);
3966  glDisable(GL_STENCIL_TEST);
3967  }
3968 
3969  // set opengl settings that don't normally change
3970  // this should be able to go in SetupOpenGL, but it's
3971  // safer here incase a plugin mangles these
3972  if (g_GLOptions.m_GLLineSmoothing) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
3973  if (g_GLOptions.m_GLPolygonSmoothing)
3974  glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
3975  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3976 
3977  // Delete any textures known to the GPU that
3978  // belong to charts which will not be used in this render
3979  // This is done chart-by-chart...later we will scrub for unused textures
3980  // that belong to charts which ARE used in this render, if we need to....
3981 
3982  g_glTextureManager->TextureCrunch(0.8);
3983 
3984  // If we plan to post process the display, don't use accelerated panning
3985  double scale_factor = VPoint.ref_scale / VPoint.chart_scale;
3986 
3987  bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion().Empty();
3988  bool useFBO = false;
3989  int sx = gl_width;
3990  int sy = gl_height;
3991 
3992  // Try to use the framebuffer object's cache of the last frame
3993  // to accelerate drawing this frame (if overlapping)
3994  if (m_b_BuiltFBO && !bpost_hilite
3995  //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality
3996  // but slower
3997  ) {
3998  // Is this viewpoint the same as the previously painted one?
3999  bool b_newview = true;
4000  bool b_full = false;
4001 
4002  // If the view is the same we do no updates,
4003  // cached texture to the framebuffer
4004  if (m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm &&
4005  m_cache_vp.rotation == VPoint.rotation &&
4006  m_cache_vp.clat == VPoint.clat && m_cache_vp.clon == VPoint.clon &&
4007  m_cache_vp.IsValid() && m_cache_vp.pix_height == VPoint.pix_height &&
4008  m_cache_current_ch == m_pParentCanvas->m_singleChart) {
4009  b_newview = false;
4010  }
4011 
4012 #ifdef USE_ANDROID_GLES2
4013  if (recompose) b_newview = true;
4014 
4015  if (m_bforcefull) {
4016  b_newview = true;
4017  b_full = true;
4018  }
4019 
4020  // If no charts are to be rendered, we need to refresh the entire display
4021  // This fixes a problem with routes/tracks/marks rendering on pans at very
4022  // small scale. It is a workaround, so finding root cause should be
4023  // considered a TODO
4024 
4025  if (VPoint.b_quilt) {
4026  ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
4027  if (!chart) b_full = true;
4028  }
4029 
4030 #endif
4031 
4032  if (b_newview) {
4033  float dx = 0;
4034  float dy = 0;
4035 
4036  bool accelerated_pan = false;
4037  if (g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid() &&
4038  (VPoint.m_projection_type == PROJECTION_MERCATOR ||
4039  VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR) &&
4040  m_cache_vp.pix_height == VPoint.pix_height) {
4041  wxPoint2DDouble c_old =
4042  VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4043  m_displayScale;
4044  wxPoint2DDouble c_new =
4045  m_cache_vp.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4046  m_displayScale;
4047 
4048  dy = wxRound(c_new.m_y - c_old.m_y);
4049  dx = wxRound(c_new.m_x - c_old.m_x);
4050 
4051  // The math below using the previous frame's texture does not really
4052  // work for sub-pixel pans.
4053  // TODO is to rethink this.
4054  // Meanwhile, require the accelerated pans to be whole pixel multiples
4055  // only. This is not as bad as it sounds. Keyboard and mouse pans are
4056  // whole_pixel moves. However, autofollow at large scale is certainly
4057  // not.
4058 
4059  double deltax = c_new.m_x - c_old.m_x;
4060  double deltay = c_new.m_y - c_old.m_y;
4061 
4062  bool b_whole_pixel = true;
4063  if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2))
4064  b_whole_pixel = false;
4065 
4066  accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x &&
4067  abs(dy) < m_cache_tex_y &&
4068  (abs(dx) > 0 || (abs(dy) > 0));
4069  }
4070 
4071  // FBO swapping has trouble with Retina display on MacOS Monterey.
4072  // So, disable accelerated pan ops on this case.
4073  if (m_displayScale > 1) accelerated_pan = false;
4074 
4075  // FIXME (dave) There are some display artifact troubles using accPan on rotation.
4076  // Especially seen on sparse RNC rendering
4077  if (fabs(VPoint.rotation) > 0) accelerated_pan = false;
4078 
4079  // do we allow accelerated panning? can we perform it here?
4080 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4081 #else // GLES2
4082  // enable rendering to texture in framebuffer object
4083  glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
4084 
4085  if (VPoint.chart_scale < 5000) b_full = true;
4086 
4087  if (VPoint.chart_scale > 5e7) b_full = true;
4088 
4089  if (b_full) accelerated_pan = false;
4090 
4091  if (accelerated_pan) {
4092  if ((dx != 0) || (dy != 0)) { // Anything to do?
4093 
4094  // calculate the new regions to render
4095  // add extra pixels to avoid coordindate rounding issues at large
4096  // scale
4097  OCPNRegion update_region;
4098 
4099  int fluff = 2;
4100 
4101  // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4102  if (VPoint.chart_scale < 10000) fluff = 8;
4103 
4104  if (dy > 0 && dy < gl_height)
4105  update_region.Union(
4106  wxRect(0, gl_height - (dy + fluff), gl_width, dy + fluff));
4107  else if (dy < 0)
4108  update_region.Union(wxRect(0, 0, gl_width, -dy + fluff));
4109 
4110  if (dx > 0 && dx < gl_width)
4111  update_region.Union(
4112  wxRect(gl_width - (dx + fluff), 0, dx + fluff, gl_height));
4113  else if (dx < 0)
4114  update_region.Union(wxRect(0, 0, -dx + fluff, gl_height));
4115 
4116  m_cache_page = !m_cache_page; /* page flip */
4117 
4118  // Bind the destination (target frame) texture to the frame buffer
4119  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4120  GL_TEXTURE_2D, m_cache_tex[m_cache_page], 0);
4121 
4122  // Before rendering anything, clear the color buffers
4123  // wxColour color = GetGlobalColor(_T ( "NODTA" ));
4124  // glClearColor(color.Red() / 256., color.Green() / 256.,
4125  // color.Blue() / 256., 1.0);
4126  // glClear(GL_COLOR_BUFFER_BIT);
4127 
4128  // First render the new content into the update region
4129  RenderCharts(m_gldc, update_region);
4130  glDisable(g_texture_rectangle_format);
4131  glUseProgram(0);
4132 
4133  // Next, render the cached texture as quad to FBO(m_blit_tex) with offsets
4134  glBindTexture(GL_TEXTURE_2D, m_cache_tex[!m_cache_page]);
4135  glEnable(GL_TEXTURE_2D);
4136 
4137  // Blit the existing content onto the alternate FBO, at the correct location
4138  float x1, x2, y1, y2;
4139 
4140  if (dx > 0)
4141  x1 = dx, x2 = 0;
4142  else
4143  x1 = 0, x2 = -dx;
4144 
4145  if (dy > 0)
4146  y1 = dy, y2 = 0;
4147  else
4148  y1 = 0, y2 = -dy;
4149 
4150  // normalize to texture coordinates range from 0 to 1
4151  float tx1, tx2, ty1, ty2;
4152 
4153  float xcor = 0;
4154  float ycor = 0;
4155 
4156  tx1 = 0;
4157  tx2 = sx / (float)m_cache_tex_x;
4158  ty1 = 0;
4159  ty2 = sy / (float)m_cache_tex_y;
4160 
4161  float coords[8];
4162  float uv[8];
4163 
4164  // normal uv
4165  uv[0] = tx1;
4166  uv[1] = ty1;
4167  uv[2] = tx2;
4168  uv[3] = ty1;
4169  uv[4] = tx2;
4170  uv[5] = ty2;
4171  uv[6] = tx1;
4172  uv[7] = ty2;
4173 
4174  coords[0] = -dx;
4175  coords[1] = dy;
4176  coords[2] = -dx + sx;
4177  coords[3] = dy;
4178  coords[4] = -dx + sx;
4179  coords[5] = dy + sy;
4180  coords[6] = -dx;
4181  coords[7] = dy + sy;
4182 
4183  GLShaderProgram *shader =
4184  ptexture_2D_shader_program[GetCanvasIndex()];
4185  shader->Bind();
4186 
4187  // Set up the texture sampler to texture unit 0
4188  shader->SetUniform1i("uTex", 0);
4189 
4190  mat4x4 m, mvp, I;
4191  mat4x4_identity(m);
4192  mat4x4_scale_aniso(mvp, m, 2.0 / (float)sx, 2.0 / (float)sy, 1.0);
4193  mat4x4_translate_in_place(mvp, -(float)sx / 2, -(float)sy / 2, 0);
4194  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)mvp);
4195  mat4x4_identity(I);
4196  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
4197 
4198  float co1[8];
4199  co1[0] = coords[0];
4200  co1[1] = coords[1];
4201  co1[2] = coords[2];
4202  co1[3] = coords[3];
4203  co1[4] = coords[6];
4204  co1[5] = coords[7];
4205  co1[6] = coords[4];
4206  co1[7] = coords[5];
4207 
4208  float tco1[8];
4209  tco1[0] = uv[0];
4210  tco1[1] = uv[1];
4211  tco1[2] = uv[2];
4212  tco1[3] = uv[3];
4213  tco1[4] = uv[6];
4214  tco1[5] = uv[7];
4215  tco1[6] = uv[4];
4216  tco1[7] = uv[5];
4217 
4218  shader->SetAttributePointerf("aPos", co1);
4219  shader->SetAttributePointerf("aUV", tco1);
4220 
4221  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4222 
4223  // restore the shader matrix
4224  shader->SetUniformMatrix4fv("MVMatrix",
4225  (GLfloat *)VPoint.vp_matrix_transform);
4226 
4227  shader->UnBind();
4228  glBindTexture(g_texture_rectangle_format, 0);
4229 
4230  glDisable(g_texture_rectangle_format);
4231  glUseProgram(0);
4232  }
4233 
4234  } // accelerated pan
4235 
4236  else { // must redraw the entire screen
4237  // qDebug() << "Fullpage";
4238  glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
4239  g_texture_rectangle_format,
4240  m_cache_tex[!m_cache_page], 0);
4241 
4242  m_fbo_offsetx = 0;
4243  m_fbo_offsety = 0;
4244  m_fbo_swidth = sx;
4245  m_fbo_sheight = sy;
4246 
4247  // FIXME (dave) test on Android
4248  // This can be annoying on Android pinch zoom
4249 
4250  // Clear the screen to NODTA color
4251  wxColour color = GetGlobalColor(_T ( "NODTA" ));
4252  glClearColor(color.Red() / 256., color.Green() / 256.,
4253  color.Blue() / 256., 1.0);
4254  glClear(GL_COLOR_BUFFER_BIT);
4255 
4256  OCPNRegion rscreen_region(VPoint.rv_rect);
4257  RenderCharts(m_gldc, rscreen_region);
4258 
4259  m_cache_page = !m_cache_page; /* page flip */
4260 
4261  } // full page render
4262 
4263  // Disable Render to FBO
4264  glBindFramebuffer(GL_FRAMEBUFFER, 0);
4265 
4266 #endif // gles2 for accpan
4267 
4268  } // newview
4269 
4270  useFBO = true;
4271  }
4272 
4273 #ifndef __ANDROID__
4274  if (VPoint.tilt) {
4275  glMatrixMode(GL_PROJECTION);
4276  glLoadIdentity();
4277 
4278  gluPerspective(2 * 180 / PI * atan2((double)gl_height, (double)gl_width),
4279  (GLfloat)gl_width / (GLfloat)gl_height, 1, gl_width);
4280 
4281  glMatrixMode(GL_MODELVIEW);
4282  glLoadIdentity();
4283 
4284  glScalef(1, -1, 1);
4285  glTranslatef(-gl_width / 2, -gl_height / 2, -gl_width / 2);
4286  glRotated(VPoint.tilt * 180 / PI, 1, 0, 0);
4287 
4288  glGetIntegerv(GL_VIEWPORT, viewport);
4289  glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4290  glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4291  }
4292 #endif
4293 
4294  if (useFBO) {
4295 #if 0 // #ifndef USE_ANDROID_GLES2
4296  glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fb0);
4297  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
4298  glBlitFramebuffer(0, 0, sx, sy, 0, 0, sx*2, sy*2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
4299 
4300  glBindFramebuffer(GL_FRAMEBUFFER, 0);
4301 
4302 #else
4303  // Render the cached texture as quad to screen
4304  glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4305  glEnable(g_texture_rectangle_format);
4306 
4307  float tx, ty, tx0, ty0, divx, divy;
4308 
4309  // Normalize, or not?
4310  if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format) {
4311  divx = divy = 1.0f;
4312  } else {
4313  divx = m_cache_tex_x;
4314  divy = m_cache_tex_y;
4315  }
4316 
4317  tx0 = m_fbo_offsetx / divx;
4318  ty0 = m_fbo_offsety / divy;
4319  tx = (m_fbo_offsetx + m_fbo_swidth) / divx;
4320  ty = (m_fbo_offsety + m_fbo_sheight) / divy;
4321 
4322  float coords[8];
4323  float uv[8];
4324 
4325  // normal uv
4326  uv[0] = tx0;
4327  uv[1] = ty;
4328  uv[2] = tx;
4329  uv[3] = ty;
4330  uv[4] = tx;
4331  uv[5] = ty0;
4332  uv[6] = tx0;
4333  uv[7] = ty0;
4334 
4335  // pixels
4336  coords[0] = 0;
4337  coords[1] = 0;
4338  coords[2] = sx;
4339  coords[3] = 0;
4340  coords[4] = sx;
4341  coords[5] = sy;
4342  coords[6] = 0;
4343  coords[7] = sy;
4344 
4345  wxColour color = GetGlobalColor(_T ( "NODTA" ));
4346  glClearColor(color.Red() / 256., color.Green() / 256., color.Blue() / 256.,
4347  1.0);
4348  glClear(GL_COLOR_BUFFER_BIT);
4349 
4350  RenderTextures(gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4351 #endif
4352 
4353  glDisable(g_texture_rectangle_format);
4354 
4355  m_cache_vp = VPoint;
4356  m_cache_current_ch = m_pParentCanvas->m_singleChart;
4357 
4358  if (VPoint.b_quilt) m_pParentCanvas->m_pQuilt->SetRenderedVP(VPoint);
4359 
4360  } else // useFBO
4361  {
4362  RenderCharts(m_gldc, screen_region);
4363  }
4364 
4365 #if 1
4366  // Done with base charts.
4367  // Now the overlays
4368  RenderS57TextOverlay(VPoint);
4369  RenderMBTilesOverlay(VPoint);
4370 
4371  // Render static overlay objects
4372  for (OCPNRegionIterator upd(screen_region); upd.HaveRects(); upd.NextRect()) {
4373  wxRect rt = upd.GetRect();
4374  LLRegion region = VPoint.GetLLRegion(rt);
4375  ViewPort cvp = ClippedViewport(VPoint, region);
4376  DrawGroundedOverlayObjects(gldc, cvp);
4377  }
4378 
4379  if (m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent) {
4380  LLRegion screenLLRegion = VPoint.GetLLRegion(screen_region);
4381  LLBBox screenBox = screenLLRegion.GetBox();
4382  // Enlarge the box a bit
4383  screenBox.EnLarge(screenBox.GetLonRange() * 0.05);
4384 
4385  // update the tide/current select points, if necessary
4386  if (m_pParentCanvas->m_bShowTide) {
4387  m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4388  DrawGLTidesInBBox(gldc, VPoint.GetBBox());
4389  }
4390 
4391  if (m_pParentCanvas->m_bShowCurrent) {
4392  m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4393  DrawGLCurrentsInBBox(gldc, VPoint.GetBBox());
4394  }
4395  }
4396 
4397  // If multi-canvas, indicate which canvas has keyboard focus
4398  // by drawing a simple blue bar at the top.
4399  if (g_canvasConfig != 0) { // multi-canvas?
4400  if (m_pParentCanvas == wxWindow::FindFocus()) {
4401  g_focusCanvas = m_pParentCanvas;
4402 
4403  wxColour colour = GetGlobalColor(_T("BLUE4"));
4404  wxPen ppBlue(colour, 1);
4405  wxBrush ppBrush(colour);
4406  gldc.SetPen(ppBlue);
4407  gldc.SetBrush(ppBrush);
4408  int xw = m_pParentCanvas->GetClientSize().x * m_displayScale;
4409  float rect_pix = m_pParentCanvas->m_focus_indicator_pix * m_displayScale;
4410  wxPoint barPoints[4];
4411  barPoints[0].x = 0;
4412  barPoints[0].y = 0;
4413  barPoints[1].x = xw;
4414  barPoints[1].y = 0;
4415  barPoints[2].x = xw;
4416  barPoints[2].y = rect_pix;
4417  barPoints[3].x = 0;
4418  barPoints[3].y = rect_pix;
4419 
4420  gldc.DrawPolygon(4, barPoints, 0, 0, 1, 0);
4421  }
4422  }
4423 
4424  DrawDynamicRoutesTracksAndWaypoints(VPoint);
4425 
4426  // Now draw all the objects which normally move around and are not
4427  // cached from the previous frame
4428  DrawFloatingOverlayObjects(m_gldc);
4429 
4430 #ifndef USE_ANDROID_GLES2
4431  // from this point on don't use perspective
4432  if (VPoint.tilt) {
4433  glMatrixMode(GL_PROJECTION);
4434  glLoadIdentity();
4435 
4436  glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4437  glMatrixMode(GL_MODELVIEW);
4438  glLoadIdentity();
4439  }
4440 #endif
4441 
4442  DrawEmboss(m_gldc, m_pParentCanvas->EmbossDepthScale());
4443  DrawEmboss(m_gldc, m_pParentCanvas->EmbossOverzoomIndicator(gldc));
4444 
4445  if (g_pi_manager) {
4446  ViewPort &vp = m_pParentCanvas->GetVP();
4447  g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4448  g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4449  m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_EMBOSS);
4450  }
4451  if(!g_PrintingInProgress){
4452  if (m_pParentCanvas->m_pTrackRolloverWin)
4453  m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4454 
4455  if (m_pParentCanvas->m_pRouteRolloverWin)
4456  m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4457 
4458  if (m_pParentCanvas->m_pAISRolloverWin)
4459  m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4460 
4461 
4462  if (m_pParentCanvas->GetMUIBar())
4463  m_pParentCanvas->GetMUIBar()->DrawGL(gldc, m_displayScale);
4464 
4465  if (g_MainToolbar && m_pParentCanvas->IsPrimaryCanvas())
4466  g_MainToolbar->DrawGL(gldc, m_displayScale);
4467 
4468  if (g_iENCToolbar && m_pParentCanvas->IsPrimaryCanvas())
4469  g_iENCToolbar->DrawGL(gldc, m_displayScale);
4470  }
4471 
4472  // On some platforms, the opengl context window is always on top of any
4473  // standard DC windows, so we need to draw the Chart Info Window
4474  // as overlayed bmps.
4475 
4476 #ifdef __WXOSX__
4477  if (m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4478  int x, y, width, height;
4479  m_pParentCanvas->m_pCIWin->GetClientSize(&width, &height);
4480  m_pParentCanvas->m_pCIWin->GetPosition(&x, &y);
4481  wxBitmap bmp(width, height, -1);
4482  wxMemoryDC dc(bmp);
4483  if (bmp.IsOk()) {
4484  dc.SetBackground(wxBrush(GetGlobalColor(_T ( "UIBCK" ))));
4485  dc.Clear();
4486 
4487  dc.SetTextBackground(GetGlobalColor(_T ( "UIBCK" )));
4488  dc.SetTextForeground(GetGlobalColor(_T ( "UITX1" )));
4489 
4490  int yt = 0;
4491  int xt = 0;
4492  wxString s = m_pParentCanvas->m_pCIWin->GetString();
4493  int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4494 
4495  wxStringTokenizer tkz(s, _T("\n"));
4496  wxString token;
4497 
4498  while (tkz.HasMoreTokens()) {
4499  token = tkz.GetNextToken();
4500  dc.DrawText(token, xt, yt);
4501  yt += h;
4502  }
4503  dc.SelectObject(wxNullBitmap);
4504 
4505  m_gldc.DrawBitmap(bmp, x, y, false);
4506  }
4507  }
4508 
4509 #endif
4510  // render the chart bar
4511  if (g_bShowChartBar) DrawChartBar(m_gldc);
4512 
4513  if (m_pParentCanvas->m_Compass) m_pParentCanvas->m_Compass->Paint(gldc);
4514 
4515  RenderGLAlertMessage();
4516 #endif
4517 
4518  if (g_pi_manager) {
4519  ViewPort &vp = m_pParentCanvas->GetVP();
4520  g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4521  g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4522  m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_UI);
4523  }
4524 
4525  // quiting?
4526  if (g_bquiting) DrawQuiting();
4527  if (g_bcompression_wait)
4528  DrawCloseMessage(_("Waiting for raster chart compression thread exit."));
4529 
4530  // Some older MSW OpenGL drivers are generally very unstable.
4531  // This helps...
4532 
4533  if (g_b_needFinish) glFinish();
4534 
4535  SwapBuffers();
4536 
4537  g_glTextureManager->TextureCrunch(0.8);
4538  g_glTextureManager->FactoryCrunch(0.6);
4539 
4540  m_pParentCanvas->PaintCleanup();
4541  // OCPNPlatform::HideBusySpinner();
4542  m_bforcefull = false;
4543 
4544  n_render++;
4545 }
4546 
4547 void glChartCanvas::RenderS57TextOverlay(ViewPort &VPoint) {
4548  // Render the decluttered Text overlay for quilted vector charts, except for
4549  // CM93 Composite
4550  if (VPoint.b_quilt) {
4551  if (m_pParentCanvas->m_pQuilt->IsQuiltVector() && ps52plib &&
4552  ps52plib->GetShowS57Text()) {
4553  ChartBase *chart = m_pParentCanvas->m_pQuilt->GetRefChart();
4554  if (chart && (chart->GetChartType() != CHART_TYPE_CM93COMP)) {
4555  // Clear the text Global declutter list
4556  if (chart) {
4557  ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
4558  if (ChPI)
4559  ChPI->ClearPLIBTextList();
4560  else
4561  ps52plib->ClearTextList();
4562  }
4563 
4564  // Grow the ViewPort a bit laterally, to minimize "jumping" of text
4565  // elements at left side of screen
4566  ViewPort vpx = VPoint;
4567  vpx.BuildExpandedVP(VPoint.pix_width * 12 / 10, VPoint.pix_height);
4568 
4569  OCPNRegion screen_region(
4570  wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4571  RenderQuiltViewGLText(vpx, screen_region);
4572  }
4573  }
4574  }
4575 }
4576 
4577 void glChartCanvas::RenderMBTilesOverlay(ViewPort &VPoint) {
4578  // Render MBTiles as overlay
4579  std::vector<int> stackIndexArray =
4580  m_pParentCanvas->m_pQuilt->GetExtendedStackIndexArray();
4581  unsigned int im = stackIndexArray.size();
4582  // XXX should
4583  // assert(!VPoint.b_quilt && im == 0)
4584  if (VPoint.b_quilt && im > 0) {
4585  bool regionVPBuilt = false;
4586  OCPNRegion screen_region;
4587  LLRegion screenLLRegion;
4588  LLBBox screenBox;
4589  ViewPort vp;
4590 
4591  std::vector<int> tiles_to_show;
4592  for (unsigned int is = 0; is < im; is++) {
4593  const ChartTableEntry &cte =
4594  ChartData->GetChartTableEntry(stackIndexArray[is]);
4595  if (cte.GetChartType() == CHART_TYPE_MBTILES) {
4596  if (m_pParentCanvas->IsTileOverlayIndexInNoShow(stackIndexArray[is])) {
4597  // Turn off the piano highlite
4598  std::vector<int> piano_active_array_tiles =
4599  m_pParentCanvas->m_Piano->GetActiveKeyArray();
4600  bool bfound = false;
4601 
4602  for (unsigned int i = 0; i < piano_active_array_tiles.size(); i++) {
4603  if (piano_active_array_tiles[i] == stackIndexArray[is]) {
4604  piano_active_array_tiles.erase(piano_active_array_tiles.begin() +
4605  i); // erase it
4606  bfound = true;
4607  break;
4608  }
4609  }
4610 
4611  if (bfound)
4612  m_pParentCanvas->m_Piano->SetActiveKeyArray(
4613  piano_active_array_tiles);
4614 
4615  continue;
4616  }
4617 
4618  tiles_to_show.push_back(stackIndexArray[is]);
4619  if (!regionVPBuilt) {
4620  screen_region =
4621  OCPNRegion(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4622  screenLLRegion = VPoint.GetLLRegion(screen_region);
4623  screenBox = screenLLRegion.GetBox();
4624 
4625  vp = VPoint;
4626  wxPoint p;
4627  p.x = VPoint.pix_width / 2;
4628  p.y = VPoint.pix_height / 2;
4629  VPoint.GetLLFromPix(p, &vp.clat, &vp.clon);
4630 
4631  regionVPBuilt = true;
4632  }
4633  }
4634  }
4635 
4636  // We need to show the tilesets in reverse order to have the largest scale
4637  // on top
4638  for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4639  rit != tiles_to_show.rend(); ++rit) {
4640  ChartBase *chart = ChartData->OpenChartFromDBAndLock(*rit, FULL_INIT);
4641 
4642  // Chart may have been prevented from initial loading due to size, or some
4643  // other reason...
4644  if (chart == NULL) continue;
4645 
4646  wxFileName tileFile(chart->GetFullPath());
4647  // Size test for 5 GByte
4648  wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
4649 
4650  if (!ChartData->CheckAnyCanvasExclusiveTileGroup() ||
4651  (tileSizeMB.GetLo() > 5000)) {
4652  // Check to see if the tile has been "clicked".
4653  // If so, do not add to no-show array again.
4654  if (!m_pParentCanvas->IsTileOverlayIndexInYesShow(*rit)) {
4655  if (!m_pParentCanvas->IsTileOverlayIndexInNoShow(*rit)) {
4656  m_pParentCanvas->m_tile_noshow_index_array.push_back(*rit);
4657  }
4658  }
4659  }
4660 
4661  // This test catches the case where the chart is added to no_show list
4662  // when first loaded by OpenChartFromDBAndLock
4663  if (m_pParentCanvas->IsTileOverlayIndexInNoShow(*rit)) {
4664  continue;
4665  }
4666 
4667  ChartMBTiles *pcmbt = dynamic_cast<ChartMBTiles *>(chart);
4668  if (pcmbt) {
4669  pcmbt->RenderRegionViewOnGL(*m_pcontext, vp, screen_region,
4670  screenLLRegion);
4671 
4672  // Light up the piano key if the chart was rendered
4673  std::vector<int> piano_active_array_tiles =
4674  m_pParentCanvas->m_Piano->GetActiveKeyArray();
4675  bool bfound = false;
4676 
4677  if (std::find(piano_active_array_tiles.begin(),
4678  piano_active_array_tiles.end(),
4679  *rit) != piano_active_array_tiles.end()) {
4680  bfound = true;
4681  }
4682 
4683  if (!bfound) {
4684  piano_active_array_tiles.push_back(*rit);
4685  m_pParentCanvas->m_Piano->SetActiveKeyArray(piano_active_array_tiles);
4686  }
4687  }
4688  }
4689 
4690  // Render the HiLite on piano rollover of mbTile key
4691  LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
4692 
4693  if (!hiregion.Empty()) {
4694  glEnable(GL_BLEND);
4695 
4696  double hitrans;
4697  switch (global_color_scheme) {
4698  case GLOBAL_COLOR_SCHEME_DAY:
4699  hitrans = .4;
4700  break;
4701  case GLOBAL_COLOR_SCHEME_DUSK:
4702  hitrans = .2;
4703  break;
4704  case GLOBAL_COLOR_SCHEME_NIGHT:
4705  hitrans = .1;
4706  break;
4707  default:
4708  hitrans = .4;
4709  break;
4710  }
4711 
4712 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4713  glColor4f((float).8, (float).4, (float).4, (float)hitrans);
4714 #else
4715  s_regionColor = wxColor(204, 102, 102, hitrans * 256);
4716 #endif
4717 
4718  DrawRegion(VPoint, hiregion);
4719 
4720  glDisable(GL_BLEND);
4721  }
4722  }
4723 }
4724 
4725 void glChartCanvas::RenderCanvasBackingChart(ocpnDC &dc,
4726  OCPNRegion valid_region) {
4727  // Fill the FBO with the current gshhs world chart
4728  int w, h;
4729  GetClientSize(&w, &h);
4730 
4731  glViewport(0, 0, (GLint)m_cache_tex_x, (GLint)m_cache_tex_y);
4732 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4733  glMatrixMode(GL_PROJECTION);
4734  glLoadIdentity();
4735 
4736  glOrtho(0, m_cache_tex_x, m_cache_tex_y, 0, -1, 1);
4737  glMatrixMode(GL_MODELVIEW);
4738  glLoadIdentity();
4739 #endif
4740 
4741  wxRect rtex(0, 0, m_cache_tex_x, m_cache_tex_y);
4742  ViewPort cvp =
4743  m_pParentCanvas->GetVP().BuildExpandedVP(m_cache_tex_x, m_cache_tex_y);
4744 
4745  bool world_view = false;
4746  RenderWorldChart(dc, cvp, rtex, world_view);
4747  gShapeBasemap.RenderViewOnDC(dc, cvp);
4748 
4749  // dc.SetPen(wxPen(wxColour(254,254,0), 3));
4750  // dc.DrawLine( 0, 0, m_cache_tex_x, m_cache_tex_y);
4751 
4752  // Reset matrices
4753  glViewport(0, 0, (GLint)w, (GLint)h);
4754 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4755  glMatrixMode(GL_PROJECTION);
4756  glLoadIdentity();
4757 
4758  glOrtho(0, (GLint)w, (GLint)h, 0, -1, 1);
4759  glMatrixMode(GL_MODELVIEW);
4760  glLoadIdentity();
4761 #endif
4762 }
4763 
4764 void glChartCanvas::FastPan(int dx, int dy) {
4765 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4766 #endif
4767 }
4768 
4769 void glChartCanvas::ZoomProject(float offset_x, float offset_y, float swidth,
4770  float sheight) {
4771  SetCurrent(*m_pcontext);
4772 
4773  float sx = GetSize().x;
4774  float sy = GetSize().y;
4775 
4776  float tx, ty, tx0, ty0;
4777  // if( GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format )
4778  // tx = sx, ty = sy;
4779  // else
4780  tx = 1, ty = 1;
4781 
4782  tx0 = ty0 = 0.;
4783 
4784  tx0 = offset_x;
4785  ty0 = offset_y;
4786  tx = offset_x + swidth;
4787  ty = offset_y + sheight;
4788 
4789  int w, h;
4790  GetClientSize(&w, &h);
4791  glViewport(0, 0, (GLint)w, (GLint)h);
4792 
4793  if (s_b_useStencil) {
4794  glEnable(GL_STENCIL_TEST);
4795  glStencilMask(0xff);
4796  glClear(GL_STENCIL_BUFFER_BIT);
4797  glDisable(GL_STENCIL_TEST);
4798  }
4799 
4800  float vx0 = 0;
4801  float vy0 = 0;
4802  float vy = sy;
4803  float vx = sx;
4804 
4805  glBindTexture(g_texture_rectangle_format, 0);
4806 
4807  // Render the cached texture as quad to screen
4808  glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4809  glEnable(g_texture_rectangle_format);
4810  // glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
4811 
4812  float uv[8];
4813  float coords[8];
4814 
4815  // normal uv Texture coordinates
4816  uv[0] = tx0 / m_cache_tex_x;
4817  uv[1] = ty / m_cache_tex_y;
4818  uv[2] = tx / m_cache_tex_x;
4819  uv[3] = ty / m_cache_tex_y;
4820  uv[4] = tx / m_cache_tex_x;
4821  uv[5] = ty0 / m_cache_tex_y;
4822  uv[6] = tx0 / m_cache_tex_x;
4823  uv[7] = ty0 / m_cache_tex_y;
4824 
4825  // pixels
4826  coords[0] = vx0;
4827  coords[1] = vy0;
4828  coords[2] = vx;
4829  coords[3] = vy0;
4830  coords[4] = vx;
4831  coords[5] = vy;
4832  coords[6] = vx0;
4833  coords[7] = vy;
4834 
4835  RenderTextures(m_gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4836 
4837  glDisable(g_texture_rectangle_format);
4838  glBindTexture(g_texture_rectangle_format, 0);
4839 
4840  // For fun, we prove the coordinates of the blank area outside the chart when
4841  // zooming out. Bottom stripe
4842  // wxColour color = GetGlobalColor(_T("YELO1")); //GREY1
4843  wxColour color = GetGlobalColor(_T("GREY1")); //
4844  float ht = -offset_y * (sy / sheight);
4845  wxRect r(0, sy - ht, w, ht);
4846  RenderColorRect(r, color);
4847 
4848  // top stripe
4849  wxRect rt(0, 0, w, sy - (ht + (sy * sy / sheight)));
4850  RenderColorRect(rt, color);
4851 
4852  // left
4853  float w1 = -offset_x * sx / swidth;
4854  wxRect rl(0, 0, w1, sy);
4855  RenderColorRect(rl, color);
4856 
4857  // right
4858  float px = w1 + sx * sx / swidth;
4859  wxRect rr(px, 0, sx - px, sy);
4860  RenderColorRect(rr, color);
4861 
4862  // When zooming out, if we go too far, then the frame buffer is repeated
4863  // on-screen due to address wrapping in the frame buffer. Detect this case,
4864  // and render some simple solid covering quads to avoid a confusing display.
4865 
4866 
4867  SwapBuffers();
4868 }
4869 
4870 void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event) {
4871  // If m_zoomFinal is set, shortcut the timer sequence.
4872 
4873  if ((m_nRun < m_nTotal) && !m_zoomFinal) {
4874  m_runoffsetx += m_offsetxStep;
4875  if (m_offsetxStep > 0)
4876  m_runoffsetx = wxMin(m_runoffsetx, m_fbo_offsetx);
4877  else
4878  m_runoffsetx = wxMax(m_runoffsetx, m_fbo_offsetx);
4879 
4880  m_runoffsety += m_offsetyStep;
4881  if (m_offsetyStep > 0)
4882  m_runoffsety = wxMin(m_runoffsety, m_fbo_offsety);
4883  else
4884  m_runoffsety = wxMax(m_runoffsety, m_fbo_offsety);
4885 
4886  m_runswidth += m_swidthStep;
4887  if (m_swidthStep > 0)
4888  m_runswidth = wxMin(m_runswidth, m_fbo_swidth);
4889  else
4890  m_runswidth = wxMax(m_runswidth, m_fbo_swidth);
4891 
4892  m_runsheight += m_sheightStep;
4893  if (m_sheightStep > 0)
4894  m_runsheight = wxMin(m_runsheight, m_fbo_sheight);
4895  else
4896  m_runsheight = wxMax(m_runsheight, m_fbo_sheight);
4897 
4898  // qDebug() << "onZoomTimerEvent" << m_nRun << m_nTotal <<
4899  // m_runoffsetx << m_offsetxStep;
4900 
4901  ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
4902  m_nRun += m_nStep;
4903  } else {
4904  // qDebug() << "onZoomTimerEvent DONE" << m_nRun << m_nTotal;
4905 
4906  zoomTimer.Stop();
4907  if (m_zoomFinal) {
4908  // qDebug() << "onZoomTimerEvent FINALZOOM" << m_zoomFinalZoom;
4909 
4910  m_pParentCanvas->ZoomCanvas(m_zoomFinalZoom, false);
4911 
4912  if (m_zoomFinaldx || m_zoomFinaldy) {
4913  m_pParentCanvas->PanCanvas(m_zoomFinaldx, m_zoomFinaldy);
4914  }
4915  }
4916  m_zoomFinal = false;
4917  }
4918 }
4919 
4920 void glChartCanvas::FastZoom(float factor, float cp_x, float cp_y, float post_x,
4921  float post_y) {
4922  // qDebug() << "FastZoom" << factor << post_x << post_y << m_nRun;
4923 
4924  int sx = GetSize().x;
4925  int sy = GetSize().y;
4926 
4927  m_lastfbo_offsetx = m_fbo_offsetx;
4928  m_lastfbo_offsety = m_fbo_offsety;
4929  m_lastfbo_swidth = m_fbo_swidth;
4930  m_lastfbo_sheight = m_fbo_sheight;
4931 
4932  float curr_fbo_offset_x = m_fbo_offsetx;
4933  float curr_fbo_offset_y = m_fbo_offsety;
4934  float curr_fbo_swidth = m_fbo_swidth;
4935  float curr_fbo_sheight = m_fbo_sheight;
4936 
4937  float fx = (float)cp_x / sx;
4938  float fy = 1.0 - (float)cp_y / sy;
4939  if (factor < 1.0f) {
4940  // fx = 0.5; // center screen
4941  // fy = 0.5;
4942  }
4943 
4944  float fbo_ctr_x = curr_fbo_offset_x + (curr_fbo_swidth * fx);
4945  float fbo_ctr_y = curr_fbo_offset_y + (curr_fbo_sheight * fy);
4946 
4947  m_fbo_swidth = curr_fbo_swidth / factor;
4948  m_fbo_sheight = curr_fbo_sheight / factor;
4949 
4950  m_fbo_offsetx = fbo_ctr_x - (m_fbo_swidth * fx);
4951  m_fbo_offsety = fbo_ctr_y - (m_fbo_sheight * fy);
4952 
4953  m_fbo_offsetx += post_x;
4954  m_fbo_offsety += post_y;
4955 
4956  // if(factor < 1.0f){
4957  // ZoomProject(m_fbo_offsetx, m_fbo_offsety, m_fbo_swidth,
4958  // m_fbo_sheight); zoomTimer.Stop(); m_zoomFinal = false;
4959  // }
4960  // else
4961  {
4962  m_nStep = 20;
4963  m_nTotal = 100;
4964 
4965  m_nStep = 10;
4966  m_nTotal = 40;
4967 
4968  m_nRun = 0;
4969 
4970  float perStep = m_nStep / m_nTotal;
4971 
4972  if (zoomTimer.IsRunning()) {
4973  m_offsetxStep = (m_fbo_offsetx - m_runoffsetx) * perStep;
4974  m_offsetyStep = (m_fbo_offsety - m_runoffsety) * perStep;
4975  m_swidthStep = (m_fbo_swidth - m_runswidth) * perStep;
4976  m_sheightStep = (m_fbo_sheight - m_runsheight) * perStep;
4977 
4978  } else {
4979  m_offsetxStep = (m_fbo_offsetx - m_lastfbo_offsetx) * perStep;
4980  m_offsetyStep = (m_fbo_offsety - m_lastfbo_offsety) * perStep;
4981  m_swidthStep = (m_fbo_swidth - m_lastfbo_swidth) * perStep;
4982  m_sheightStep = (m_fbo_sheight - m_lastfbo_sheight) * perStep;
4983 
4984  m_runoffsetx = m_lastfbo_offsetx;
4985  m_runoffsety = m_lastfbo_offsety;
4986  m_runswidth = m_lastfbo_swidth;
4987  m_runsheight = m_lastfbo_sheight;
4988  }
4989 
4990  if (!zoomTimer.IsRunning()) zoomTimer.Start(m_nStep);
4991  m_zoomFinal = false;
4992  }
4993 }
4994 
4995 #ifdef __ANDROID__
4996 
4997 void glChartCanvas::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
4998  // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
4999  // event.cursor_pos.x;
5000 
5001  if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5002  return;
5003 
5004  if (m_binPinch) return;
5005  if (m_bpinchGuard) return;
5006 
5007  int x = event.GetOffset().x;
5008  int y = event.GetOffset().y;
5009 
5010  int lx = event.GetLastOffset().x;
5011  int ly = event.GetLastOffset().y;
5012 
5013  int dx = lx - x;
5014  int dy = y - ly;
5015 
5016  switch (event.GetState()) {
5017  case GestureStarted:
5018  if (m_binPan) break;
5019 
5020  panx = pany = 0;
5021  m_binPan = true;
5022  m_binGesture = true;
5023  // qDebug() << "pg1";
5024  break;
5025 
5026  case GestureUpdated:
5027  if (m_binPan) {
5028  if (!g_GLOptions.m_bUseCanvasPanning) {
5029  // qDebug() << "slowpan" << dx << dy;
5030 
5031  m_pParentCanvas->FreezePiano();
5032  m_pParentCanvas->PanCanvas(dx, -dy);
5033  m_pParentCanvas->ThawPiano();
5034 
5035  } else {
5036  FastPan(dx, dy);
5037  }
5038 
5039  panx -= dx;
5040  pany -= dy;
5041  }
5042  break;
5043 
5044  case GestureFinished:
5045  // qDebug() << "panGestureFinished";
5046 
5047  m_pParentCanvas->UpdateCanvasControlBar();
5048 
5049  m_binPan = false;
5050  m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5051 
5052  break;
5053 
5054  case GestureCanceled:
5055  m_binPan = false;
5056  m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5057  break;
5058 
5059  default:
5060  break;
5061  }
5062 
5063  m_bgestureGuard = true;
5064  m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5065  m_bforcefull = false;
5066 
5067  // qDebug() << "panGestureDone";
5068 }
5069 
5070 float zoom_inc = 1.0;
5071 bool first_zout = false;
5072 
5073 void glChartCanvas::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5074  float zoom_gain = 1.0;
5075  float zout_gain = 1.0;
5076 
5077  float zoom_val;
5078  float total_zoom_val;
5079 
5080  float max_zoom_scale = 1000.;
5081  float min_zoom_scale = 2e8;
5082 
5083  if (event.GetScaleFactor() > 1)
5084  zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5085  else
5086  zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zout_gain);
5087 
5088  if (event.GetTotalScaleFactor() > 1)
5089  total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5090  else
5091 #if 0
5092  total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zout_gain);
5093 
5094  double projected_scale = cc1->GetVP().chart_scale / total_zoom_val;
5095 
5096  // Max zoom in is set by scale of quilt reference chart, consistent with chart render limits set elsewhere.
5097  float max_zoom_scale = 1000.;
5098  if( cc1->GetVP().b_quilt) {
5099  int ref_index = cc1->GetQuiltRefChartdbIndex();
5100 // if((ref_index >= 0) && ChartData){
5101 // max_zoom_scale = ChartData->GetDBChartScale(ref_index) / 8.0;
5102 // }
5103  }
5104 
5105 
5106  float min_zoom_scale = 2e8;
5107 
5108 #endif
5109 
5110  total_zoom_val =
5111  1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5112 
5113  double projected_scale =
5114  m_pParentCanvas->GetVP().chart_scale / total_zoom_val;
5115 
5116  switch (event.GetState()) {
5117  case GestureStarted:
5118  first_zout = false;
5119  m_binPinch = true;
5120  m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5121  // cancel" event was lost
5122  m_binGesture = true;
5123  // qDebug() << "pg2";
5124  m_pinchStart = event.GetCenterPoint();
5125  m_lpinchPoint = m_pinchStart;
5126 
5127  m_pParentCanvas->GetCanvasPixPoint(event.GetCenterPoint().x,
5128  event.GetCenterPoint().y, m_pinchlat,
5129  m_pinchlon);
5130  // qDebug() << "center" << event.GetCenterPoint().x <<
5131  // event.GetCenterPoint().y;
5132 
5133  m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5134  m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5135 
5136  // Render the full charts with overlay objects onto the frame buffer.
5137  SetCurrent(*m_pcontext);
5138  RenderScene();
5139 
5140  zoom_inc = 1.0;
5141  break;
5142 
5143  case GestureUpdated:
5144  if (g_GLOptions.m_bUseCanvasPanning) {
5145  if (projected_scale < min_zoom_scale) {
5146  wxPoint pinchPoint = event.GetCenterPoint();
5147 
5148  float dx = pinchPoint.x - m_lpinchPoint.x;
5149  float dy = pinchPoint.y - m_lpinchPoint.y;
5150 
5151  FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5152  -dx / total_zoom_val, dy / total_zoom_val);
5153 
5154  m_lpinchPoint = pinchPoint;
5155  }
5156  } else {
5157  // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5158  if (1 || ((total_zoom_val > 1) && !first_zout)) { // Zoom in
5159  wxPoint pinchPoint = event.GetCenterPoint();
5160 
5161  float dx = pinchPoint.x - m_lpinchPoint.x;
5162  float dy = pinchPoint.y - m_lpinchPoint.y;
5163 
5164  if ((projected_scale > max_zoom_scale) &&
5165  (projected_scale < min_zoom_scale))
5166  FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5167  -dx / total_zoom_val, dy / total_zoom_val);
5168 
5169  m_lpinchPoint = pinchPoint;
5170 
5171  } else {
5172  first_zout = true;
5173  zoom_inc *= zoom_val;
5174  if ((zoom_inc < 0.9) || (zoom_inc > 1.1)) {
5175  m_pParentCanvas->ZoomCanvas(zoom_inc, false);
5176  zoom_inc = 1.0;
5177  }
5178 
5179  wxPoint pinchPoint = event.GetCenterPoint();
5180  float dx = pinchPoint.x - m_lpinchPoint.x;
5181  float dy = pinchPoint.y - m_lpinchPoint.y;
5182  m_pParentCanvas->PanCanvas(-dx, -dy);
5183  m_lpinchPoint = pinchPoint;
5184 
5185  // SetCurrent(*m_pcontext);
5186  // RenderScene();
5187  // g_Piano->DrawGL(cc1->m_canvas_height -
5188  // g_Piano->GetHeight()); SwapBuffers();
5189  }
5190  }
5191 
5192  break;
5193 
5194  case GestureFinished: {
5195  // qDebug() << "finish totalzoom" << total_zoom_val <<
5196  // projected_scale;
5197 
5198  float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5199  float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5200  float dy = 0;
5201  float dx = 0;
5202 
5203  float tzoom = total_zoom_val;
5204 
5205  if (projected_scale >= min_zoom_scale)
5206  tzoom = m_pParentCanvas->GetVP().chart_scale / min_zoom_scale;
5207 
5208  if (projected_scale < max_zoom_scale)
5209  tzoom = m_pParentCanvas->GetVP().chart_scale / max_zoom_scale;
5210 
5211  dx = (cc_x - m_cc_x) * tzoom;
5212  dy = -(cc_y - m_cc_y) * tzoom;
5213 
5214 
5215  if (zoomTimer.IsRunning()) {
5216  // qDebug() << "Final zoom";
5217  m_zoomFinal = true;
5218  m_zoomFinalZoom = tzoom;
5219  m_zoomFinaldx = dx;
5220  m_zoomFinaldy = dy;
5221  }
5222 
5223  else {
5224  double final_projected_scale =
5225  m_pParentCanvas->GetVP().chart_scale / tzoom;
5226  // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5227 
5228  if (final_projected_scale < min_zoom_scale) {
5229  // qDebug() << "zoomit";
5230  m_pParentCanvas->ZoomCanvas(tzoom, false);
5231  m_pParentCanvas->PanCanvas(dx, dy);
5232  m_pParentCanvas->m_pQuilt->Invalidate();
5233  m_bforcefull = true;
5234 
5235  } else {
5236  double new_scale =
5237  m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5238  // qDebug() << "clampit";
5239  m_pParentCanvas->SetVPScale(new_scale);
5240  m_pParentCanvas->m_pQuilt->Invalidate();
5241  m_bforcefull = true;
5242  }
5243  }
5244 
5245  // if( projected_scale < 3e8)
5246  // m_pParentCanvas->ZoomCanvas( total_zoom_val, false
5247  // );
5248  // else
5249  // m_pParentCanvas->ZoomCanvas(m_pParentCanvas->GetVP().chart_scale
5250  // / 3e8, false);
5251 
5252  m_binPinch = false;
5253  m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5254  break;
5255  }
5256 
5257  case GestureCanceled:
5258  m_binPinch = false;
5259  m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5260  break;
5261 
5262  default:
5263  break;
5264  }
5265 
5266  m_bgestureGuard = true;
5267  // m_bpinchGuard = true;
5268  m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5269 }
5270 
5271 void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5272  // On some devices, the pan GestureFinished event fails to show up
5273  // Watch for this case, and fix it.....
5274  // qDebug() << "onGestureTimerEvent";
5275 
5276  if (m_binPan) {
5277  m_binPan = false;
5278  Invalidate();
5279  Update();
5280  }
5281  m_bgestureGuard = false;
5282  m_bpinchGuard = false;
5283  m_binGesture = false;
5284  m_bforcefull = false;
5285 }
5286 
5287 void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5288  // qDebug() << "onGestureFinishTimerEvent";
5289 
5290  // signal gesture is finished after a delay
5291  m_binGesture = false;
5292  m_bforcefull = false;
5293 }
5294 
5295 #endif
5296 
5297 void glChartCanvas::configureShaders(ViewPort & vp) {
5298 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5299  mat4x4 I;
5300  mat4x4_identity(I);
5301 
5302  ViewPort *pvp = (ViewPort *)&vp;
5303 
5304  GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5305  shader->Bind();
5306  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5307  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5308  shader->UnBind();
5309 
5310 // glUseProgram(color_tri_shader_program);
5311 // GLint matloc = glGetUniformLocation(color_tri_shader_program, "MVMatrix");
5312 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5313 // (const GLfloat *)pvp->vp_transform);
5314 // GLint transloc =
5315 // glGetUniformLocation(color_tri_shader_program, "TransformMatrix");
5316 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5317 
5318 
5319 // glUseProgram(texture_2D_shader_program);
5320 // matloc = glGetUniformLocation(texture_2D_shader_program, "MVMatrix");
5321 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5322 // (const GLfloat *)pvp->vp_transform);
5323 // transloc =
5324 // glGetUniformLocation(texture_2D_shader_program, "TransformMatrix");
5325 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5326 
5327  shader = ptexture_2D_shader_program[GetCanvasIndex()];
5328  shader->Bind();
5329  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5330  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5331  shader->UnBind();
5332 
5333 // glUseProgram(circle_filled_shader_program);
5334 // matloc = glGetUniformLocation(circle_filled_shader_program, "MVMatrix");
5335 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5336 // (const GLfloat *)pvp->vp_transform);
5337 // transloc =
5338 // glGetUniformLocation(circle_filled_shader_program, "TransformMatrix");
5339 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5340 
5341  shader = pcircle_filled_shader_program[GetCanvasIndex()];
5342  shader->Bind();
5343  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5344  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5345  shader->UnBind();
5346 
5347 
5348  shader = ptexture_2DA_shader_program[GetCanvasIndex()];
5349  shader->Bind();
5350  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5351  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5352  shader->UnBind();
5353 
5354  //glUseProgram(AALine_shader_program);
5355  //matloc = glGetUniformLocation(AALine_shader_program, "MVMatrix");
5356  //glUniformMatrix4fv(matloc, 1, GL_FALSE,
5357  // (const GLfloat *)pvp->vp_transform);
5358 
5359  shader = pAALine_shader_program[GetCanvasIndex()];
5360  shader->Bind();
5361  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5362  shader->UnBind();
5363 
5364  shader = pring_shader_program[GetCanvasIndex()];
5365  shader->Bind();
5366  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5367  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5368  shader->UnBind();
5369 
5370  // Leftover shader required by some older Android plugins
5371  if (texture_2DA_shader_program){
5372  glUseProgram(texture_2DA_shader_program);
5373  GLint matloc = glGetUniformLocation(texture_2DA_shader_program, "MVMatrix");
5374  glUniformMatrix4fv(matloc, 1, GL_FALSE,
5375  (const GLfloat *)pvp->vp_matrix_transform);
5376  GLint transloc =
5377  glGetUniformLocation(texture_2DA_shader_program, "TransformMatrix");
5378  glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5379  }
5380 
5381  m_gldc.m_texfont.PrepareShader(vp.pix_width, vp.pix_height, vp.rotation);
5382 
5383 #endif
5384  }
5385 
5386 
5387 void glChartCanvas::RenderTextures(ocpnDC &dc, float *coords, float *uvCoords,
5388  int nVertex, ViewPort *vp) {
5389 //#ifdef USE_ANDROID_GLES2
5390 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5391  int nl = nVertex / 4;
5392  float *lc = coords;
5393  float *luv = uvCoords;
5394 
5395  while (nl) {
5396  RenderSingleTexture(dc, lc, luv, vp, 0, 0, 0);
5397 
5398  lc += 8;
5399  luv += 8;
5400  nl--;
5401  }
5402 
5403 #else
5404  glEnableClientState(GL_VERTEX_ARRAY);
5405  glEnableClientState(GL_TEXTURE_COORD_ARRAY);
5406 
5407  glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), uvCoords);
5408  glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
5409  glDrawArrays(GL_QUADS, 0, 4);
5410 
5411 #endif
5412 
5413  return;
5414  }
5415 
5416 void glChartCanvas::RenderSingleTexture(ocpnDC &dc, float *coords, float *uvCoords,
5417  ViewPort *vp, float dx, float dy,
5418  float angle_rad) {
5419 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5420 
5421  GLShaderProgram *shader = ptexture_2D_shader_program[dc.m_canvasIndex];
5422  if(!shader) return;
5423 
5424  shader->Bind();
5425 
5426  // Set up the texture sampler to texture unit 0
5427  shader->SetUniform1i("uTex", 0);
5428 
5429  // Rotate
5430  mat4x4 I, Q;
5431  mat4x4_identity(I);
5432  mat4x4_rotate_Z(Q, I, angle_rad);
5433 
5434  // Translate
5435  Q[3][0] = dx;
5436  Q[3][1] = dy;
5437 
5438 
5439  shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
5440 
5441  float co1[8];
5442  float tco1[8];
5443 
5444  shader->SetAttributePointerf("aPos", co1);
5445  shader->SetAttributePointerf("aUV", tco1);
5446 
5447 
5448 // Perform the actual drawing.
5449 
5450 // For some reason, glDrawElements is busted on Android
5451 // So we do this a hard ugly way, drawing two triangles...
5452 #if 0
5453  GLushort indices1[] = {0,1,3,2};
5454  glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
5455 #else
5456 
5457  co1[0] = coords[0];
5458  co1[1] = coords[1];
5459  co1[2] = coords[2];
5460  co1[3] = coords[3];
5461  co1[4] = coords[6];
5462  co1[5] = coords[7];
5463  co1[6] = coords[4];
5464  co1[7] = coords[5];
5465 
5466  tco1[0] = uvCoords[0];
5467  tco1[1] = uvCoords[1];
5468  tco1[2] = uvCoords[2];
5469  tco1[3] = uvCoords[3];
5470  tco1[4] = uvCoords[6];
5471  tco1[5] = uvCoords[7];
5472  tco1[6] = uvCoords[4];
5473  tco1[7] = uvCoords[5];
5474 
5475  //glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
5476  //glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
5477 
5478  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5479 
5480  shader->UnBind();
5481 
5482 #endif
5483 
5484 #else
5485 #endif
5486 
5487  return;
5488  }
5489 
5490 void glChartCanvas::RenderColorRect(wxRect r, wxColor & color) {
5491 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5492 
5493  GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5494  shader->Bind();
5495 
5496  shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)m_pParentCanvas->GetpVP()->vp_matrix_transform);
5497 
5498  float colorv[4];
5499  colorv[0] = color.Red() / float(256);
5500  colorv[1] = color.Green() / float(256);
5501  colorv[2] = color.Blue() / float(256);
5502  colorv[3] = 1.0;
5503  shader->SetUniform4fv("color", colorv);
5504 
5505  float pf[8];
5506  pf[0] = r.x + r.width;
5507  pf[1] = r.y;
5508  pf[2] = r.x;
5509  pf[3] = r.y;
5510  pf[4] = r.x + r.width;
5511  pf[5] = r.y + r.height;
5512  pf[6] = r.x;
5513  pf[7] = r.y + r.height;
5514  shader->SetAttributePointerf("position", pf);
5515 
5516  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5517 
5518  shader->UnBind();
5519 
5520 #else
5521 #endif
5522  }
5523 
5524 void glChartCanvas::RenderScene(bool bRenderCharts, bool bRenderOverlays) {
5525 
5526 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5527 
5528  ViewPort VPoint = m_pParentCanvas->VPoint;
5529  ocpnDC gldc(*this);
5530 
5531  int w, h;
5532  GetClientSize(&w, &h);
5533  int sx = GetSize().x;
5534  int sy = GetSize().y;
5535 
5536  OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
5537 
5538  glViewport(0, 0, (GLint)w, (GLint)h);
5539 
5540  if (s_b_useStencil) {
5541  glEnable(GL_STENCIL_TEST);
5542  glStencilMask(0xff);
5543  glClear(GL_STENCIL_BUFFER_BIT);
5544  glDisable(GL_STENCIL_TEST);
5545  }
5546 
5547  // Make sure we have a valid quilt composition
5548  m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
5549 
5550  // set opengl settings that don't normally change
5551  // this should be able to go in SetupOpenGL, but it's
5552  // safer here incase a plugin mangles these
5553  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
5554  glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
5555  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5556 
5557  // enable rendering to texture in framebuffer object
5558  glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
5559 
5560  glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
5561  g_texture_rectangle_format,
5562  m_cache_tex[m_cache_page], 0);
5563 
5564  m_fbo_offsetx = 0;
5565  m_fbo_offsety = 0;
5566  m_fbo_swidth = sx;
5567  m_fbo_sheight = sy;
5568 
5569  if (bRenderCharts) RenderCharts(gldc, screen_region);
5570 
5571  if (bRenderOverlays) {
5572  RenderS57TextOverlay(m_pParentCanvas->VPoint);
5573  RenderMBTilesOverlay(m_pParentCanvas->VPoint);
5574  DrawStaticRoutesTracksAndWaypoints(m_pParentCanvas->VPoint);
5575  DrawDynamicRoutesTracksAndWaypoints(VPoint);
5576  DrawFloatingOverlayObjects(m_gldc);
5577  }
5578 
5579  // All done, so disable Render to FBO
5580  glBindFramebuffer(GL_FRAMEBUFFER, 0);
5581 
5582 #endif
5583  }
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
Definition: chcanv.cpp:7129
Definition: IDX_entry.h:41
bool Compose(const ViewPort &vp)
Definition: Quilt.cpp:1694
Definition: route.h:75
Set of basemaps at different resolutions.
Definition: tcmgr.h:86
Definition: track.h:78
Definition: ocpndc.h:58
Definition: Quilt.cpp:867