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