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