1 /******************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  Layer to perform wxDC drawing using wxDC or opengl
5  * Author:   Sean D'Epagnier
6  *
7  ***************************************************************************
8  *   Copyright (C) 2011 by Sean D'Epagnier                                 *
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 
28 #include "wx/wxprec.h"
29 
30 #ifndef  WX_PRECOMP
31 #include "wx/wx.h"
32 #endif
33 
34 #include "ocpn_plugin.h"
35 #include "linmath.h"
36 
37 
38 #ifdef __MSVC__
39 #include <windows.h>
40 #endif
41 
42 #ifdef ocpnUSE_GL
43 #include <wx/glcanvas.h>
44 #endif
45 
46 #include <wx/graphics.h>
47 #include <wx/dcclient.h>
48 
49 #include <vector>
50 
51 #include "pi_ocpndc.h"
52 #include "wx28compat.h"
53 #include "cutil.h"
54 
55 
56 
57 #ifdef __OCPN__ANDROID__
58 #include <qopengl.h>
59 #include "GL/gl_private.h"
60 #else
61 #include "GL/gl.h"
62 #include "GL/glu.h"
63 #endif
64 
65 #ifdef USE_ANDROID_GLES2
66 #include "pi_shaders.h"
67 #include <gl2.h>
68 #endif
69 
70 
71 #ifdef __OCPN__ANDROID__
72 #include "qdebug.h"
73 #endif
74 
75 extern float g_piGLMinSymbolLineWidth;
76 wxArrayPtrVoid pi_gTesselatorVertices;
77 
78 #ifdef USE_ANDROID_GLES2
79 extern GLint pi_color_tri_shader_program;
80 extern GLint pi_circle_filled_shader_program;
81 #endif
82 
NextPow2(int size)83 int NextPow2(int size)
84 {
85     int n = size-1;          // compute dimensions needed as next larger power of 2
86     int shift = 1;
87     while ((n+1) & n){
88         n |= n >> shift;
89         shift <<= 1;
90     }
91 
92     return n + 1;
93 }
94 
95 //----------------------------------------------------------------------------
96 /* pass the dc to the constructor, or NULL to use opengl */
pi_ocpnDC(wxGLCanvas & canvas)97 pi_ocpnDC::pi_ocpnDC( wxGLCanvas &canvas ) :
98         glcanvas( &canvas ), dc( NULL ), m_pen( wxNullPen ), m_brush( wxNullBrush )
99 {
100 #if wxUSE_GRAPHICS_CONTEXT
101     pgc = NULL;
102 #endif
103 #ifdef ocpnUSE_GL
104     m_textforegroundcolour = wxColour( 0, 0, 0 );
105 #endif
106     m_buseTex = GetLocaleCanonicalName().IsSameAs(_T("en_US"));
107     workBuf = NULL;
108     workBufSize = 0;
109     s_odc_tess_work_buf = NULL;
110 
111     #ifdef USE_ANDROID_GLES2
112     s_odc_tess_vertex_idx = 0;
113     s_odc_tess_vertex_idx_this = 0;
114     s_odc_tess_buf_len = 0;
115 
116     s_odc_tess_work_buf = (GLfloat *)malloc( 100 * sizeof(GLfloat));
117     s_odc_tess_buf_len = 100;
118 
119     pi_loadShaders();
120 
121     #endif
122 
123 }
124 
pi_ocpnDC(wxDC & pdc)125 pi_ocpnDC::pi_ocpnDC( wxDC &pdc ) :
126         glcanvas( NULL ), dc( &pdc ), m_pen( wxNullPen ), m_brush( wxNullBrush )
127 {
128 #if wxUSE_GRAPHICS_CONTEXT
129     pgc = NULL;
130     wxMemoryDC *pmdc = wxDynamicCast(dc, wxMemoryDC);
131     if( pmdc ) pgc = wxGraphicsContext::Create( *pmdc );
132     else {
133         wxClientDC *pcdc = wxDynamicCast(dc, wxClientDC);
134         if( pcdc ) pgc = wxGraphicsContext::Create( *pcdc );
135     }
136 #endif
137     m_textforegroundcolour = wxColour( 0, 0, 0 );
138     m_buseTex = GetLocaleCanonicalName().IsSameAs(_T("en_US"));
139     workBuf = NULL;
140     workBufSize = 0;
141     s_odc_tess_work_buf = NULL;
142 
143 }
144 
pi_ocpnDC()145 pi_ocpnDC::pi_ocpnDC() :
146         glcanvas( NULL ), dc( NULL ), m_pen( wxNullPen ), m_brush( wxNullBrush )
147 {
148 #if wxUSE_GRAPHICS_CONTEXT
149     pgc = NULL;
150 #endif
151     m_buseTex = GetLocaleCanonicalName().IsSameAs(_T("en_US"));
152     workBuf = NULL;
153     workBufSize = 0;
154     s_odc_tess_work_buf = NULL;
155 
156 #ifdef USE_ANDROID_GLES2
157     pi_loadShaders();
158 #endif
159 }
160 
~pi_ocpnDC()161 pi_ocpnDC::~pi_ocpnDC()
162 {
163 #if wxUSE_GRAPHICS_CONTEXT
164     if( pgc ) delete pgc;
165 #endif
166     free(workBuf);
167 
168     free(s_odc_tess_work_buf);
169 
170 }
171 
SetVP(PlugIn_ViewPort * vp)172 void pi_ocpnDC::SetVP(PlugIn_ViewPort *vp)
173 {
174 #ifdef USE_ANDROID_GLES2
175     configureShaders(vp->pix_width, vp->pix_height);
176 #endif
177     m_vpSize = wxSize(vp->pix_width, vp->pix_height);
178 }
179 
180 
Clear()181 void pi_ocpnDC::Clear()
182 {
183     if( dc ) dc->Clear();
184     else {
185 #ifdef ocpnUSE_GL
186         wxBrush tmpBrush = m_brush;
187         int w, h;
188         SetBrush( wxBrush( glcanvas->GetBackgroundColour() ) );
189         glcanvas->GetSize( &w, &h );
190         DrawRectangle( 0, 0, w, h );
191         SetBrush( tmpBrush );
192 #endif
193     }
194 }
195 
SetBackground(const wxBrush & brush)196 void pi_ocpnDC::SetBackground( const wxBrush &brush )
197 {
198     if( dc )
199         dc->SetBackground( brush );
200     else {
201 #ifdef ocpnUSE_GL
202         glcanvas->SetBackgroundColour( brush.GetColour() );
203 #endif
204     }
205 }
206 
SetPen(const wxPen & pen)207 void pi_ocpnDC::SetPen( const wxPen &pen )
208 {
209     if( dc ) {
210         if( pen == wxNullPen ) dc->SetPen( *wxTRANSPARENT_PEN );
211         else
212             dc->SetPen( pen );
213     } else
214         m_pen = pen;
215 }
216 
SetBrush(const wxBrush & brush)217 void pi_ocpnDC::SetBrush( const wxBrush &brush )
218 {
219     if( dc ) dc->SetBrush( brush );
220     else
221         m_brush = brush;
222 }
223 
SetTextForeground(const wxColour & colour)224 void pi_ocpnDC::SetTextForeground( const wxColour &colour )
225 {
226     if( dc ) dc->SetTextForeground( colour );
227     else
228         m_textforegroundcolour = colour;
229 }
230 
SetFont(const wxFont & font)231 void pi_ocpnDC::SetFont( const wxFont& font )
232 {
233     if( dc ) dc->SetFont( font );
234     else
235         m_font = font;
236 }
237 
GetPen() const238 const wxPen& pi_ocpnDC::GetPen() const
239 {
240     if( dc ) return dc->GetPen();
241     return m_pen;
242 }
243 
GetBrush() const244 const wxBrush& pi_ocpnDC::GetBrush() const
245 {
246     if( dc ) return dc->GetBrush();
247     return m_brush;
248 }
249 
GetFont() const250 const wxFont& pi_ocpnDC::GetFont() const
251 {
252     if( dc ) return dc->GetFont();
253     return m_font;
254 }
255 
GetSize(wxCoord * width,wxCoord * height) const256 void pi_ocpnDC::GetSize( wxCoord *width, wxCoord *height ) const
257 {
258     if( dc )
259         dc->GetSize( width, height );
260     else {
261 #ifdef ocpnUSE_GL
262         glcanvas->GetSize( width, height );
263 #endif
264     }
265 }
266 
SetGLAttrs(bool highQuality)267 void pi_ocpnDC::SetGLAttrs( bool highQuality )
268 {
269 #ifdef ocpnUSE_GL
270 
271  // Enable anti-aliased polys, at best quality
272     if( highQuality ) {
273         glEnable( GL_LINE_SMOOTH );
274         glEnable( GL_POLYGON_SMOOTH );
275         glEnable( GL_BLEND );
276     } else {
277         glDisable(GL_LINE_SMOOTH);
278         glDisable( GL_POLYGON_SMOOTH );
279         glDisable( GL_BLEND );
280     }
281 #endif
282 }
283 
SetGLStipple() const284 void pi_ocpnDC::SetGLStipple() const
285 {
286 #ifdef ocpnUSE_GL
287 
288 #ifndef USE_ANDROID_GLES2
289     switch( m_pen.GetStyle() ) {
290         case wxDOT: {
291             glLineStipple( 1, 0x3333 );
292             glEnable( GL_LINE_STIPPLE );
293             break;
294         }
295         case wxLONG_DASH: {
296             glLineStipple( 1, 0xFFF8 );
297             glEnable( GL_LINE_STIPPLE );
298             break;
299         }
300         case wxSHORT_DASH: {
301             glLineStipple( 1, 0x3F3F );
302             glEnable( GL_LINE_STIPPLE );
303             break;
304         }
305         case wxDOT_DASH: {
306             glLineStipple( 1, 0x8FF1 );
307             glEnable( GL_LINE_STIPPLE );
308             break;
309         }
310         default: break;
311     }
312 #endif
313 #endif
314 }
315 
316 #ifdef ocpnUSE_GL
317 /* draw a half circle using triangles */
piDrawEndCap(float x1,float y1,float t1,float angle)318 void piDrawEndCap(float x1, float y1, float t1, float angle)
319 {
320 #ifndef USE_ANDROID_GLES2
321     const int steps = 16;
322     float xa, ya;
323     bool first = true;
324     for(int i = 0; i <= steps; i++) {
325         float a = angle + M_PI/2 + M_PI/steps*i;
326 
327         float xb = x1 + t1 / 2 * cos( a );
328         float yb = y1 + t1 / 2 * sin( a );
329         if(first)
330             first = false;
331         else {
332             glVertex2f( x1, y1 );
333             glVertex2f( xa, ya );
334             glVertex2f( xb, yb );
335         }
336         xa = xb, ya = yb;
337     }
338 #endif
339 }
340 #endif
341 
342 // Draws a line between (x1,y1) - (x2,y2) with a start thickness of t1
piDrawGLThickLine(float x1,float y1,float x2,float y2,wxPen pen,bool b_hiqual)343 void piDrawGLThickLine( float x1, float y1, float x2, float y2, wxPen pen, bool b_hiqual )
344 {
345 #ifdef ocpnUSE_GL
346 
347     float angle = atan2f( y2 - y1, x2 - x1 );
348     float t1 = pen.GetWidth();
349     float t2sina1 = t1 / 2 * sinf( angle );
350     float t2cosa1 = t1 / 2 * cosf( angle );
351 
352 #ifndef USE_ANDROID_GLES2
353     glBegin( GL_TRIANGLES );
354 
355     //    n.b.  The dwxDash interpretation for GL only allows for 2 elements in the dash table.
356     //    The first is assumed drawn, second is assumed space
357     wxDash *dashes;
358     int n_dashes = pen.GetDashes( &dashes );
359     if( n_dashes ) {
360         float lpix = sqrtf( powf( (float) (x1 - x2), 2) + powf( (float) (y1 - y2), 2) );
361         float lrun = 0.;
362         float xa = x1;
363         float ya = y1;
364         float ldraw = t1 * dashes[0];
365         float lspace = t1 * dashes[1];
366 
367         while( lrun < lpix ) {
368             //    Dash
369             float xb = xa + ldraw * cosf( angle );
370             float yb = ya + ldraw * sinf( angle );
371 
372             if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
373             {
374                 xb = x2;
375                 yb = y2;
376             }
377 
378             glVertex2f( xa + t2sina1, ya - t2cosa1 );
379             glVertex2f( xb + t2sina1, yb - t2cosa1 );
380             glVertex2f( xb - t2sina1, yb + t2cosa1 );
381 
382             glVertex2f( xb - t2sina1, yb + t2cosa1 );
383             glVertex2f( xa - t2sina1, ya + t2cosa1 );
384             glVertex2f( xa + t2sina1, ya - t2cosa1 );
385 
386             xa = xb;
387             ya = yb;
388             lrun += ldraw;
389 
390             //    Space
391             xb = xa + lspace * cos( angle );
392             yb = ya + lspace * sin( angle );
393 
394             xa = xb;
395             ya = yb;
396             lrun += lspace;
397         }
398     } else {
399         glVertex2f( x1 + t2sina1, y1 - t2cosa1 );
400         glVertex2f( x2 + t2sina1, y2 - t2cosa1 );
401         glVertex2f( x2 - t2sina1, y2 + t2cosa1 );
402 
403         glVertex2f( x2 - t2sina1, y2 + t2cosa1 );
404         glVertex2f( x1 - t2sina1, y1 + t2cosa1 );
405         glVertex2f( x1 + t2sina1, y1 - t2cosa1 );
406 
407         /* wx draws a nice rounded end in dc mode, so replicate
408            this for opengl mode, should this be done for the dashed mode case? */
409         if(pen.GetCap() == wxCAP_ROUND) {
410              piDrawEndCap( x1, y1, t1, angle);
411              piDrawEndCap( x2, y2, t1, angle + M_PI);
412         }
413 
414     }
415 
416     glEnd();
417 #else
418 
419     //    n.b.  The dwxDash interpretation for GL only allows for 2 elements in the dash table.
420     //    The first is assumed drawn, second is assumed space
421     wxDash *dashes;
422     int n_dashes = pen.GetDashes( &dashes );
423     if( n_dashes ) {
424         float lpix = sqrtf( powf( (float) (x1 - x2), 2) + powf( (float) (y1 - y2), 2) );
425         float lrun = 0.;
426         float xa = x1;
427         float ya = y1;
428         float ldraw = t1 * dashes[0];
429         float lspace = t1 * dashes[1];
430 
431         glUseProgram(pi_color_tri_shader_program);
432 
433         float vert[12];
434 
435         // Disable VBO's (vertex buffer objects) for attributes.
436         glBindBuffer( GL_ARRAY_BUFFER, 0 );
437         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
438 
439         GLint pos = glGetAttribLocation(pi_color_tri_shader_program, "position");
440         glEnableVertexAttribArray(pos);
441         glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), vert);
442 
443         // Build Transform matrix
444         mat4x4 I;
445         mat4x4_identity(I);
446 
447         GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"TransformMatrix");
448         glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)I);
449 
450         wxColor c = pen.GetColour();
451         float colorv[4];
452         colorv[0] = c.Red() / float(256);
453         colorv[1] = c.Green() / float(256);
454         colorv[2] = c.Blue() / float(256);
455         colorv[3] = c.Alpha() / float(256);
456 
457         GLint colloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
458         glUniform4fv(colloc, 1, colorv);
459 
460         while( lrun < lpix ) {
461             //    Dash
462             float xb = xa + ldraw * cosf( angle );
463             float yb = ya + ldraw * sinf( angle );
464 
465             if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
466             {
467                 xb = x2;
468                 yb = y2;
469             }
470 
471             vert[0] = xa + t2sina1; vert[1] = ya - t2cosa1; vert[2] = xb + t2sina1; vert[3] = yb - t2cosa1; vert[4] = xb - t2sina1; vert[5] = yb + t2cosa1;
472             vert[6] = xb - t2sina1; vert[7] = yb + t2cosa1; vert[8] = xa - t2sina1; vert[9] = ya + t2cosa1; vert[10] = xa + t2sina1; vert[11] = ya - t2cosa1;
473 
474             glDrawArrays(GL_TRIANGLES, 0, 6);
475 
476 
477             xa = xb;
478             ya = yb;
479             lrun += ldraw;
480 
481             //    Space
482             xb = xa + lspace * cos( angle );
483             yb = ya + lspace * sin( angle );
484 
485             xa = xb;
486             ya = yb;
487             lrun += lspace;
488         }
489     } else {
490 
491         float vert[12];
492         vert[0] = x1 + t2sina1; vert[1] = y1 - t2cosa1; vert[2] = x2 + t2sina1; vert[3] = y2 - t2cosa1; vert[4] = x2 - t2sina1; vert[5] = y2 + t2cosa1;
493         vert[6] = x2 - t2sina1; vert[7] = y2 + t2cosa1; vert[8] = x1 - t2sina1; vert[9] = y1 + t2cosa1; vert[10] = x1 + t2sina1; vert[11] = y1 - t2cosa1;
494 
495         glUseProgram(pi_color_tri_shader_program);
496 
497         // Disable VBO's (vertex buffer objects) for attributes.
498         glBindBuffer( GL_ARRAY_BUFFER, 0 );
499         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
500 
501         GLint pos = glGetAttribLocation(pi_color_tri_shader_program, "position");
502         glEnableVertexAttribArray(pos);
503         glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), vert);
504 
505         // Build Transform matrix
506         mat4x4 I;
507         mat4x4_identity(I);
508 
509         GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"TransformMatrix");
510         glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)I);
511 
512         wxColor c = pen.GetColour();
513         float colorv[4];
514         colorv[0] = c.Red() / float(256);
515         colorv[1] = c.Green() / float(256);
516         colorv[2] = c.Blue() / float(256);
517         colorv[3] = c.Alpha() / float(256);
518 
519         GLint colloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
520         glUniform4fv(colloc, 1, colorv);
521 
522         glDrawArrays(GL_TRIANGLES, 0, 6);
523 
524 
525         /* wx draws a nice rounded end in dc mode, so replicate
526          *           this for opengl mode, should this be done for the dashed mode case? */
527 //         if(pen.GetCap() == wxCAP_ROUND) {
528 //             DrawEndCap( x1, y1, t1, angle);
529 //             DrawEndCap( x2, y2, t1, angle + M_PI);
530 //         }
531 //
532      }
533 
534 #endif
535 
536 #endif
537 }
538 
DrawLine(wxCoord x1,wxCoord y1,wxCoord x2,wxCoord y2,bool b_hiqual)539 void pi_ocpnDC::DrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual )
540 {
541 
542     if( dc )
543         dc->DrawLine( x1, y1, x2, y2 );
544 #ifdef ocpnUSE_GL
545     else if( ConfigurePen() ) {
546         bool b_draw_thick = false;
547 
548         float pen_width = wxMax(g_piGLMinSymbolLineWidth, m_pen.GetWidth());
549 
550         //      Enable anti-aliased lines, at best quality
551         if( b_hiqual ) {
552             SetGLStipple();
553 
554 #ifndef __WXQT__
555             glEnable( GL_BLEND );
556             glEnable( GL_LINE_SMOOTH );
557 #endif
558 
559             if( pen_width > 1.0 ) {
560                 GLint parms[2];
561                 glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
562                 if(glGetError())
563                     glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
564                 if( pen_width > parms[1] )
565                     b_draw_thick = true;
566                 else
567                     glLineWidth( pen_width );
568             } else
569                 glLineWidth( pen_width );
570         } else {
571             if( pen_width > 1 ) {
572                 GLint parms[2];
573                 glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
574                 if( pen_width > parms[1] ) b_draw_thick = true;
575                     else
576                         glLineWidth( pen_width );
577             } else
578                 glLineWidth( pen_width );
579         }
580 
581 
582 #ifdef USE_ANDROID_GLES2
583         if( b_draw_thick )
584             piDrawGLThickLine( x1, y1, x2, y2, m_pen, b_hiqual );
585         else {
586             glUseProgram(pi_color_tri_shader_program);
587 
588             float fBuf[4];
589             GLint pos = glGetAttribLocation(pi_color_tri_shader_program, "position");
590             glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), fBuf);
591             glEnableVertexAttribArray(pos);
592 
593 //             GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"MVMatrix");
594 //             glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)cc1->GetpVP()->vp_transform);
595 
596             float colorv[4];
597             colorv[0] = m_pen.GetColour().Red() / float(256);
598             colorv[1] = m_pen.GetColour().Green() / float(256);
599             colorv[2] = m_pen.GetColour().Blue() / float(256);
600             colorv[3] = 1.0;
601 
602             GLint colloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
603             glUniform4fv(colloc, 1, colorv);
604 
605             wxDash *dashes;
606             int n_dashes = m_pen.GetDashes( &dashes );
607             if( n_dashes ) {
608                 float angle = atan2f( (float) ( y2 - y1 ), (float) ( x2 - x1 ) );
609                 float cosa = cosf( angle );
610                 float sina = sinf( angle );
611                 float t1 = m_pen.GetWidth();
612 
613                 float lpix = sqrtf( powf(x1 - x2, 2) + powf(y1 - y2, 2) );
614                 float lrun = 0.;
615                 float xa = x1;
616                 float ya = y1;
617                 float ldraw = t1 * dashes[0];
618                 float lspace = t1 * dashes[1];
619 
620                 ldraw = wxMax(ldraw, 4.0);
621                 lspace = wxMax(lspace, 4.0);
622                 lpix = wxMin(lpix, 2000.0);
623 
624                 while( lrun < lpix ) {
625                     //    Dash
626                     float xb = xa + ldraw * cosa;
627                     float yb = ya + ldraw * sina;
628 
629                     if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
630                     {
631                         xb = x2;
632                         yb = y2;
633                     }
634 
635                     fBuf[0] = xa;
636                     fBuf[1] = ya;
637                     fBuf[2] = xb;
638                     fBuf[3] = yb;
639 
640                     glDrawArrays(GL_LINES, 0, 2);
641 
642                     xa = xa + ( lspace + ldraw ) * cosa;
643                     ya = ya + ( lspace + ldraw ) * sina;
644                     lrun += lspace + ldraw;
645 
646                 }
647             } else                    // not dashed
648             {
649                 fBuf[0] = x1;
650                 fBuf[1] = y1;
651                 fBuf[2] = x2;
652                 fBuf[3] = y2;
653 
654                 glDrawArrays(GL_LINES, 0, 2);
655             }
656         }
657 
658 #else
659         if( b_draw_thick )
660             piDrawGLThickLine( x1, y1, x2, y2, m_pen, b_hiqual );
661         else {
662             wxDash *dashes;
663             int n_dashes = m_pen.GetDashes( &dashes );
664             if( n_dashes ) {
665                 float angle = atan2f( (float) ( y2 - y1 ), (float) ( x2 - x1 ) );
666                 float cosa = cosf( angle );
667                 float sina = sinf( angle );
668                 float t1 = m_pen.GetWidth();
669 
670                 float lpix = sqrtf( powf(x1 - x2, 2) + powf(y1 - y2, 2) );
671                 float lrun = 0.;
672                 float xa = x1;
673                 float ya = y1;
674                 float ldraw = t1 * dashes[0];
675                 float lspace = t1 * dashes[1];
676 
677                 ldraw = wxMax(ldraw, 4.0);
678                 lspace = wxMax(lspace, 4.0);
679                 lpix = wxMin(lpix, 2000.0);
680 
681                 glBegin( GL_LINES );
682                 while( lrun < lpix ) {
683                     //    Dash
684                     float xb = xa + ldraw * cosa;
685                     float yb = ya + ldraw * sina;
686 
687                     if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
688                     {
689                         xb = x2;
690                         yb = y2;
691                         }
692 
693                         glVertex2f( xa, ya );
694                         glVertex2f( xb, yb );
695 
696                         xa = xa + ( lspace + ldraw ) * cosa;
697                         ya = ya + ( lspace + ldraw ) * sina;
698                         lrun += lspace + ldraw;
699 
700                     }
701                 glEnd();
702             } else                    // not dashed
703             {
704                 glBegin( GL_LINES );
705                 glVertex2i( x1, y1 );
706                 glVertex2i( x2, y2 );
707                 glEnd();
708             }
709         }
710 #endif
711         glDisable( GL_LINE_STIPPLE );
712 
713         if( b_hiqual ) {
714             glDisable( GL_LINE_SMOOTH );
715             glDisable( GL_BLEND );
716         }
717     }
718 #endif
719 }
720 
721 // Draws thick lines from triangles
piDrawGLThickLines(int n,wxPoint points[],wxCoord xoffset,wxCoord yoffset,wxPen pen,bool b_hiqual)722 void piDrawGLThickLines( int n, wxPoint points[],wxCoord xoffset,
723                        wxCoord yoffset, wxPen pen, bool b_hiqual )
724 {
725 #ifdef ocpnUSE_GL
726     if(n < 2)
727         return;
728 
729 #ifdef USE_ANDROID_GLES2
730      wxPoint p0 = points[0];
731      for( int i = 1; i < n; i++ ) {
732          piDrawGLThickLine( p0.x + xoffset, p0.y + yoffset, points[i].x + xoffset,
733                           points[i].y + yoffset, pen, b_hiqual );
734          p0 = points[i];
735         }
736      return;
737 #else
738 
739     /* for dashed case, for now just draw thick lines */
740     wxDash *dashes;
741     if( pen.GetDashes( &dashes ) )
742     {
743         wxPoint p0 = points[0];
744         for( int i = 1; i < n; i++ ) {
745             piDrawGLThickLine( p0.x + xoffset, p0.y + yoffset, points[i].x + xoffset,
746                              points[i].y + yoffset, pen, b_hiqual );
747             p0 = points[i];
748         }
749         return;
750     }
751 
752     /* cull zero segments */
753     wxPoint *cpoints = new wxPoint[n];
754     cpoints[0] = points[0];
755     int c = 1;
756     for( int i = 1; i < n; i++ ) {
757         if(points[i].x != points[i-1].x || points[i].y != points[i-1].y)
758             cpoints[c++] = points[i];
759     }
760 
761     /* nicer than than rendering each segment separately, this is because thick
762        line segments drawn as rectangles which have different angles have
763        rectangles which overlap and also leave a gap.
764        This code properly calculates vertexes for adjoining segments */
765     float t1 = pen.GetWidth();
766 
767     float x0 = cpoints[0].x, y0 = cpoints[0].y, x1 = cpoints[1].x, y1 = cpoints[1].y;
768     float a0 = atan2f( y1 - y0, x1 - x0 );
769 
770     // It is also possible to use triangle strip, (and triangle fan for endcap)
771     // to reduce vertex count.. is it worth it?
772     glBegin( GL_TRIANGLES );
773 
774     float t2sina0 = t1 / 2 * sinf( a0 );
775     float t2cosa0 = t1 / 2 * cosf( a0 );
776 
777     for( int i = 1; i < c; i++ ) {
778         float x2, y2;
779         float a1;
780 
781         if(i < c - 1) {
782             x2 = cpoints[i + 1].x, y2 = cpoints[i + 1].y;
783             a1 = atan2f( y2 - y1, x2 - x1 );
784         } else {
785             x2 = x1, y2 = y1;
786             a1 = a0;
787         }
788 
789         float aa = (a0 + a1) / 2;
790         float diff = fabsf(a0 - a1);
791         if(diff > M_PI)
792             diff -= 2 * (float)M_PI;
793         float rad = t1 / 2 / wxMax(cosf(diff / 2), .4);
794 
795         float t2sina1 = rad * sinf( aa );
796         float t2cosa1 = rad * cosf( aa );
797 
798         glVertex2f( x1 + t2sina1, y1 - t2cosa1 );
799         glVertex2f( x1 - t2sina1, y1 + t2cosa1 );
800         glVertex2f( x0 + t2sina0, y0 - t2cosa0 );
801 
802         glVertex2f( x0 - t2sina0, y0 + t2cosa0 );
803         glVertex2f( x0 + t2sina0, y0 - t2cosa0 );
804 
805         float dot = t2sina0 * t2sina1 + t2cosa0 * t2cosa1;
806         if(dot > 0)
807             glVertex2f( x1 - t2sina1, y1 + t2cosa1 );
808         else
809             glVertex2f( x1 + t2sina1, y1 - t2cosa1 );
810 
811         x0 = x1, x1 = x2;
812         y0 = y1, y1 = y2;
813         a0 = a1;
814         t2sina0 = t2sina1, t2cosa0 = t2cosa1;
815     }
816 
817     if(pen.GetCap() == wxCAP_ROUND) {
818         piDrawEndCap( x0, y0, t1, a0);
819         piDrawEndCap( x0, y0, t1, a0 + M_PI);
820      }
821 
822     glEnd();
823 
824     glPopAttrib();
825 
826     delete [] cpoints;
827  #endif
828  #endif
829  }
830 
DrawLines(int n,wxPoint points[],wxCoord xoffset,wxCoord yoffset,bool b_hiqual)831  void pi_ocpnDC::DrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, bool b_hiqual )
832 {
833     if( dc )
834         dc->DrawLines( n, points, xoffset, yoffset );
835 #ifdef ocpnUSE_GL
836     else if( ConfigurePen() ) {
837 
838 #ifdef __WXQT__
839         SetGLAttrs( false );            // Some QT platforms (Android) have trouble with GL_BLEND / GL_LINE_SMOOTH
840 #else
841         SetGLAttrs( b_hiqual );
842 #endif
843         bool b_draw_thick = false;
844 
845         glDisable( GL_LINE_STIPPLE );
846         SetGLStipple();
847 
848         //      Enable anti-aliased lines, at best quality
849         if( b_hiqual ) {
850             glEnable( GL_BLEND );
851             if( m_pen.GetWidth() > 1 ) {
852                 GLint parms[2];
853                 glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
854                 if(glGetError())
855                     glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
856 
857                 if( m_pen.GetWidth() > parms[1] )
858                     b_draw_thick = true;
859                 else
860                     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, m_pen.GetWidth()) );
861             } else
862                 glLineWidth( wxMax(g_piGLMinSymbolLineWidth, 1) );
863         } else {
864             if( m_pen.GetWidth() > 1 ) {
865                 GLint parms[2];
866                 glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
867                 if( m_pen.GetWidth() > parms[1] ) b_draw_thick = true;
868                 else
869                     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, m_pen.GetWidth()) );
870             } else
871                 glLineWidth( wxMax(g_piGLMinSymbolLineWidth, 1) );
872         }
873 
874         if( b_draw_thick) {
875             piDrawGLThickLines( n, points, xoffset, yoffset, m_pen, b_hiqual );
876 
877             if( b_hiqual ) {
878                 glDisable( GL_LINE_STIPPLE );
879                 glDisable( GL_POLYGON_SMOOTH );
880                 glDisable( GL_BLEND );
881             }
882 
883             return;
884         }
885 
886 #ifndef USE_ANDROID_GLES2
887 
888         glBegin( GL_LINE_STRIP );
889         for( int i = 0; i < n; i++ )
890             glVertex2i( points[i].x + xoffset, points[i].y + yoffset );
891         glEnd();
892 
893 #else
894 
895                 //  Grow the work buffer as necessary
896         if( workBufSize < (size_t)n*2 ){
897             workBuf = (float *)realloc(workBuf, (n*4) * sizeof(float));
898             workBufSize = n*4;
899         }
900 
901         for( int i = 0; i < n; i++ ){
902             workBuf[i*2] = points[i].x + xoffset;
903             workBuf[(i*2) + 1] = points[i].y + yoffset;
904         }
905 
906         glUseProgram(pi_color_tri_shader_program);
907 
908         GLint pos = glGetAttribLocation(pi_color_tri_shader_program, "position");
909         glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), workBuf);
910         glEnableVertexAttribArray(pos);
911 //         GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"MVMatrix");
912 //         glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)cc1->GetpVP()->vp_transform);
913 
914         float colorv[4];
915         colorv[0] = m_pen.GetColour().Red() / float(256);
916         colorv[1] = m_pen.GetColour().Green() / float(256);
917         colorv[2] = m_pen.GetColour().Blue() / float(256);
918         colorv[3] = m_pen.GetColour().Alpha() / float(256);1.0;
919 
920         GLint colloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
921         glUniform4fv(colloc, 1, colorv);
922 
923         glDrawArrays(GL_LINE_STRIP, 0, n);
924 
925 
926 #endif
927 
928 
929         if( b_hiqual ) {
930             glDisable( GL_LINE_STIPPLE );
931             glDisable( GL_POLYGON_SMOOTH );
932             glDisable( GL_BLEND );
933         }
934     }
935 #endif
936 }
937 
StrokeLine(wxCoord x1,wxCoord y1,wxCoord x2,wxCoord y2)938 void pi_ocpnDC::StrokeLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 )
939 {
940 #if wxUSE_GRAPHICS_CONTEXT
941     if( pgc ) {
942         pgc->SetPen( dc->GetPen() );
943         pgc->StrokeLine( x1, y1, x2, y2 );
944 
945         dc->CalcBoundingBox( x1, y1 );
946         dc->CalcBoundingBox( x2, y2 );
947     } else
948 #endif
949         DrawLine( x1, y1, x2, y2, true );
950 }
951 
StrokeLines(int n,wxPoint * points)952 void pi_ocpnDC::StrokeLines( int n, wxPoint *points) {
953     if(n < 2) /* optimization and also to avoid assertion in pgc->StrokeLines */
954         return;
955 
956 #if wxUSE_GRAPHICS_CONTEXT
957     if( pgc ) {
958         wxPoint2DDouble* dPoints = (wxPoint2DDouble*) malloc( n * sizeof( wxPoint2DDouble ) );
959         for( int i=0; i<n; i++ ) {
960             dPoints[i].m_x = points[i].x;
961             dPoints[i].m_y = points[i].y;
962         }
963         pgc->SetPen( dc->GetPen() );
964         pgc->StrokeLines( n, dPoints );
965         free( dPoints );
966     } else
967 #endif
968         DrawLines( n, points, 0, 0, true );
969 }
970 
DrawGLLineArray(int n,float * vertex_array,float * color_array,bool b_hiqual)971 void pi_ocpnDC::DrawGLLineArray( int n, float *vertex_array, float *color_array,  bool b_hiqual )
972 {
973 #ifdef ocpnUSE_GL
974         if( ConfigurePen() ) {
975 
976             #ifdef __WXQT__
977             SetGLAttrs( false );            // Some QT platforms (Android) have trouble with GL_BLEND / GL_LINE_SMOOTH
978             #else
979             SetGLAttrs( b_hiqual );
980             #endif
981             bool b_draw_thick = false;
982 
983             glDisable( GL_LINE_STIPPLE );
984             SetGLStipple();
985 
986             //      Enable anti-aliased lines, at best quality
987             if( b_hiqual ) {
988                 glEnable( GL_BLEND );
989                 if( m_pen.GetWidth() > 1 ) {
990                     //GLint parms[2];
991                     //glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
992                     //if(glGetError())
993                         //glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
994 
995                     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, m_pen.GetWidth()) );
996                 } else
997                     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, 1) );
998             } else {
999                 if( m_pen.GetWidth() > 1 ) {
1000                     //GLint parms[2];
1001                     //glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
1002                     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, m_pen.GetWidth()) );
1003                 } else
1004                     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, 1) );
1005             }
1006 
1007 #ifndef USE_ANDROID_GLES2
1008 
1009 // TODO
1010 //             glBegin( GL_LINE_STRIP );
1011 //             for( int i = 0; i < n; i++ )
1012 //                 glVertex2i( points[i].x + xoffset, points[i].y + yoffset );
1013 //             glEnd();
1014 
1015 #else
1016             glUseProgram(pi_colorv_tri_shader_program);
1017 
1018             GLint pos = glGetAttribLocation(pi_colorv_tri_shader_program, "position");
1019             glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), vertex_array);
1020             glEnableVertexAttribArray(pos);
1021 
1022             GLint colloc = glGetAttribLocation(pi_colorv_tri_shader_program, "colorv");
1023             glVertexAttribPointer(colloc, 4, GL_FLOAT, GL_FALSE, 4*sizeof(float), color_array);
1024             glEnableVertexAttribArray(colloc);
1025 
1026             glDrawArrays(GL_LINES, 0, n);
1027 
1028 
1029 #endif
1030 
1031 
1032             if( b_hiqual ) {
1033                 glDisable( GL_LINE_STIPPLE );
1034                 glDisable( GL_POLYGON_SMOOTH );
1035                 glDisable( GL_BLEND );
1036             }
1037         }
1038 #endif
1039 }
1040 
1041 
DrawRectangle(wxCoord x,wxCoord y,wxCoord w,wxCoord h)1042 void pi_ocpnDC::DrawRectangle( wxCoord x, wxCoord y, wxCoord w, wxCoord h )
1043 {
1044     if( dc )
1045         dc->DrawRectangle( x, y, w, h );
1046 #ifdef ocpnUSE_GL
1047     else {
1048 #ifndef USE_ANDROID_GLES2
1049         if( ConfigureBrush() ) {
1050             glBegin( GL_QUADS );
1051             glVertex2i( x, y );
1052             glVertex2i( x + w, y );
1053             glVertex2i( x + w, y + h );
1054             glVertex2i( x, y + h );
1055             glEnd();
1056         }
1057 
1058         if( ConfigurePen() ) {
1059             glBegin( GL_LINE_LOOP );
1060             glVertex2i( x, y );
1061             glVertex2i( x + w, y );
1062             glVertex2i( x + w, y + h );
1063             glVertex2i( x, y + h );
1064             glEnd();
1065         }
1066 #endif
1067     }
1068 #endif
1069 }
1070 
1071 /* draw the arc along corners */
drawrrhelper(wxCoord x0,wxCoord y0,wxCoord r,int quadrant,int steps)1072 static void drawrrhelper( wxCoord x0, wxCoord y0, wxCoord r, int quadrant, int steps )
1073 {
1074 #ifdef ocpnUSE_GL
1075 #ifndef USE_ANDROID_GLES2
1076     float step = 1.0/steps, rs = 2.0*r*step, rss = rs*step, x, y, dx, dy, ddx, ddy;
1077     switch(quadrant) {
1078     case 0: x =  r, y =  0, dx =   0, dy = -rs, ddx = -rss, ddy =  rss; break;
1079     case 1: x =  0, y = -r, dx = -rs, dy =   0, ddx =  rss, ddy =  rss; break;
1080     case 2: x = -r, y =  0, dx =   0, dy =  rs, ddx =  rss, ddy = -rss; break;
1081     case 3: x =  0, y =  r, dx =  rs, dy =   0, ddx = -rss, ddy = -rss; break;
1082     default: return; // avoid unitialized compiler warnings
1083     }
1084 
1085     for(int i=0; i<steps; i++) {
1086         glVertex2i( x0 + floor(x), y0 + floor(y) );
1087          x += dx+ddx/2,  y += dy+ddy/2;
1088         dx += ddx,      dy += ddy;
1089     }
1090     glVertex2i( x0 + floor(x), y0 + floor(y) );
1091 #endif
1092 #endif
1093 }
1094 
drawrrhelperGLES2(wxCoord x0,wxCoord y0,wxCoord r,int quadrant,int steps)1095 void pi_ocpnDC::drawrrhelperGLES2( wxCoord x0, wxCoord y0, wxCoord r, int quadrant, int steps )
1096 {
1097 #ifdef ocpnUSE_GL
1098     float step = 1.0/steps, rs = 2.0*r*step, rss = rs*step, x, y, dx, dy, ddx, ddy;
1099     switch(quadrant) {
1100         case 0: x =  r, y =  0, dx =   0, dy = -rs, ddx = -rss, ddy =  rss; break;
1101         case 1: x =  0, y = -r, dx = -rs, dy =   0, ddx =  rss, ddy =  rss; break;
1102         case 2: x = -r, y =  0, dx =   0, dy =  rs, ddx =  rss, ddy = -rss; break;
1103         case 3: x =  0, y =  r, dx =  rs, dy =   0, ddx = -rss, ddy = -rss; break;
1104         default: return; // avoid unitialized compiler warnings
1105     }
1106 
1107     for(int i=0; i<steps; i++) {
1108         workBuf[workBufIndex++] = x0 + floor(x);
1109         workBuf[workBufIndex++] = y0 + floor(y);
1110 
1111         x += dx+ddx/2,  y += dy+ddy/2;
1112         dx += ddx,      dy += ddy;
1113     }
1114 
1115     workBuf[workBufIndex++] = x0 + floor(x);
1116     workBuf[workBufIndex++] = y0 + floor(y);
1117 #endif
1118 }
1119 
DrawRoundedRectangle(wxCoord x,wxCoord y,wxCoord w,wxCoord h,wxCoord r)1120 void pi_ocpnDC::DrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord w, wxCoord h, wxCoord r )
1121 {
1122     if( dc )
1123         dc->DrawRoundedRectangle( x, y, w, h, r );
1124 #ifdef ocpnUSE_GL
1125     else {
1126         r++;
1127         int steps = ceil(sqrt((float)r));
1128 
1129         wxCoord x1 = x + r, x2 = x + w - r;
1130         wxCoord y1 = y + r, y2 = y + h - r;
1131 
1132 #ifdef USE_ANDROID_GLES2
1133 
1134         //  Grow the work buffer as necessary
1135         size_t bufReq = steps * 8 * 2 * sizeof(float);      // large, to be sure
1136 
1137         if( workBufSize < bufReq ){
1138             workBuf = (float *)realloc(workBuf, bufReq);
1139             workBufSize = bufReq;
1140         }
1141         workBufIndex = 0;
1142 
1143         drawrrhelperGLES2( x2, y1, r, 0, steps );
1144         drawrrhelperGLES2( x1, y1, r, 1, steps );
1145         drawrrhelperGLES2( x1, y2, r, 2, steps );
1146         drawrrhelperGLES2( x2, y2, r, 3, steps );
1147 
1148         glUseProgram( pi_color_tri_shader_program );
1149 
1150         // Get pointers to the attributes in the program.
1151         GLint mPosAttrib = glGetAttribLocation( pi_color_tri_shader_program, "position" );
1152 
1153         // Disable VBO's (vertex buffer objects) for attributes.
1154         glBindBuffer( GL_ARRAY_BUFFER, 0 );
1155         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
1156 
1157         glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, workBuf );
1158         glEnableVertexAttribArray( mPosAttrib );
1159 
1160 
1161         //  Border color
1162         float bcolorv[4];
1163         bcolorv[0] = m_brush.GetColour().Red() / float(256);
1164         bcolorv[1] = m_brush.GetColour().Green() / float(256);
1165         bcolorv[2] = m_brush.GetColour().Blue() / float(256);
1166         bcolorv[3] = m_brush.GetColour().Alpha() / float(256);
1167 
1168         GLint bcolloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
1169         glUniform4fv(bcolloc, 1, bcolorv);
1170 
1171 
1172         float angle = 0.;
1173         float xoffset = 0;
1174         float yoffset = 0;
1175 
1176         // Rotate
1177         mat4x4 I, Q;
1178         mat4x4_identity(I);
1179         mat4x4_rotate_Z(Q, I, angle);
1180 
1181         // Translate
1182         Q[3][0] = xoffset;
1183         Q[3][1] = yoffset;
1184 
1185         GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"TransformMatrix");
1186         glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
1187 
1188         // Perform the actual drawing.
1189         glDrawArrays(GL_TRIANGLE_FAN, 0, workBufIndex/2);
1190 
1191         // Restore the per-object transform to Identity Matrix
1192         mat4x4 IM;
1193         mat4x4_identity(IM);
1194         GLint matlocf = glGetUniformLocation(pi_color_tri_shader_program,"TransformMatrix");
1195         glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
1196 
1197 
1198 #else
1199         if( ConfigureBrush() ) {
1200             glBegin( GL_TRIANGLE_FAN );
1201             drawrrhelper( x2, y1, r, 0, steps );
1202             drawrrhelper( x1, y1, r, 1, steps );
1203             drawrrhelper( x1, y2, r, 2, steps );
1204             drawrrhelper( x2, y2, r, 3, steps );
1205             glEnd();
1206         }
1207 
1208         if( ConfigurePen() ) {
1209             glBegin( GL_LINE_LOOP );
1210             drawrrhelper( x2, y1, r, 0, steps );
1211             drawrrhelper( x1, y1, r, 1, steps );
1212             drawrrhelper( x1, y2, r, 2, steps );
1213             drawrrhelper( x2, y2, r, 3, steps );
1214             glEnd();
1215         }
1216 #endif
1217     }
1218 #endif
1219 }
1220 
DrawCircle(wxCoord x,wxCoord y,wxCoord radius)1221 void pi_ocpnDC::DrawCircle( wxCoord x, wxCoord y, wxCoord radius )
1222 {
1223 #ifdef USE_ANDROID_GLES2
1224 
1225     //      Enable anti-aliased lines, at best quality
1226     glEnable( GL_BLEND );
1227 
1228     float coords[8];
1229     coords[0] = x - radius;  coords[1] = y + radius;
1230     coords[2] = x + radius;  coords[3] = y + radius;
1231     coords[4] = x - radius;  coords[5] = y - radius;
1232     coords[6] = x + radius;  coords[7] = y - radius;
1233 
1234     glUseProgram( pi_circle_filled_shader_program );
1235 
1236     // Get pointers to the attributes in the program.
1237     GLint mPosAttrib = glGetAttribLocation( pi_circle_filled_shader_program, "aPos" );
1238 
1239         // Disable VBO's (vertex buffer objects) for attributes.
1240     glBindBuffer( GL_ARRAY_BUFFER, 0 );
1241     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
1242 
1243     glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
1244     glEnableVertexAttribArray( mPosAttrib );
1245 
1246     //  Circle radius
1247     GLint radiusloc = glGetUniformLocation(pi_circle_filled_shader_program,"circle_radius");
1248     glUniform1f(radiusloc, radius);
1249 
1250     //  Circle center point
1251     GLint centerloc = glGetUniformLocation(pi_circle_filled_shader_program,"circle_center");
1252     float ctrv[2];
1253     ctrv[0] = x; ctrv[1] = m_vpSize.y - y;
1254     glUniform2fv(centerloc, 1, ctrv);
1255 
1256     //  Circle color
1257     float colorv[4];
1258     colorv[0] = m_brush.GetColour().Red() / float(256);
1259     colorv[1] = m_brush.GetColour().Green() / float(256);
1260     colorv[2] = m_brush.GetColour().Blue() / float(256);
1261     colorv[3] = (m_brush.GetStyle() == wxBRUSHSTYLE_TRANSPARENT) ? 0.0 : 1.0;
1262 
1263     GLint colloc = glGetUniformLocation(pi_circle_filled_shader_program,"circle_color");
1264     glUniform4fv(colloc, 1, colorv);
1265 
1266     //  Border color
1267     float bcolorv[4];
1268     bcolorv[0] = m_pen.GetColour().Red() / float(256);
1269     bcolorv[1] = m_pen.GetColour().Green() / float(256);
1270     bcolorv[2] = m_pen.GetColour().Blue() / float(256);
1271     bcolorv[3] = m_pen.GetColour().Alpha() / float(256);
1272 
1273     GLint bcolloc = glGetUniformLocation(pi_circle_filled_shader_program,"border_color");
1274     glUniform4fv(bcolloc, 1, bcolorv);
1275 
1276     //  Border Width
1277     GLint borderWidthloc = glGetUniformLocation(pi_circle_filled_shader_program,"border_width");
1278     glUniform1f(borderWidthloc, m_pen.GetWidth());
1279 
1280 
1281 //     GLint matloc = glGetUniformLocation(pi_circle_filled_shader_program,"MVMatrix");
1282 //     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)(cc1->GetpVP()->vp_transform) );
1283 
1284         // Perform the actual drawing.
1285     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1286 
1287     //      Enable anti-aliased lines, at best quality
1288     glDisable( GL_BLEND );
1289 
1290 #else
1291     DrawEllipse( x - radius, y - radius, 2 * radius, 2 * radius );
1292 #endif
1293 }
1294 
StrokeCircle(wxCoord x,wxCoord y,wxCoord radius)1295 void pi_ocpnDC::StrokeCircle( wxCoord x, wxCoord y, wxCoord radius )
1296 {
1297 #if wxUSE_GRAPHICS_CONTEXT
1298     if( pgc ) {
1299         wxGraphicsPath gpath = pgc->CreatePath();
1300         gpath.AddCircle( x, y, radius );
1301 
1302         pgc->SetPen( GetPen() );
1303         pgc->SetBrush( GetBrush() );
1304         pgc->DrawPath( gpath );
1305 
1306         // keep dc dirty box up-to-date
1307         dc->CalcBoundingBox( x + radius + 2, y + radius + 2 );
1308         dc->CalcBoundingBox( x - radius - 2, y - radius - 2 );
1309     } else
1310 #endif
1311         DrawCircle( x, y, radius );
1312 }
1313 
DrawEllipse(wxCoord x,wxCoord y,wxCoord width,wxCoord height)1314 void pi_ocpnDC::DrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
1315 {
1316     if( dc )
1317         dc->DrawEllipse( x, y, width, height );
1318 #ifdef ocpnUSE_GL
1319     else {
1320         float r1 = width / 2, r2 = height / 2;
1321         float cx = x + r1, cy = y + r2;
1322 
1323         //      Enable anti-aliased lines, at best quality
1324         glEnable( GL_BLEND );
1325 
1326         /* formula for variable step count to produce smooth ellipse */
1327         float steps = floorf(wxMax(sqrtf(sqrtf((float)(width*width + height*height))), 1) * M_PI);
1328 
1329 #ifndef USE_ANDROID_GLES2
1330         if( ConfigureBrush() ) {
1331             glBegin( GL_TRIANGLE_FAN );
1332             glVertex2f( cx, cy );
1333             for( float a = 0; a <= 2 * M_PI + M_PI/steps; a += 2 * M_PI / steps )
1334                 glVertex2f( cx + r1 * sinf( a ), cy + r2 * cosf( a ) );
1335             glEnd();
1336         }
1337 
1338         if( ConfigurePen() ) {
1339             glBegin( GL_LINE_LOOP );
1340             for( float a = 0; a < 2 * M_PI - M_PI/steps; a += 2 * M_PI / steps )
1341                 glVertex2f( cx + r1 * sinf( a ), cy + r2 * cosf( a ) );
1342             glEnd();
1343         }
1344 #else
1345 #endif
1346         glDisable( GL_BLEND );
1347     }
1348 #endif
1349 }
1350 
DrawPolygon(int n,wxPoint points[],wxCoord xoffset,wxCoord yoffset,float scale,float angle)1351 void pi_ocpnDC::DrawPolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, float scale, float angle )
1352 {
1353     if( dc )
1354         dc->DrawPolygon( n, points, xoffset, yoffset );
1355 #ifdef ocpnUSE_GL
1356     else {
1357 
1358 #ifdef __WXQT__
1359         SetGLAttrs( false );            // Some QT platforms (Android) have trouble with GL_BLEND / GL_LINE_SMOOTH
1360 #else
1361         SetGLAttrs( true );
1362 #endif
1363 
1364 #ifdef USE_ANDROID_GLES2
1365 
1366         glEnable( GL_BLEND );
1367 
1368         if(n > 4)
1369             DrawPolygonTessellated( n, points, xoffset, yoffset);
1370         else{           // n = 3 or 4, most common case for pre-tesselated shapes
1371 
1372 
1373             //  Grow the work buffer as necessary
1374             if( workBufSize < (size_t)n*2 ){
1375                 workBuf = (float *)realloc(workBuf, (n*4) * sizeof(float));
1376                 workBufSize = n*4;
1377             }
1378 
1379             for( int i = 0; i < n; i++ ){
1380                 workBuf[i*2] = (points[i].x * scale); // + xoffset;
1381                 workBuf[i*2 + 1] = (points[i].y * scale); // + yoffset;
1382             }
1383 
1384             glUseProgram( pi_color_tri_shader_program );
1385 
1386             // Get pointers to the attributes in the program.
1387             GLint mPosAttrib = glGetAttribLocation( pi_color_tri_shader_program, "position" );
1388 
1389             // Disable VBO's (vertex buffer objects) for attributes.
1390             glBindBuffer( GL_ARRAY_BUFFER, 0 );
1391             glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
1392 
1393             glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, workBuf );
1394             glEnableVertexAttribArray( mPosAttrib );
1395 
1396 
1397             //  Border color
1398             float bcolorv[4];
1399             bcolorv[0] = m_pen.GetColour().Red() / float(256);
1400             bcolorv[1] = m_pen.GetColour().Green() / float(256);
1401             bcolorv[2] = m_pen.GetColour().Blue() / float(256);
1402             bcolorv[3] = m_pen.GetColour().Alpha() / float(256);
1403 
1404             GLint bcolloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
1405             glUniform4fv(bcolloc, 1, bcolorv);
1406 
1407 
1408             // Rotate
1409             mat4x4 I, Q;
1410             mat4x4_identity(I);
1411             mat4x4_rotate_Z(Q, I, angle);
1412 
1413             // Translate
1414             Q[3][0] = xoffset;
1415             Q[3][1] = yoffset;
1416 
1417             GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"TransformMatrix");
1418             glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
1419 
1420             // Perform the actual drawing.
1421             glDrawArrays(GL_LINE_LOOP, 0, n);
1422 
1423             //  Fill color
1424             bcolorv[0] = m_brush.GetColour().Red() / float(256);
1425             bcolorv[1] = m_brush.GetColour().Green() / float(256);
1426             bcolorv[2] = m_brush.GetColour().Blue() / float(256);
1427             bcolorv[3] = m_brush.GetColour().Alpha() / float(256);
1428 
1429             glUniform4fv(bcolloc, 1, bcolorv);
1430 
1431             // For the simple common case of a convex rectangle...
1432             //  swizzle the array points to enable GL_TRIANGLE_STRIP
1433             if(n == 4){
1434                 float x1 = workBuf[4];
1435                 float y1 = workBuf[5];
1436                 workBuf[4] = workBuf[6];
1437                 workBuf[5] = workBuf[7];
1438                 workBuf[6] = x1;
1439                 workBuf[7] = y1;
1440 
1441                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1442             }
1443             else if(n == 3){
1444                 glDrawArrays(GL_TRIANGLES, 0, 3);
1445             }
1446 
1447             // Restore the per-object transform to Identity Matrix
1448             mat4x4 IM;
1449             mat4x4_identity(IM);
1450             GLint matlocf = glGetUniformLocation(pi_color_tri_shader_program,"TransformMatrix");
1451             glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
1452 
1453         }
1454 
1455 
1456 #else
1457 
1458         if( ConfigureBrush() ) {
1459             glEnable( GL_POLYGON_SMOOTH );
1460             glBegin( GL_POLYGON );
1461             for( int i = 0; i < n; i++ )
1462                 glVertex2f( (points[i].x * scale) + xoffset, (points[i].y * scale) + yoffset );
1463             glEnd();
1464             glDisable( GL_POLYGON_SMOOTH );
1465         }
1466 
1467         if( ConfigurePen() ) {
1468             glEnable( GL_LINE_SMOOTH );
1469             glBegin( GL_LINE_LOOP );
1470             for( int i = 0; i < n; i++ )
1471                 glVertex2f( (points[i].x * scale) + xoffset, (points[i].y * scale) + yoffset );
1472             glEnd();
1473             glDisable( GL_LINE_SMOOTH );
1474         }
1475 #endif
1476 
1477         SetGLAttrs( false );
1478 
1479     }
1480 #endif
1481 }
1482 
1483 #ifdef ocpnUSE_GL
1484 
1485 // GL callbacks
1486 
1487 typedef union {
1488     GLdouble data[6];
1489     struct sGLvertex {
1490         GLdouble x;
1491         GLdouble y;
1492         GLdouble z;
1493         GLdouble r;
1494         GLdouble g;
1495         GLdouble b;
1496     } info;
1497 } GLvertex;
1498 
1499 #ifndef USE_ANDROID_GLES2
1500 
1501 #ifdef __WXMAC__
1502 #ifndef APIENTRY
1503 #define APIENTRY
1504 #endif
1505 #endif
1506 
pi_ocpnDCcombineCallback(GLdouble coords[3],GLdouble * vertex_data[4],GLfloat weight[4],GLdouble ** dataOut)1507 void APIENTRY pi_ocpnDCcombineCallback( GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
1508         GLdouble **dataOut )
1509 {
1510     GLvertex *vertex;
1511 
1512     vertex = new GLvertex();
1513     pi_gTesselatorVertices.Add(vertex );
1514 
1515     vertex->info.x = coords[0];
1516     vertex->info.y = coords[1];
1517     vertex->info.z = coords[2];
1518 
1519     for( int i = 3; i < 6; i++ ) {
1520         vertex->data[i] = weight[0] * vertex_data[0][i] + weight[1] * vertex_data[1][i];
1521     }
1522 
1523     *dataOut = &(vertex->data[0]);
1524 }
1525 
ocpnDCvertexCallback(GLvoid * arg)1526 void APIENTRY ocpnDCvertexCallback( GLvoid* arg )
1527 {
1528     GLvertex* vertex;
1529     vertex = (GLvertex*) arg;
1530     glVertex2f( (float)vertex->info.x, (float)vertex->info.y );
1531 }
1532 
ocpnDCerrorCallback(GLenum errorCode)1533 void APIENTRY ocpnDCerrorCallback( GLenum errorCode )
1534 {
1535    const GLubyte *estring;
1536    estring = gluErrorString(errorCode);
1537    //wxLogMessage( _T("OpenGL Tessellation Error: %s"), (char *)estring );
1538 }
1539 
ocpnDCbeginCallback(GLenum type)1540 void APIENTRY ocpnDCbeginCallback( GLenum type )
1541 {
1542     glBegin( type );
1543 }
1544 
ocpnDCendCallback()1545 void APIENTRY ocpnDCendCallback()
1546 {
1547     glEnd();
1548 }
1549 #endif
1550 
1551 
1552 // GLSL callbacks
1553 
1554 
1555 #ifdef USE_ANDROID_GLES2
1556 
1557 static std::list<double*> odc_combine_work_data;
pi_odc_combineCallbackD(GLdouble coords[3],GLdouble * vertex_data[4],GLfloat weight[4],GLdouble ** dataOut,void * data)1558 static void pi_odc_combineCallbackD(GLdouble coords[3],
1559                              GLdouble *vertex_data[4],
1560                              GLfloat weight[4], GLdouble **dataOut, void *data )
1561 {
1562 //     double *vertex = new double[3];
1563 //     odc_combine_work_data.push_back(vertex);
1564 //     memcpy(vertex, coords, 3*(sizeof *coords));
1565 //     *dataOut = vertex;
1566 }
1567 
pi_odc_vertexCallbackD_GLSL(GLvoid * vertex,void * data)1568 void pi_odc_vertexCallbackD_GLSL(GLvoid *vertex, void *data)
1569 {
1570     pi_ocpnDC* pDC = (pi_ocpnDC*)data;
1571 
1572     // Grow the work buffer if necessary
1573     if(pDC->s_odc_tess_vertex_idx > pDC->s_odc_tess_buf_len - 8)
1574     {
1575         int new_buf_len = pDC->s_odc_tess_buf_len + 100;
1576         GLfloat * tmp = pDC->s_odc_tess_work_buf;
1577 
1578         pDC->s_odc_tess_work_buf = (GLfloat *)realloc(pDC->s_odc_tess_work_buf, new_buf_len * sizeof(GLfloat));
1579         if (NULL == pDC->s_odc_tess_work_buf)
1580         {
1581             free(tmp);
1582             tmp = NULL;
1583         }
1584         else
1585             pDC->s_odc_tess_buf_len = new_buf_len;
1586     }
1587 
1588     GLdouble *pointer = (GLdouble *) vertex;
1589 
1590     pDC->s_odc_tess_work_buf[pDC->s_odc_tess_vertex_idx++] = (float)pointer[0];
1591     pDC->s_odc_tess_work_buf[pDC->s_odc_tess_vertex_idx++] = (float)pointer[1];
1592 
1593     pDC->s_odc_nvertex++;
1594 }
1595 
pi_odc_beginCallbackD_GLSL(GLenum mode,void * data)1596 void pi_odc_beginCallbackD_GLSL( GLenum mode, void *data)
1597 {
1598     pi_ocpnDC* pDC = (pi_ocpnDC*)data;
1599     pDC->s_odc_tess_vertex_idx_this =  pDC->s_odc_tess_vertex_idx;
1600     pDC->s_odc_tess_mode = mode;
1601     pDC->s_odc_nvertex = 0;
1602 }
1603 
pi_odc_endCallbackD_GLSL(void * data)1604 void pi_odc_endCallbackD_GLSL(void *data)
1605 {
1606     //qDebug() << "End" << s_odc_nvertex << s_odc_tess_buf_len << s_odc_tess_vertex_idx << s_odc_tess_vertex_idx_this;
1607     //End 5 100 10 0
1608 #if 1
1609     pi_ocpnDC* pDC = (pi_ocpnDC*)data;
1610 
1611     glUseProgram(pi_color_tri_shader_program);
1612 
1613     // Disable VBO's (vertex buffer objects) for attributes.
1614     glBindBuffer( GL_ARRAY_BUFFER, 0 );
1615     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
1616 
1617     float *bufPt = &(pDC->s_odc_tess_work_buf[pDC->s_odc_tess_vertex_idx_this]);
1618     GLint pos = glGetAttribLocation(pi_color_tri_shader_program, "position");
1619     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufPt);
1620     glEnableVertexAttribArray(pos);
1621 
1622     ///GLint matloc = glGetUniformLocation(pi_color_tri_shader_program,"MVMatrix");
1623     ///glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)s_tessVP.vp_transform);
1624 
1625     float colorv[4];
1626     wxColour c = pDC->GetBrush().GetColour();
1627 
1628     colorv[0] = c.Red() / float(256);
1629     colorv[1] = c.Green() / float(256);
1630     colorv[2] = c.Blue() / float(256);
1631     colorv[3] = c.Alpha() / float(256);
1632 
1633     GLint colloc = glGetUniformLocation(pi_color_tri_shader_program,"color");
1634     glUniform4fv(colloc, 1, colorv);
1635 
1636     glDrawArrays(pDC->s_odc_tess_mode, 0, pDC->s_odc_nvertex);
1637 #endif
1638 }
1639 #endif
1640 
1641 
1642 #endif          //#ifdef ocpnUSE_GL
1643 
DrawPolygonTessellated(int n,wxPoint points[],wxCoord xoffset,wxCoord yoffset)1644 void pi_ocpnDC::DrawPolygonTessellated( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset )
1645 {
1646     if( dc )
1647         dc->DrawPolygon( n, points, xoffset, yoffset );
1648 #ifdef ocpnUSE_GL
1649     else {
1650 #if !defined(ocpnUSE_GLES) || defined(USE_ANDROID_GLES2) // tessalator in glues is broken
1651         if( n < 5 )
1652 # endif
1653         {
1654             DrawPolygon( n, points, xoffset, yoffset );
1655             return;
1656         }
1657 
1658 
1659 
1660 #ifdef USE_ANDROID_GLES2
1661         m_tobj = gluNewTess();
1662         s_odc_tess_vertex_idx = 0;
1663 
1664         gluTessCallback( m_tobj, GLU_TESS_VERTEX_DATA, (_GLUfuncptr) &pi_odc_vertexCallbackD_GLSL  );
1665         gluTessCallback( m_tobj, GLU_TESS_BEGIN_DATA, (_GLUfuncptr) &pi_odc_beginCallbackD_GLSL  );
1666         gluTessCallback( m_tobj, GLU_TESS_END_DATA, (_GLUfuncptr) &pi_odc_endCallbackD_GLSL  );
1667         gluTessCallback( m_tobj, GLU_TESS_COMBINE_DATA, (_GLUfuncptr) &pi_odc_combineCallbackD );
1668         //s_tessVP = vp;
1669 
1670         gluTessNormal( m_tobj, 0, 0, 1);
1671         gluTessProperty( m_tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
1672 
1673         if( ConfigureBrush() ) {
1674             gluTessBeginPolygon( m_tobj, this );
1675             gluTessBeginContour( m_tobj );
1676 
1677             for( int i = 0; i < n; i++ ) {
1678                 double *p = new double[6];
1679                 p[0] = points[i].x, p[1] = points[i].y, p[2] = 0;
1680                 gluTessVertex(m_tobj, p, p);
1681                 //odc_combine_work_data.push_back(p);
1682 
1683             }
1684             gluTessEndContour( m_tobj );
1685             gluTessEndPolygon( m_tobj );
1686         }
1687 
1688         gluDeleteTess(m_tobj);
1689 
1690 
1691 //         for(std::list<double*>::iterator i = odc_combine_work_data.begin(); i!=odc_combine_work_data.end(); i++)
1692 //             delete [] *i;
1693 //         odc_combine_work_data.clear();
1694 
1695     }
1696 #else
1697         static GLUtesselator *tobj = NULL;
1698         if( ! tobj ) tobj = gluNewTess();
1699 
1700         gluTessCallback( tobj, GLU_TESS_VERTEX, (_GLUfuncptr) &ocpnDCvertexCallback );
1701         gluTessCallback( tobj, GLU_TESS_BEGIN, (_GLUfuncptr) &ocpnDCbeginCallback );
1702         gluTessCallback( tobj, GLU_TESS_END, (_GLUfuncptr) &ocpnDCendCallback );
1703         gluTessCallback( tobj, GLU_TESS_COMBINE, (_GLUfuncptr) &pi_ocpnDCcombineCallback );
1704         gluTessCallback( tobj, GLU_TESS_ERROR, (_GLUfuncptr) &ocpnDCerrorCallback );
1705 
1706         gluTessNormal( tobj, 0, 0, 1);
1707         gluTessProperty( tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
1708 
1709         if( ConfigureBrush() ) {
1710             gluTessBeginPolygon( tobj, NULL );
1711             gluTessBeginContour( tobj );
1712 
1713             for( int i = 0; i < n; i++ ) {
1714                 GLvertex* vertex = new GLvertex();
1715                 pi_gTesselatorVertices.Add( vertex );
1716                 vertex->info.x = (GLdouble) points[i].x;
1717                 vertex->info.y = (GLdouble) points[i].y;
1718                 vertex->info.z = (GLdouble) 0.0;
1719                 vertex->info.r = (GLdouble) 0.0;
1720                 vertex->info.g = (GLdouble) 0.0;
1721                 vertex->info.b = (GLdouble) 0.0;
1722                 gluTessVertex( tobj, (GLdouble*)vertex, (GLdouble*)vertex );
1723             }
1724             gluTessEndContour( tobj );
1725             gluTessEndPolygon( tobj );
1726         }
1727 
1728         for( unsigned int i=0; i<pi_gTesselatorVertices.Count(); i++ )
1729             delete (GLvertex*)pi_gTesselatorVertices.Item(i);
1730         pi_gTesselatorVertices.Clear();
1731 
1732         gluDeleteTess(tobj);
1733 
1734     }
1735 #endif
1736 #endif
1737 }
1738 
StrokePolygon(int n,wxPoint points[],wxCoord xoffset,wxCoord yoffset,float scale)1739 void pi_ocpnDC::StrokePolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, float scale )
1740 {
1741 #if wxUSE_GRAPHICS_CONTEXT
1742     if( pgc ) {
1743         wxGraphicsPath gpath = pgc->CreatePath();
1744         gpath.MoveToPoint( points[0].x + xoffset, points[0].y + yoffset );
1745         for( int i = 1; i < n; i++ )
1746             gpath.AddLineToPoint( points[i].x + xoffset, points[i].y + yoffset );
1747         gpath.AddLineToPoint( points[0].x + xoffset, points[0].y + yoffset );
1748 
1749         pgc->SetPen( GetPen() );
1750         pgc->SetBrush( GetBrush() );
1751         pgc->DrawPath( gpath );
1752 
1753         for( int i = 0; i < n; i++ )
1754             dc->CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset );
1755     } else
1756 #endif
1757         DrawPolygon( n, points, xoffset, yoffset, scale );
1758 }
1759 
DrawBitmap(const wxBitmap & bitmap,wxCoord x,wxCoord y,bool usemask)1760 void pi_ocpnDC::DrawBitmap( const wxBitmap &bitmap, wxCoord x, wxCoord y, bool usemask )
1761 {
1762     wxBitmap bmp;
1763     if( x < 0 || y < 0 ) {
1764         int dx = ( x < 0 ? -x : 0 );
1765         int dy = ( y < 0 ? -y : 0 );
1766         int w = bitmap.GetWidth() - dx;
1767         int h = bitmap.GetHeight() - dy;
1768         /* picture is out of viewport */
1769         if( w <= 0 || h <= 0 ) return;
1770         wxBitmap newBitmap = bitmap.GetSubBitmap( wxRect( dx, dy, w, h ) );
1771         x += dx;
1772         y += dy;
1773         bmp = newBitmap;
1774     } else {
1775         bmp = bitmap;
1776     }
1777     if( dc )
1778         dc->DrawBitmap( bmp, x, y, usemask );
1779 #ifdef ocpnUSE_GL
1780     else {
1781 #ifdef ocpnUSE_GLES  // Do not attempt to do anything with glDrawPixels if using opengles
1782         return; // this should not be hit anymore ever anyway
1783 #endif
1784 
1785 #ifndef USE_ANDROID_GLES2
1786         wxImage image = bmp.ConvertToImage();
1787         int w = image.GetWidth(), h = image.GetHeight();
1788 
1789         if( usemask ) {
1790             unsigned char *d = image.GetData();
1791             unsigned char *a = image.GetAlpha();
1792 
1793             unsigned char mr, mg, mb;
1794             if( !image.GetOrFindMaskColour( &mr, &mg, &mb ) && !a ){
1795                 printf("trying to use mask to draw a bitmap without alpha or mask\n" );
1796             }
1797 
1798 #ifdef __WXOSX__
1799             if(image.HasMask())
1800                 a=0;
1801 #endif
1802 
1803             unsigned char *e = new unsigned char[4 * w * h];
1804             if(e && d){
1805                 for( int y = 0; y < h; y++ )
1806                     for( int x = 0; x < w; x++ ) {
1807                         unsigned char r, g, b;
1808                         int off = ( y * image.GetWidth() + x );
1809                         r = d[off * 3 + 0];
1810                         g = d[off * 3 + 1];
1811                         b = d[off * 3 + 2];
1812 
1813                         e[off * 4 + 0] = r;
1814                         e[off * 4 + 1] = g;
1815                         e[off * 4 + 2] = b;
1816 
1817                         e[off * 4 + 3] =
1818                                 a ? a[off] : ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
1819 //                        e[off * 4 + 3] = ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
1820                     }
1821             }
1822 
1823             glColor4f( 1, 1, 1, 1 );
1824             GLDrawBlendData( x, y, w, h, GL_RGBA, e );
1825             delete[] ( e );
1826         } else {
1827             glRasterPos2i( x, y );
1828             glPixelZoom( 1, -1 ); /* draw data from top to bottom */
1829             if(image.GetData())
1830                 glDrawPixels( w, h, GL_RGB, GL_UNSIGNED_BYTE, image.GetData() );
1831             glPixelZoom( 1, 1 );
1832         }
1833 #endif          // GLES2
1834     }
1835 
1836 #endif
1837 }
1838 
DrawText(const wxString & text,wxCoord x,wxCoord y)1839 void pi_ocpnDC::DrawText( const wxString &text, wxCoord x, wxCoord y )
1840 {
1841     if( dc )
1842         dc->DrawText( text, x, y );
1843 #ifdef ocpnUSE_GL
1844     else {
1845        wxCoord w = 0;
1846         wxCoord h = 0;
1847 
1848         if(m_buseTex){
1849 
1850             m_texfont.Build( m_font );      // make sure the font is ready
1851             m_texfont.GetTextExtent(text, &w, &h);
1852 
1853             if( w && h ) {
1854 
1855                 glEnable( GL_BLEND );
1856                 glEnable( GL_TEXTURE_2D );
1857                 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1858 
1859 #ifndef USE_ANDROID_GLES2
1860                 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
1861                 glPushMatrix();
1862                 glTranslatef(x, y, 0);
1863 
1864                 glColor3ub( m_textforegroundcolour.Red(), m_textforegroundcolour.Green(),
1865                             m_textforegroundcolour.Blue() );
1866 
1867 
1868                 m_texfont.RenderString(text);
1869                 glPopMatrix();
1870 #else
1871                 m_texfont.RenderString(text, x, y);
1872 #endif
1873                 glDisable( GL_TEXTURE_2D );
1874                 glDisable( GL_BLEND );
1875 
1876             }
1877         }
1878         else{
1879             wxScreenDC sdc;
1880             sdc.SetFont(m_font);
1881             sdc.GetTextExtent(text, &w, &h, NULL, NULL, &m_font);
1882 
1883             /* create bitmap of appropriate size and select it */
1884             wxBitmap bmp( w, h );
1885             wxMemoryDC temp_dc;
1886             temp_dc.SelectObject( bmp );
1887 
1888             /* fill bitmap with black */
1889             temp_dc.SetBackground( wxBrush( wxColour( 0, 0, 0 ) ) );
1890             temp_dc.Clear();
1891 
1892             /* draw the text white */
1893             temp_dc.SetFont( m_font );
1894             temp_dc.SetTextForeground( wxColour( 255, 255, 255 ) );
1895             temp_dc.DrawText( text, 0, 0 );
1896             temp_dc.SelectObject( wxNullBitmap );
1897 
1898             /* use the data in the bitmap for alpha channel,
1899              and set the color to text foreground */
1900             wxImage image = bmp.ConvertToImage();
1901             if( x < 0 || y < 0 ) { // Allow Drawing text which is offset to start off screen
1902                 int dx = ( x < 0 ? -x : 0 );
1903                 int dy = ( y < 0 ? -y : 0 );
1904                 w = bmp.GetWidth() - dx;
1905                 h = bmp.GetHeight() - dy;
1906                 /* picture is out of viewport */
1907                 if( w <= 0 || h <= 0 ) return;
1908                 image = image.GetSubImage( wxRect( dx, dy, w, h ) );
1909                 x += dx;
1910                 y += dy;
1911             }
1912 
1913             unsigned char *data = new unsigned char[w * h * 4];
1914             unsigned char *im = image.GetData();
1915 
1916 
1917             if(im){
1918                 unsigned int r = m_textforegroundcolour.Red();
1919                 unsigned int g = m_textforegroundcolour.Green();
1920                 unsigned int b = m_textforegroundcolour.Blue();
1921                 for( int i = 0; i < h; i++ ){
1922                     for(int j=0 ; j < w ; j++){
1923                         unsigned int index = ((i*w) + j) * 4;
1924                         data[index] = r;
1925                         data[index+1] = g;
1926                         data[index+2] = b;
1927                         data[index+3] = im[((i*w) + j) * 3];
1928                     }
1929                 }
1930             }
1931 #if 0
1932             glColor4ub( 255, 255, 255, 255 );
1933             glEnable( GL_BLEND );
1934             glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1935             glRasterPos2i( x, y );
1936             glPixelZoom( 1, -1 );
1937             glDrawPixels( w, h, GL_RGBA, GL_UNSIGNED_BYTE, data );
1938             glPixelZoom( 1, 1 );
1939             glDisable( GL_BLEND );
1940 #else
1941             unsigned int texobj;
1942 
1943             glGenTextures(1, &texobj);
1944             glBindTexture(GL_TEXTURE_2D, texobj);
1945 
1946             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1947             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
1948 
1949             int TextureWidth = NextPow2(w);
1950             int TextureHeight = NextPow2(h);
1951             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TextureWidth, TextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1952             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
1953 
1954             glEnable(GL_TEXTURE_2D);
1955             glEnable(GL_BLEND);
1956             glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1957 
1958 #ifndef USE_ANDROID_GLES2
1959             glColor3ub(0,0,0);
1960 
1961             float u = (float)w/TextureWidth, v = (float)h/TextureHeight;
1962             glBegin(GL_QUADS);
1963             glTexCoord2f(0, 0); glVertex2f(x, y);
1964             glTexCoord2f(u, 0); glVertex2f(x+w, y);
1965             glTexCoord2f(u, v); glVertex2f(x+w, y+h);
1966             glTexCoord2f(0, v); glVertex2f(x, y+h);
1967             glEnd();
1968 #else
1969 #endif
1970             glDisable(GL_BLEND);
1971             glDisable(GL_TEXTURE_2D);
1972 
1973             glDeleteTextures(1, &texobj);
1974 #endif
1975             delete[] data;
1976         }
1977     }
1978 #endif
1979 }
1980 
GetTextExtent(const wxString & string,wxCoord * w,wxCoord * h,wxCoord * descent,wxCoord * externalLeading,wxFont * font)1981 void pi_ocpnDC::GetTextExtent( const wxString &string, wxCoord *w, wxCoord *h, wxCoord *descent,
1982         wxCoord *externalLeading, wxFont *font )
1983 {
1984     //  Give at least reasonable results on failure.
1985     if(w) *w = 100;
1986     if(h) *h = 100;
1987 
1988     if( dc ) dc->GetTextExtent( string, w, h, descent, externalLeading, font );
1989     else {
1990         wxFont f = m_font;
1991         if( font ) f = *font;
1992 
1993         if(m_buseTex){
1994   #ifdef ocpnUSE_GL
1995         m_texfont.Build( f );      // make sure the font is ready
1996         m_texfont.GetTextExtent(string, w, h);
1997   #else
1998         wxMemoryDC temp_dc;
1999         temp_dc.GetTextExtent( string, w, h, descent, externalLeading, &f );
2000   #endif
2001         }
2002         else{
2003             wxMemoryDC temp_dc;
2004             temp_dc.GetTextExtent( string, w, h, descent, externalLeading, &f );
2005         }
2006 
2007      }
2008 
2009      //  Sometimes GetTextExtent returns really wrong, uninitialized results.
2010      //  Dunno why....
2011      if( w && (*w > 2000) ) *w = 2000;
2012      if( h && (*h > 500) ) *h = 500;
2013 }
2014 
ResetBoundingBox()2015 void pi_ocpnDC::ResetBoundingBox()
2016 {
2017     if( dc ) dc->ResetBoundingBox();
2018 }
2019 
CalcBoundingBox(wxCoord x,wxCoord y)2020 void pi_ocpnDC::CalcBoundingBox( wxCoord x, wxCoord y )
2021 {
2022     if( dc ) dc->CalcBoundingBox( x, y );
2023 }
2024 
ConfigurePen()2025 bool pi_ocpnDC::ConfigurePen()
2026 {
2027     if( !m_pen.IsOk() ) return false;
2028     if( m_pen == *wxTRANSPARENT_PEN ) return false;
2029 
2030     wxColour c = m_pen.GetColour();
2031     int width = m_pen.GetWidth();
2032 #ifdef ocpnUSE_GL
2033 #ifndef USE_ANDROID_GLES2
2034     glColor4ub( c.Red(), c.Green(), c.Blue(), c.Alpha() );
2035     glLineWidth( wxMax(g_piGLMinSymbolLineWidth, width) );
2036 #endif
2037 #endif
2038     return true;
2039 }
2040 
ConfigureBrush()2041 bool pi_ocpnDC::ConfigureBrush()
2042 {
2043     if( m_brush == wxNullBrush || m_brush.GetStyle() == wxBRUSHSTYLE_TRANSPARENT )
2044         return false;
2045 #ifdef ocpnUSE_GL
2046 #ifndef USE_ANDROID_GLES2
2047     wxColour c = m_brush.GetColour();
2048     glColor4ub( c.Red(), c.Green(), c.Blue(), c.Alpha() );
2049 #endif
2050 #endif
2051     return true;
2052 }
2053 
GLDrawBlendData(wxCoord x,wxCoord y,wxCoord w,wxCoord h,int format,const unsigned char * data)2054 void pi_ocpnDC::GLDrawBlendData( wxCoord x, wxCoord y, wxCoord w, wxCoord h, int format,
2055         const unsigned char *data )
2056 {
2057 #ifdef ocpnUSE_GL
2058 #ifndef USE_ANDROID_GLES2
2059     glEnable( GL_BLEND );
2060     glRasterPos2i( x, y );
2061     glPixelZoom( 1, -1 );
2062     glDrawPixels( w, h, format, GL_UNSIGNED_BYTE, data );
2063     glPixelZoom( 1, 1 );
2064     glDisable( GL_BLEND );
2065 #endif
2066 #endif
2067 }
2068