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