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