OpenCPN Partial API docs
Loading...
Searching...
No Matches
compass.cpp
1/******************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: OpenCPN Main wxWidgets Program
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
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#include <wx/wxprec.h>
28#ifndef WX_PRECOMP
29#include <wx/wx.h>
30#endif // precompiled headers
31#include "config.h"
32#include "ocpn_types.h"
33#include "compass.h"
34#include "chcanv.h"
35#include "styles.h"
36
37#include "glChartCanvas.h"
38#include "ocpn_frame.h" // FIXME (dave) colorschemes
39
40extern ocpnStyle::StyleManager* g_StyleManager;
41extern bool bGPSValid;
42extern bool g_bSatValid;
43extern int g_SatsInView;
44extern bool g_bopengl;
45
46#ifndef GL_RGBA8
47#define GL_RGBA8 0x8058
48#endif
49
50ocpnCompass::ocpnCompass(ChartCanvas* parent, bool bShowGPS) {
51 m_parent = parent;
52 m_bshowGPS = bShowGPS;
53
54 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
55 _img_compass = style->GetIcon(_T("CompassRose"));
56 _img_gpsRed = style->GetIcon(_T("gpsRed"));
57
58 m_rose_angle = -999; // force a refresh when first used
59
60 m_pStatBoxToolStaticBmp = NULL;
61
62 m_rect =
63 wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(),
64 _img_compass.GetWidth() + _img_gpsRed.GetWidth() +
65 style->GetCompassLeftMargin() * 2 + style->GetToolSeparation(),
66 _img_compass.GetHeight() + style->GetCompassTopMargin() +
67 style->GetCompassBottomMargin());
68
69#ifdef ocpnUSE_GL
70 m_texobj = 0;
71#endif
72 m_texOK = false;
73
74 m_scale = 1.0;
75 m_cs = GLOBAL_COLOR_SCHEME_RGB;
76}
77
78ocpnCompass::~ocpnCompass() {
79#ifdef ocpnUSE_GL
80 if (m_texobj) {
81 glDeleteTextures(1, &m_texobj);
82 m_texobj = 0;
83 }
84#endif
85
86 delete m_pStatBoxToolStaticBmp;
87}
88
89void ocpnCompass::Paint(ocpnDC& dc) {
90 if (m_shown && m_StatBmp.IsOk()) {
91#if defined(ocpnUSE_GLES) || defined(ocpnUSE_GL)
92 if (!m_texobj){
93 // The glContext is known active here,
94 // so safe to create a texture.
95 glGenTextures(1, &m_texobj);
96 CreateTexture();
97 }
98
99 if (g_bopengl && m_texobj /*&& m_texOK*/) {
100 glBindTexture(GL_TEXTURE_2D, m_texobj);
101 glEnable(GL_TEXTURE_2D);
102
103#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
104 float coords[8];
105 float uv[8];
106
107 // normal uv, normalized to POT
108 uv[0] = 0;
109 uv[1] = 0;
110 uv[2] = (float)m_image_width / m_tex_w;
111 uv[3] = 0;
112 uv[4] = (float)m_image_width / m_tex_w;
113 uv[5] = (float)m_image_height / m_tex_h;
114 uv[6] = 0;
115 uv[7] = (float)m_image_height / m_tex_h;
116
117 // pixels
118 coords[0] = m_rect.x;
119 coords[1] = m_rect.y;
120 coords[2] = m_rect.x + m_rect.width;
121 coords[3] = m_rect.y;
122 coords[4] = m_rect.x + m_rect.width;
123 coords[5] = m_rect.y + m_rect.height;
124 coords[6] = m_rect.x;
125 coords[7] = m_rect.y + m_rect.height;
126
127 m_parent->GetglCanvas()->RenderTextures(dc, coords, uv, 4,
128 m_parent->GetpVP());
129#else
130
131 glBegin(GL_QUADS);
132
133 glTexCoord2f(0, 0);
134 glVertex2i(m_rect.x, m_rect.y);
135 glTexCoord2f((float)m_image_width / m_tex_w, 0);
136 glVertex2i(m_rect.x + m_rect.width, m_rect.y);
137 glTexCoord2f((float)m_image_width / m_tex_w,
138 (float)m_image_height / m_tex_h);
139 glVertex2i(m_rect.x + m_rect.width, m_rect.y + m_rect.height);
140 glTexCoord2f(0, (float)m_image_height / m_tex_h);
141 glVertex2i(m_rect.x, m_rect.y + m_rect.height);
142
143 glEnd();
144#endif
145
146 glDisable(GL_TEXTURE_2D);
147
148 } else {
149#ifdef __WXOSX__
150 // Support MacBook Retina display
151 if(g_bopengl){
152 double scale = m_parent->GetContentScaleFactor();
153 if(scale > 1){
154 wxImage image = m_StatBmp.ConvertToImage();
155 image.Rescale( image.GetWidth() * scale, image.GetHeight() * scale);
156 wxBitmap bmp( image );
157 dc.DrawBitmap(bmp, m_rect.x, m_rect.y, true);
158 }
159 else
160 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
161 }
162 else
163 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
164#else
165 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
166#endif
167 }
168
169#else
170 dc.DrawBitmap(m_StatBmp, m_rect.x, m_rect.y, true);
171#endif
172 }
173}
174
175bool ocpnCompass::MouseEvent(wxMouseEvent& event) {
176 if (!m_shown || !m_rect.Contains(event.GetPosition())) return false;
177
178 if (event.LeftDown()) {
179 if (m_parent->GetUpMode() == NORTH_UP_MODE)
180 m_parent->SetUpMode(COURSE_UP_MODE);
181 else if (m_parent->GetUpMode() == COURSE_UP_MODE)
182 m_parent->SetUpMode(HEAD_UP_MODE);
183 else
184 m_parent->SetUpMode(NORTH_UP_MODE);
185 }
186
187 return true;
188}
189
190void ocpnCompass::SetColorScheme(ColorScheme cs) {
191 m_cs = cs;
192 UpdateStatus(true);
193}
194
195void ocpnCompass::UpdateStatus(bool bnew) {
196 if (bnew)
197 m_lastgpsIconName.Clear(); // force an update to occur
198
199 CreateBmp(bnew);
200
201#ifdef ocpnUSE_GL
202 if (g_bopengl && m_texobj)
203 CreateTexture();
204#endif
205
206}
207
208void ocpnCompass::SetScaleFactor(float factor) {
209 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
210
211 if (factor > 0.1)
212 m_scale = factor;
213 else
214 m_scale = 1.0;
215
216 // Precalculate the background sizes to get m_rect width/height
217 wxBitmap compassBg, gpsBg;
218 int orient = style->GetOrientation();
219 style->SetOrientation(wxTB_HORIZONTAL);
220 if (style->HasBackground()) {
221 compassBg = style->GetNormalBG();
222 style->DrawToolbarLineStart(compassBg);
223 compassBg = style->SetBitmapBrightness(compassBg, m_cs);
224 gpsBg = style->GetNormalBG();
225 style->DrawToolbarLineEnd(gpsBg);
226 gpsBg = style->SetBitmapBrightness(gpsBg, m_cs);
227 }
228
229 if (fabs(m_scale - 1.0) > 0.1) {
230 wxImage bg_img = compassBg.ConvertToImage();
231 bg_img.Rescale(compassBg.GetWidth() * m_scale,
232 compassBg.GetHeight() * m_scale, wxIMAGE_QUALITY_NORMAL);
233 compassBg = wxBitmap(bg_img);
234
235 bg_img = gpsBg.ConvertToImage();
236 bg_img.Rescale(gpsBg.GetWidth() * m_scale, gpsBg.GetHeight() * m_scale,
237 wxIMAGE_QUALITY_NORMAL);
238 gpsBg = wxBitmap(bg_img);
239 }
240
241 int width =
242 compassBg.GetWidth() + gpsBg.GetWidth() + style->GetCompassLeftMargin();
243 if (!style->marginsInvisible)
244 width += style->GetCompassLeftMargin() + style->GetToolSeparation();
245
246 m_rect = wxRect(style->GetCompassXOffset(), style->GetCompassYOffset(), width,
247 compassBg.GetHeight() + style->GetCompassTopMargin() +
248 style->GetCompassBottomMargin());
249}
250
251void ocpnCompass::CreateBmp(bool newColorScheme) {
252 //if (!m_shown) return;
253
254 wxString gpsIconName;
255 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
256
257 // In order to draw a horizontal compass window when the toolbar is vertical,
258 // we need to save away the sizes and backgrounds for the two icons.
259
260 static wxBitmap compassBg, gpsBg;
261 static wxSize toolsize;
262 static int topmargin, leftmargin, radius;
263
264 if (!compassBg.IsOk() || newColorScheme) {
265 int orient = style->GetOrientation();
266 style->SetOrientation(wxTB_HORIZONTAL);
267 if (style->HasBackground()) {
268 compassBg = style->GetNormalBG();
269 style->DrawToolbarLineStart(compassBg);
270 compassBg = style->SetBitmapBrightness(compassBg, m_cs);
271 gpsBg = style->GetNormalBG();
272 style->DrawToolbarLineEnd(gpsBg);
273 gpsBg = style->SetBitmapBrightness(gpsBg, m_cs);
274 }
275
276 if (fabs(m_scale - 1.0) > 0.1) {
277 wxImage bg_img = compassBg.ConvertToImage();
278 bg_img.Rescale(compassBg.GetWidth() * m_scale,
279 compassBg.GetHeight() * m_scale, wxIMAGE_QUALITY_NORMAL);
280 compassBg = wxBitmap(bg_img);
281
282 bg_img = gpsBg.ConvertToImage();
283 bg_img.Rescale(gpsBg.GetWidth() * m_scale, gpsBg.GetHeight() * m_scale,
284 wxIMAGE_QUALITY_NORMAL);
285 gpsBg = wxBitmap(bg_img);
286 }
287
288 leftmargin = style->GetCompassLeftMargin();
289 topmargin = style->GetCompassTopMargin();
290 radius = style->GetCompassCornerRadius();
291
292 if (orient == wxTB_VERTICAL) style->SetOrientation(wxTB_VERTICAL);
293 }
294
295 bool b_need_refresh = false;
296
297 if (bGPSValid) {
298 if (g_bSatValid) {
299 gpsIconName = _T("gps3Bar");
300 if (g_SatsInView <= 8) gpsIconName = _T("gps2Bar");
301 if (g_SatsInView <= 4) gpsIconName = _T("gps1Bar");
302 if (g_SatsInView < 0) gpsIconName = _T("gpsGry");
303
304 } else
305 gpsIconName = _T("gpsGrn");
306 } else
307 gpsIconName = _T("gpsRed");
308
309 if (m_lastgpsIconName != gpsIconName) b_need_refresh = true;
310
311 double rose_angle = -999.;
312
313 if ((fabs(m_parent->GetVPRotation()) > .01) ||
314 (fabs(m_parent->GetVPSkew()) > .01)) {
315 rose_angle = -m_parent->GetVPRotation();
316 } else
317 rose_angle = 0.;
318
319 if (fabs(m_rose_angle - rose_angle) > .1) b_need_refresh = true;
320
321 //if (!b_need_refresh) return;
322
323 int width = compassBg.GetWidth();
324 if (m_bshowGPS) width += gpsBg.GetWidth() + leftmargin;
325
326 if (!style->marginsInvisible)
327 width += leftmargin + style->GetToolSeparation();
328
329 m_StatBmp.Create(width, compassBg.GetHeight() + topmargin +
330 style->GetCompassBottomMargin());
331
332 m_rect.width = m_StatBmp.GetWidth();
333 m_rect.height = m_StatBmp.GetHeight();
334
335 //if (!m_StatBmp.IsOk()) return;
336
337 m_MaskBmp = wxBitmap(m_StatBmp.GetWidth(), m_StatBmp.GetHeight());
338 if (style->marginsInvisible) {
339 wxMemoryDC sdc(m_MaskBmp);
340 sdc.SetBackground(*wxWHITE_BRUSH);
341 sdc.Clear();
342 sdc.SetBrush(*wxBLACK_BRUSH);
343 sdc.SetPen(*wxBLACK_PEN);
344 wxSize maskSize = wxSize(m_MaskBmp.GetWidth() - leftmargin,
345 m_MaskBmp.GetHeight() - (2 * topmargin));
346 sdc.DrawRoundedRectangle(wxPoint(leftmargin, topmargin), maskSize, radius);
347 sdc.SelectObject(wxNullBitmap);
348 } else if (radius) {
349 wxMemoryDC sdc(m_MaskBmp);
350 sdc.SetBackground(*wxWHITE_BRUSH);
351 sdc.Clear();
352 sdc.SetBrush(*wxBLACK_BRUSH);
353 sdc.SetPen(*wxBLACK_PEN);
354 sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), m_MaskBmp.GetHeight(),
355 radius);
356 sdc.SelectObject(wxNullBitmap);
357 }
358#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
359 m_StatBmp.SetMask(new wxMask(m_MaskBmp, *wxWHITE));
360#endif
361
362 wxMemoryDC mdc;
363 mdc.SelectObject(m_StatBmp);
364 mdc.SetBackground(wxBrush(GetGlobalColor(_T("COMP1")), wxBRUSHSTYLE_SOLID));
365 mdc.Clear();
366
367 mdc.SetPen(wxPen(GetGlobalColor(_T("UITX1")), 1));
368 mdc.SetBrush(wxBrush(GetGlobalColor(_T("UITX1")), wxBRUSHSTYLE_TRANSPARENT));
369
370 if (!style->marginsInvisible)
371 mdc.DrawRoundedRectangle(0, 0, m_StatBmp.GetWidth(), m_StatBmp.GetHeight(),
372 radius);
373
374 wxPoint offset(leftmargin, topmargin);
375
376 // Build Compass Rose, rotated...
377 wxBitmap BMPRose;
378 wxPoint after_rotate;
379
380 int cwidth = style->GetToolSize().x * m_scale;
381 int cheight = style->GetToolSize().y * m_scale;
382 cheight = wxMin(cheight, compassBg.GetHeight());
383 cwidth = wxMin(cwidth, cheight);
384 cheight = cwidth;
385
386 if (m_parent->GetUpMode() == COURSE_UP_MODE)
387 BMPRose = style->GetIcon(_T("CompassRose"), cwidth, cheight);
388 else if (m_parent->GetUpMode() == HEAD_UP_MODE)
389 BMPRose = style->GetIcon(_T("CompassRoseMag"), cwidth, cheight);
390 else
391 BMPRose = style->GetIcon(_T("CompassRoseBlue"), cwidth, cheight);
392 if ((fabs(m_parent->GetVPRotation()) > .01) ||
393 (fabs(m_parent->GetVPSkew()) > .01)) {
394 wxImage rose_img = BMPRose.ConvertToImage();
395 wxPoint rot_ctr(cwidth / 2, cheight / 2);
396 wxImage rot_image =
397 rose_img.Rotate(rose_angle, rot_ctr, true, &after_rotate);
398 BMPRose = wxBitmap(rot_image).GetSubBitmap(
399 wxRect(-after_rotate.x, -after_rotate.y, cwidth, cheight));
400 }
401
402 wxBitmap iconBm;
403
404 if (style->HasBackground()) {
405 iconBm = MergeBitmaps(compassBg, BMPRose, wxSize(0, 0));
406 } else {
407 iconBm = BMPRose;
408 }
409
410 iconBm = ConvertTo24Bit(wxColor(0, 0, 0), iconBm);
411
412 mdc.DrawBitmap(iconBm, offset);
413 offset.x += iconBm.GetWidth();
414 offset.x += style->GetToolSeparation();
415
416 m_rose_angle = rose_angle;
417
418 if (m_bshowGPS) {
419 // GPS Icon
420 int twidth = style->GetToolSize().x * m_scale;
421 int theight = style->GetToolSize().y * m_scale;
422 theight = wxMin(cheight, compassBg.GetHeight());
423 int swidth = wxMax(twidth, theight);
424 int sheight = wxMin(twidth, theight);
425
426 // Sometimes, the SVG renderer gets the size wrong due to some internal
427 // rounding error. If so found, it seems to work OK by just reducing the
428 // requested size by one pixel....
429 wxBitmap gicon = style->GetIcon(gpsIconName, swidth, sheight);
430 if (gicon.GetHeight() != sheight)
431 gicon = style->GetIcon(gpsIconName, swidth - 1, sheight - 1, true);
432
433 if (style->HasBackground()) {
434 iconBm = MergeBitmaps(gpsBg, gicon, wxSize(0, 0));
435 } else {
436 iconBm = gicon;
437 }
438
439 iconBm = ConvertTo24Bit(wxColor(0, 0, 0), iconBm);
440 mdc.DrawBitmap(iconBm, offset);
441 mdc.SelectObject(wxNullBitmap);
442
443 m_lastgpsIconName = gpsIconName;
444 }
445}
446
447void ocpnCompass::CreateTexture() {
448#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
449 // GLES does not do ocpnDC::DrawBitmap(), so use
450 // texture
451 if (g_bopengl) {
452 wxImage image = m_StatBmp.ConvertToImage();
453 unsigned char* imgdata = image.GetData();
454 unsigned char* imgalpha = image.GetAlpha();
455 m_tex_w = image.GetWidth();
456 m_tex_h = image.GetHeight();
457 m_image_width = m_tex_w;
458 m_image_height = m_tex_h;
459
460 // Make it POT
461 int width_pot = m_tex_w;
462 int height_pot = m_tex_h;
463
464 int xp = image.GetWidth();
465 if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
466 width_pot = xp;
467 else {
468 int a = 0;
469 while (xp) {
470 xp = xp >> 1;
471 a++;
472 }
473 width_pot = 1 << a;
474 }
475
476 xp = image.GetHeight();
477 if (((xp != 0) && !(xp & (xp - 1))))
478 height_pot = xp;
479 else {
480 int a = 0;
481 while (xp) {
482 xp = xp >> 1;
483 a++;
484 }
485 height_pot = 1 << a;
486 }
487
488 m_tex_w = width_pot;
489 m_tex_h = height_pot;
490
491 GLuint format = GL_RGBA;
492 GLuint internalformat = GL_RGBA8; //format;
493 int stride = 4;
494
495 if (imgdata) {
496 unsigned char* teximage =
497 (unsigned char*)malloc(stride * m_tex_w * m_tex_h);
498
499 for (int i = 0; i < m_image_height; i++) {
500 for (int j = 0; j < m_image_width; j++) {
501 int s = (i * 3 * m_image_width) + (j * 3);
502 int d = (i * stride * m_tex_w) + (j * stride);
503
504 teximage[d + 0] = imgdata[s + 0];
505 teximage[d + 1] = imgdata[s + 1];
506 teximage[d + 2] = imgdata[s + 2];
507 teximage[d + 3] = 255;
508 }
509 }
510
511 glBindTexture(GL_TEXTURE_2D, m_texobj);
512
513 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
514 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
515 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
516 GL_NEAREST); // No mipmapping
517 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
518
519 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0,
520 format, GL_UNSIGNED_BYTE, teximage);
521
522 free(teximage);
523 glBindTexture(GL_TEXTURE_2D, 0);
524 m_texOK = true;
525 }
526 }
527#endif
528}
529
530void ocpnCompass::UpdateTexture() {
531#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
532 // GLES does not do ocpnDC::DrawBitmap(), so use
533 // texture
534 if (g_bopengl) {
535 wxImage image = m_StatBmp.ConvertToImage();
536 unsigned char* imgdata = image.GetData();
537 unsigned char* imgalpha = image.GetAlpha();
538 m_tex_w = image.GetWidth();
539 m_tex_h = image.GetHeight();
540 m_image_width = m_tex_w;
541 m_image_height = m_tex_h;
542
543 // Make it POT
544 int width_pot = m_tex_w;
545 int height_pot = m_tex_h;
546
547 int xp = image.GetWidth();
548 if (((xp != 0) && !(xp & (xp - 1)))) // detect POT
549 width_pot = xp;
550 else {
551 int a = 0;
552 while (xp) {
553 xp = xp >> 1;
554 a++;
555 }
556 width_pot = 1 << a;
557 }
558
559 xp = image.GetHeight();
560 if (((xp != 0) && !(xp & (xp - 1))))
561 height_pot = xp;
562 else {
563 int a = 0;
564 while (xp) {
565 xp = xp >> 1;
566 a++;
567 }
568 height_pot = 1 << a;
569 }
570
571 m_tex_w = width_pot;
572 m_tex_h = height_pot;
573
574 GLuint format = GL_RGBA;
575 GLuint internalformat = GL_RGBA8; //format;
576 int stride = 4;
577
578 if (imgdata) {
579 unsigned char* teximage =
580 (unsigned char*)malloc(stride * m_tex_w * m_tex_h);
581
582 for (int i = 0; i < m_image_height; i++) {
583 for (int j = 0; j < m_image_width; j++) {
584 int s = (i * 3 * m_image_width) + (j * 3);
585 int d = (i * stride * m_tex_w) + (j * stride);
586
587 teximage[d + 0] = imgdata[s + 0];
588 teximage[d + 1] = imgdata[s + 1];
589 teximage[d + 2] = imgdata[s + 2];
590 teximage[d + 3] = 255;
591 }
592 }
593
594 glBindTexture(GL_TEXTURE_2D, m_texobj);
595 glEnable(GL_TEXTURE_2D);
596
597 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_w, m_tex_h, format, GL_UNSIGNED_BYTE, teximage);
598
599 free(teximage);
600 glBindTexture(GL_TEXTURE_2D, 0);
601 }
602 }
603#endif
604}
Definition: ocpndc.h:55
Definition: Quilt.cpp:864