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