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