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