OpenCPN Partial API docs
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 "model/ocpn_types.h"
33 #include "model/own_ship.h"
34 #include "compass.h"
35 #include "chcanv.h"
36 #include "styles.h"
37 
38 #include "glChartCanvas.h"
39 #include "ocpn_frame.h" // FIXME (dave) colorschemes
40 
41 extern ocpnStyle::StyleManager* g_StyleManager;
42 extern bool g_bSatValid;
43 extern int g_SatsInView;
44 extern bool g_bopengl;
45 
46 #ifndef GL_RGBA8
47 #define GL_RGBA8 0x8058
48 #endif
49 
50 ocpnCompass::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 
78 ocpnCompass::~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 
89 void ocpnCompass::Paint(ocpnDC& dc) {
90  if (m_shown && m_StatBmp.IsOk()) {
91 #if defined(ocpnUSE_GLES) || defined(ocpnUSE_GL)
92  if (g_bopengl && !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 
175 bool 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 
190 void ocpnCompass::SetColorScheme(ColorScheme cs) {
191  m_cs = cs;
192  UpdateStatus(true);
193 }
194 
195 void 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 
208 void 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 
251 void 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 
447 void 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 
530 void 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:58
Definition: Quilt.cpp:867