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