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