diff --git a/CMakeLists.txt b/CMakeLists.txt index 69984c84..75d33400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,7 @@ SET(SRC_OESENC src/tinyxmlerror.cpp src/tinyxmlparser.cpp src/razdsparser.cpp + src/pi_DepthFont.cpp ) SET(SRC_CPL diff --git a/src/ocpn_plugin.h b/src/ocpn_plugin.h index 5fc16481..fe4deb89 100644 --- a/src/ocpn_plugin.h +++ b/src/ocpn_plugin.h @@ -27,22 +27,38 @@ #define _PLUGIN_H_ #ifndef DECL_EXP -#ifdef __WXMSW__ +#if defined(__WXMSW__) || defined(__CYGWIN__) # define DECL_EXP __declspec(dllexport) +#elif defined __GNUC__ && __GNUC__ >= 4 +# define DECL_EXP __attribute__((visibility("default"))) +#elif defined __WXOSX__ +# define DECL_EXP __attribute__((visibility("default"))) #else -# ifdef __GNUC__ -# define DECL_EXP __attribute__((visibility("default"))) -# endif +# define DECL_EXP #endif #endif +#ifdef __WXMSW__ +#ifdef MAKING_PLUGIN +# define DECL_IMP __declspec(dllimport) +#endif +#endif #include +#include +#include +#include +#include +#include + #ifdef ocpnUSE_SVG -#include "wxsvg/include/wxSVG/svg.h" +#include #endif // ocpnUSE_SVG +#include +#include + class wxGLContext; // This is the most modern API Version number @@ -50,7 +66,7 @@ class wxGLContext; // PlugIns conforming to API Version less then the most modern will also // be correctly supported. #define API_VERSION_MAJOR 1 -#define API_VERSION_MINOR 13 +#define API_VERSION_MINOR 17 // Fwd Definitions class wxFileConfig; @@ -58,6 +74,7 @@ class wxNotebook; class wxFont; class wxAuiManager; class wxScrolledWindow; +class wxGLCanvas; //--------------------------------------------------------------------------------------------------------- // @@ -147,6 +164,16 @@ class PlugIn_Position_Fix_Ex int nSats; }; +class Plugin_Active_Leg_Info +{ +public: + double Xte; // Left side of the track -> negative XTE + double Btw; + double Dtw; + wxString wp_name; // Name of destination waypoint for active leg + bool arrival; // True when within arrival circle +}; + // Describe AIS Alarm state enum plugin_ais_alarm_type { @@ -461,7 +488,6 @@ class DECL_EXP opencpn_plugin_18 : public opencpn_plugin virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); - virtual void SetPluginMessage(wxString &message_id, wxString &message_body); virtual void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix); @@ -515,6 +541,54 @@ class DECL_EXP opencpn_plugin_113 : public opencpn_plugin_112 virtual void OnToolbarToolUpCallback(int id); }; +class DECL_EXP opencpn_plugin_114 : public opencpn_plugin_113 +{ +public: + opencpn_plugin_114(void *pmgr); + virtual ~opencpn_plugin_114(); + +}; + +class DECL_EXP opencpn_plugin_115 : public opencpn_plugin_114 +{ +public: + opencpn_plugin_115(void *pmgr); + virtual ~opencpn_plugin_115(); +}; + +class DECL_EXP opencpn_plugin_116 : public opencpn_plugin_115 +{ +public: + opencpn_plugin_116(void *pmgr); + virtual ~opencpn_plugin_116(); + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, int canvasIndex); + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvasIndex); + virtual void PrepareContextMenu( int canvasIndex); + +}; + +class DECL_EXP opencpn_plugin_117 : public opencpn_plugin_116 +{ +public: + opencpn_plugin_117(void *pmgr); + /* + * Forms a semantic version together with GetPlugInVersionMajor() and + * GetPlugInVersionMinor(), see https://semver.org/ + */ + virtual int GetPlugInVersionPatch(); + + /** Post-release version part, extends the semver spec. */ + virtual int GetPlugInVersionPost(); + + /** Pre-release tag version part, see GetPlugInVersionPatch() */ + virtual const char* GetPlugInVersionPre(); + + /** Build version part see GetPlugInVersionPatch(). */ + virtual const char* GetPlugInVersionBuild(); + + /*Provide active leg data to plugins*/ + virtual void SetActiveLegInfo(Plugin_Active_Leg_Info &leg_info); +}; //------------------------------------------------------------------ // Route and Waypoint PlugIn support // @@ -700,6 +774,10 @@ extern DECL_EXP wxString getUsrDistanceUnit_Plugin( int unit = -1 ); extern DECL_EXP wxString getUsrSpeedUnit_Plugin( int unit = -1 ); extern DECL_EXP wxString GetNewGUID(); extern "C" DECL_EXP bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, double lat2, double lon2); +/** + * Start playing a sound file asynchronously. Supported formats depends + * on sound backend. + */ extern DECL_EXP void PlugInPlaySound( wxString &sound_file ); @@ -1050,13 +1128,20 @@ extern DECL_EXP wxColour GetFontColour_PlugIn(wxString TextElement); extern DECL_EXP double GetCanvasTilt(); extern DECL_EXP void SetCanvasTilt(double tilt); +/** + * Start playing a sound file asynchronously. Supported formats depends + * on sound backend. The deviceIx is only used on platforms using the + * portaudio sound backend where -1 indicates the default device. + */ extern DECL_EXP bool PlugInPlaySoundEx( wxString &sound_file, int deviceIndex=-1 ); extern DECL_EXP void AddChartDirectory( wxString &path ); extern DECL_EXP void ForceChartDBUpdate(); +extern DECL_EXP void ForceChartDBRebuild(); extern DECL_EXP wxString GetWritableDocumentsDir( void ); extern DECL_EXP wxDialog *GetActiveOptionsDialog(); extern DECL_EXP wxArrayString GetWaypointGUIDArray( void ); +extern DECL_EXP wxArrayString GetIconNameArray(void); extern DECL_EXP bool AddPersistentFontKey(wxString TextElement); extern DECL_EXP wxString GetActiveStyleName(); @@ -1127,7 +1212,7 @@ enum OCPN_DLDialogStyle /* Synchronous (Blocking) download of a single file */ -extern DECL_EXP _OCPN_DLStatus OCPN_downloadFile( const wxString& url, const wxString &outputFile, +extern DECL_EXP _OCPN_DLStatus OCPN_downloadFile( const wxString& url, const wxString &outputFile, const wxString &title, const wxString &message, const wxBitmap& bitmap, wxWindow *parent, long style, int timeout_secs); @@ -1196,9 +1281,106 @@ class DECL_EXP OCPN_downloadEvent: public wxEvent bool m_b_complete; }; -//DECLARE_EVENT_TYPE(wxEVT_DOWNLOAD_EVENT, -1) -//extern const wxEventType DECL_EXP wxEVT_DOWNLOAD_EVENT; -extern WXDLLIMPEXP_CORE const wxEventType wxEVT_DOWNLOAD_EVENT; +//extern WXDLLIMPEXP_CORE const wxEventType wxEVT_DOWNLOAD_EVENT; + +#ifdef MAKING_PLUGIN +extern DECL_IMP wxEventType wxEVT_DOWNLOAD_EVENT; +#else +extern DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT; +#endif + + +/* API 1.14 */ +/* API 1.14 adds some more common functions to avoid unnecessary code duplication */ + +bool LaunchDefaultBrowser_Plugin( wxString url ); + + +// API 1.14 Extra canvas Support + +/* Allow drawing of objects onto other OpenGL canvases */ +extern DECL_EXP void PlugInAISDrawGL( wxGLCanvas* glcanvas, const PlugIn_ViewPort& vp ); +extern DECL_EXP bool PlugInSetFontColor(const wxString TextElement, const wxColour color); + +// API 1.15 +extern DECL_EXP double PlugInGetDisplaySizeMM(); + +// +extern DECL_EXP wxFont* FindOrCreateFont_PlugIn( int point_size, wxFontFamily family, + wxFontStyle style, wxFontWeight weight, bool underline = false, + const wxString &facename = wxEmptyString, + wxFontEncoding encoding = wxFONTENCODING_DEFAULT ); + +extern DECL_EXP int PlugInGetMinAvailableGshhgQuality(); +extern DECL_EXP int PlugInGetMaxAvailableGshhgQuality(); + +extern DECL_EXP void PlugInHandleAutopilotRoute(bool enable); + +// API 1.16 +// +/** + * Return the plugin data directory for a given directory name. + * + * On Linux, the returned data path is an existing directory ending in + * "opencpn/plugins/" where the last part is the plugin_name + * argument. The prefix part is one of the directories listed in the + * environment variable XDG_DATA_DIRS, by default + * ~/.local/share:/usr/local/share:/usr/share. + * + * On other platforms, the returned value is GetSharedDataDir() + + * "/opencpn/plugins/" + plugin_name (with native path separators) + * if that path exists. + * + * Return "" if no existing directory is found. + */ +extern DECL_EXP wxString GetPluginDataDir(const char* plugin_name); + +extern DECL_EXP bool ShuttingDown( void ); + +// Support for MUI MultiCanvas model + +extern DECL_EXP wxWindow* PluginGetFocusCanvas(); +extern DECL_EXP wxWindow* PluginGetOverlayRenderCanvas(); + +extern "C" DECL_EXP void CanvasJumpToPosition( wxWindow *canvas, double lat, double lon, double scale); +extern "C" DECL_EXP int AddCanvasMenuItem(wxMenuItem *pitem, opencpn_plugin *pplugin, const char *name = ""); +extern "C" DECL_EXP void RemoveCanvasMenuItem(int item, const char *name = ""); // Fully remove this item +extern "C" DECL_EXP void SetCanvasMenuItemViz(int item, bool viz, const char *name = ""); // Temporarily change context menu options +extern "C" DECL_EXP void SetCanvasMenuItemGrey(int item, bool grey, const char *name = ""); + +// Extract waypoints, routes and tracks +extern DECL_EXP wxString GetSelectedWaypointGUID_Plugin( ); +extern DECL_EXP wxString GetSelectedRouteGUID_Plugin( ); +extern DECL_EXP wxString GetSelectedTrackGUID_Plugin( ); + +extern DECL_EXP std::unique_ptr GetWaypoint_Plugin( const wxString& ); // doublon with GetSingleWaypoint +extern DECL_EXP std::unique_ptr GetRoute_Plugin( const wxString& ); +extern DECL_EXP std::unique_ptr GetTrack_Plugin( const wxString& ); + +extern DECL_EXP wxWindow* GetCanvasUnderMouse( ); +extern DECL_EXP int GetCanvasIndexUnderMouse( ); +//extern DECL_EXP std::vector GetCanvasArray(); +extern DECL_EXP wxWindow *GetCanvasByIndex( int canvasIndex ); +extern DECL_EXP int GetCanvasCount( ); +extern DECL_EXP bool CheckMUIEdgePan_PlugIn( int x, int y, bool dragging, int margin, int delta, int canvasIndex ); +extern DECL_EXP void SetMUICursor_PlugIn( wxCursor *pCursor, int canvasIndex ); + +// API 1.17 +// +extern DECL_EXP wxRect GetMasterToolbarRect(); + +enum SDDMFORMAT +{ + DEGREES_DECIMAL_MINUTES = 0, + DECIMAL_DEGREES, + DEGREES_MINUTES_SECONDS, + END_SDDMFORMATS +}; + +extern DECL_EXP int GetLatLonFormat(void); + +// API 1.17 +extern "C" DECL_EXP void ZeroXTE(); #endif //_PLUGIN_H_ diff --git a/src/pi_DepthFont.cpp b/src/pi_DepthFont.cpp new file mode 100644 index 00000000..e82e75ab --- /dev/null +++ b/src/pi_DepthFont.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: texture OpenGL text rendering built from wxFont + * Author: Sean D'Epagnier + * + *************************************************************************** + * Copyright (C) 2014 Sean D'Epagnier * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#include + +#include "pi_DepthFont.h" + +#ifdef USE_ANDROID_GLES2 +#include +#include "linmath.h" +#include "shaders.h" +#else + #include + #include +#endif + +#if 0 +#define TXF_CACHE 8 +static TexFontCache s_txf[TXF_CACHE]; + +TexFont *GetTexFont(wxFont *pFont) +{ + // rebuild font if needed + TexFont *f_cache; + unsigned int i; + for (i = 0; i < TXF_CACHE && s_txf[i].key != nullptr; i++) + { + if (s_txf[i].key == pFont) { + return &s_txf[i].cache; + } + } + if (i == TXF_CACHE) { + i = rand() & (TXF_CACHE -1); + } + s_txf[i].key = pFont; + f_cache = &s_txf[i].cache; + f_cache->Build(*pFont); + return f_cache; +} +#endif + +DepthFont::DepthFont( ) +{ + texobj = 0; + m_built = false; + m_scaleFactor = 0; +} + +DepthFont::~DepthFont( ) +{ + Delete( ); +} + + +void DepthFont::Build( wxFont *font, double scale ) +{ + /* avoid rebuilding if the parameters are the same */ + if(m_built && (*font == m_font) ) + return; + + m_font = *font; + m_scaleFactor = scale; + + m_maxglyphw = 0; + m_maxglyphh = 0; + + wxScreenDC sdc; + + sdc.SetFont( *font ); + + for( int i=0 ; i < 10 ; i++){ + wxCoord gw, gh; + wxString text; + text = wxString::Format(_T("%d"), i); + wxCoord descent, exlead; + sdc.GetTextExtent( text, &gw, &gh, &descent, &exlead, font ); // measure the text + + tgi[i].width = gw; + tgi[i].height = gh - descent; + + tgi[i].advance = gw; + + + m_maxglyphw = wxMax(tgi[i].width, m_maxglyphw); + m_maxglyphh = wxMax(tgi[i].height, m_maxglyphh); + } + + int w = 10 * m_maxglyphw; + int h = m_maxglyphh; + + /* make power of 2 */ + + for(tex_w = 1; tex_w < w; tex_w *= 2); + for(tex_h = 1; tex_h < h; tex_h *= 2); + + wxBitmap tbmp(tex_w, tex_h); + wxMemoryDC dc; + dc.SelectObject(tbmp); + dc.SetFont( *font ); + + /* fill bitmap with black */ + dc.SetBackground( wxBrush( wxColour( 0, 0, 0 ) ) ); + dc.Clear(); + + /* draw the text white */ + dc.SetTextForeground( wxColour( 255, 255, 255 ) ); + + /* wxPen pen(wxColour( 255, 255, 255 )); + wxBrush brush(wxColour( 255, 255, 255 ), wxTRANSPARENT); + dc.SetPen(pen); + dc.SetBrush(brush); + */ + int row = 0, col = 0; + for( int i = 0; i < 10; i++ ) { + + tgi[i].x = col * m_maxglyphw; + tgi[i].y = row * m_maxglyphh; + + wxString text; + text = wxString::Format(_T("%d"), i); + + dc.DrawText(text, tgi[i].x, tgi[i].y ); + + col++; + } + + dc.SelectObject(wxNullBitmap); + + wxImage image = tbmp.ConvertToImage(); + + GLuint format, internalformat; + int stride; + + format = GL_ALPHA; + internalformat = format; + stride = 1; + + unsigned char *imgdata = image.GetData(); + + if(imgdata){ + unsigned char *teximage = (unsigned char *) malloc( stride * tex_w * tex_h ); + + for( int j = 0; j < tex_w*tex_h; j++ ) + for( int k = 0; k < stride; k++ ) + teximage[j * stride + k] = imgdata[3*j]; + + glGenTextures( 1, &texobj ); + glBindTexture( GL_TEXTURE_2D, texobj ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST/*GL_LINEAR*/ ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, internalformat, tex_w, tex_h, 0, + format, GL_UNSIGNED_BYTE, teximage ); + + free(teximage); + } + + m_built = true; +} + +void DepthFont::Delete( ) +{ + if (texobj) { + glDeleteTextures(1, &texobj); + texobj = 0; + } + m_built = false; + m_scaleFactor = 0; +} + +bool DepthFont::GetGLTextureRect(wxRect &texrect, int symIndex) +{ + if(symIndex < 10){ + texrect.x = tgi[symIndex].x; + texrect.y = tgi[symIndex].y; + texrect.width = tgi[symIndex].width; + texrect.height = tgi[symIndex].height; + return true; + } + else{ + texrect.x = tgi[0].x; + texrect.y = tgi[0].y; + texrect.width = tgi[0].width; + texrect.height = tgi[0].height; + return false; + } +} + +#if 0 +void DepthFont::RenderGlyph( int c ) +{ + + SoundTexGlyphInfo &tgic = tgi[c]; + + int x = tgic.x, y = tgic.y; + float w = m_maxglyphw, h = m_maxglyphh; + float tx1 = (float)x / (float)tex_w; + float tx2 = (float)(x + w) / (float)tex_w; + float ty1 = (float)y / (float)tex_h; + float ty2 = (float)(y + h) / (float)tex_h; + +#ifndef USE_ANDROID_GLES2 + + glBegin( GL_QUADS ); + + glTexCoord2f( tx1, ty1 ); glVertex2i( 0, 0 ); + glTexCoord2f( tx2, ty1 ); glVertex2i( w, 0 ); + glTexCoord2f( tx2, ty2 ); glVertex2i( w, h ); + glTexCoord2f( tx1, ty2 ); glVertex2i( 0, h ); + + glEnd(); + glTranslatef( tgic.advance, 0.0, 0.0 ); +#else + + float uv[8]; + float coords[8]; + + //normal uv + uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1; + uv[4] = tx2; uv[5] = ty2; uv[6] = tx1; uv[7] = ty2; + + // pixels + coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0; + coords[4] = w; coords[5] = h; coords[6] = 0; coords[7] = h; + + glUseProgram( texture_2DA_shader_program ); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = glGetAttribLocation( texture_2DA_shader_program, "aPos" ); + GLint mUvAttrib = glGetAttribLocation( texture_2DA_shader_program, "aUV" ); + + // Set up the texture sampler to texture unit 0 + GLint texUni = glGetUniformLocation( texture_2DA_shader_program, "uTex" ); + glUniform1i( texUni, 0 ); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); + + // Set the attribute mPosAttrib with the vertices in the screen coordinates... + glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords ); + // ... and enable it. + glEnableVertexAttribArray( mPosAttrib ); + + // Set the attribute mUvAttrib with the vertices in the GL coordinates... + glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv ); + // ... and enable it. + glEnableVertexAttribArray( mUvAttrib ); + + float colorv[4]; + colorv[0] = m_color.Red() / float(256); + colorv[1] = m_color.Green() / float(256); + colorv[2] = m_color.Blue() / float(256); + colorv[3] = 0; + + GLint colloc = glGetUniformLocation(texture_2DA_shader_program,"color"); + glUniform4fv(colloc, 1, colorv); + + // Rotate + float angle = 0; + mat4x4 I, Q; + mat4x4_identity(I); + mat4x4_rotate_Z(Q, I, angle); + + // Translate + Q[3][0] = m_dx; + Q[3][1] = m_dy; + + + //mat4x4 X; + //mat4x4_mul(X, (float (*)[4])vp->vp_transform, Q); + + GLint matloc = glGetUniformLocation(texture_2DA_shader_program,"TransformMatrix"); + glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q); + + // Select the active texture unit. + glActiveTexture( GL_TEXTURE0 ); + + // For some reason, glDrawElements is busted on Android + // So we do this a hard ugly way, drawing two triangles... + #if 0 + GLushort indices1[] = {0,1,3,2}; + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1); + #else + + float co1[8]; + co1[0] = coords[0]; + co1[1] = coords[1]; + co1[2] = coords[2]; + co1[3] = coords[3]; + co1[4] = coords[6]; + co1[5] = coords[7]; + co1[6] = coords[4]; + co1[7] = coords[5]; + + float tco1[8]; + tco1[0] = uv[0]; + tco1[1] = uv[1]; + tco1[2] = uv[2]; + tco1[3] = uv[3]; + tco1[4] = uv[6]; + tco1[5] = uv[7]; + tco1[6] = uv[4]; + tco1[7] = uv[5]; + + glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 ); + glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 ); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + #endif + + + + + m_dx += tgic.advance; + +#endif +} +#endif + +#if 0 +void DepthFont::RenderString( const char *string, int x, int y ) +{ + +#ifndef USE_ANDROID_GLES2 + + glPushMatrix(); + glTranslatef(x, y, 0); + + glPushMatrix(); + glBindTexture( GL_TEXTURE_2D, texobj); + + for( int i = 0; string[i]; i++ ) { + if(string[i] == '\n') { + glPopMatrix(); + glTranslatef(0, tgi[(int)'A'].height, 0); + glPushMatrix(); + continue; + } + /* degree symbol */ + if((unsigned char)string[i] == 0xc2 && + (unsigned char)string[i+1] == 0xb0) { + RenderGlyph( DEGREE_GLYPH ); + i++; + continue; + } + RenderGlyph( string[i] ); + } + + glPopMatrix(); + glPopMatrix(); +#else + m_dx = x; + m_dy = y; + + glBindTexture( GL_TEXTURE_2D, texobj); + + for( int i = 0; string[i]; i++ ) { + if(string[i] == '\n') { + m_dy += tgi[(int)'A'].height; + continue; + } + /* degree symbol */ + if((unsigned char)string[i] == 0xc2 && + (unsigned char)string[i+1] == 0xb0) { + RenderGlyph( DEGREE_GLYPH ); + i++; + continue; + } + RenderGlyph( string[i] ); + } + +#endif +} + +void DepthFont::RenderString( const wxString &string, int x, int y ) +{ + RenderString((const char*)string.ToUTF8(), x, y); +} +#endif +//#endif //#ifdef ocpnUSE_GL diff --git a/src/pi_DepthFont.h b/src/pi_DepthFont.h new file mode 100644 index 00000000..97e15d0e --- /dev/null +++ b/src/pi_DepthFont.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: OpenGL text rendering + * Author: David Register, Sean D'Epagnier + * + *************************************************************************** + * Copyright (C) 2020 David Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef __DEPTHFONT_H__ +#define __DEPTHFONT_H__ + + +#define SOUND_MAX_GLYPH 50 + + +struct SoundTexGlyphInfo { + int x, y, width, height; + float advance; +}; + +class DepthFont { +public: + DepthFont(); + ~DepthFont(); + + void Build( wxFont *font, double scale ); + void Delete(); + + bool IsBuilt(){ return m_built; } + unsigned int GetTexture(){ return texobj; } + bool GetGLTextureRect(wxRect &texrect, int symIndex); + wxSize GLTextureSize(){ return wxSize(tex_w, tex_h); }; + double GetScale(){ return m_scaleFactor; } +private: + + wxFont m_font; + + SoundTexGlyphInfo tgi[SOUND_MAX_GLYPH]; + + unsigned int texobj; + int tex_w, tex_h; + int m_maxglyphw; + int m_maxglyphh; + bool m_built; + + float m_dx; + float m_dy; + double m_scaleFactor; +}; + + +#endif //guard \ No newline at end of file diff --git a/src/s52plib.cpp b/src/s52plib.cpp index 327a3722..72059b9e 100644 --- a/src/s52plib.cpp +++ b/src/s52plib.cpp @@ -3572,6 +3572,344 @@ int s52plib::RenderSY( ObjRazRules *rzRules, Rules *rules, ViewPort *vp ) } +bool s52plib::RenderSoundingSymbol( ObjRazRules *rzRules, Rule *prule, wxPoint &r, ViewPort *vp, + wxColor symColor, float rot_angle ) +{ + double scale_factor = 1.0; + + scale_factor *= g_ChartScaleFactorExp; + scale_factor *= g_scaminScale; + + if(m_display_size_mm < 200){ //about 8 inches, implying some sort of smaller mobile device + // Set the onscreen size of the symbol + // Compensate for various display resolutions + // Develop empirically, making a buoy about 4 mm tall + double boyHeight = 21. / GetPPMM(); // from raster symbol definitions, boylat is xx pix high + + double targetHeight0 = 4.0; + + // But we want to scale the size for smaller displays + double displaySize = m_display_size_mm; + displaySize = wxMax(displaySize, 100); + + float targetHeight = wxMin(targetHeight0, displaySize / 30); + + double pix_factor = targetHeight / boyHeight; + + //qDebug() << "scaleing" << m_display_size_mm << targetHeight0 << targetHeight << GetPPMM() << boyHeight << pix_factor; + + // for Hubert, and my moto + //scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082 + // My nvidia tab + //scaleing 144.78 144 4 4 12.6667 1.65789 2.4127 + // judgement: all OK + + + scale_factor *= pix_factor; + } + + // calculate the required point size to give 2.5 mm height + int point_size = 6; + bool not_done = true; + wxScreenDC sdc; + int charWidth, charHeight, charDescent; + while((point_size < 20) && not_done){ + wxFont *tentativeFont = FindOrCreateFont_PlugIn( point_size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ); + sdc.GetTextExtent( _T("0"), &charWidth, &charHeight, &charDescent, NULL, tentativeFont ); // measure the text + double font_size_mm = (double)(charHeight- charDescent) / GetPPMM(); + + if(font_size_mm >= (3.2 * scale_factor)){ + not_done = false; + break; + } + point_size++; + } + + // Build the texDepth object, if required + if(!m_pdc){ // OpenGL + if(!m_texSoundings.IsBuilt() || (fabs(m_texSoundings.GetScale() - scale_factor) > 0.1)){ + m_texSoundings.Delete(); + + m_soundFont = FindOrCreateFont_PlugIn( point_size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ); + m_texSoundings.Build(m_soundFont, scale_factor); //texSounding owns the font + } + } + else{ + m_soundFont = FindOrCreateFont_PlugIn( point_size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ); + m_pdc->SetFont(*m_soundFont); + charHeight -= charDescent; + } + + int pivot_x; + int pivot_y; + + // Parse the symbol name + + // The digit + char symDigit = prule->name.SYNM[7]; + int symIndex = symDigit - 0x30; + + // The pivot point offset group + char symCPivot = prule->name.SYNM[6]; + int symPivot = symCPivot - 0x30; + + int pivotWidth, pivotHeight; + // For opengl, the symbols are loaded in a texture + unsigned int texture = 0; + wxRect texrect; + if(!m_pdc) { // GL + texture = m_texSoundings.GetTexture(); + m_texSoundings.GetGLTextureRect(texrect, symIndex); + + if(texture) { + prule->parm2 = texrect.width; + prule->parm3 = texrect.height; + } + + pivotWidth = texrect.width; + pivotHeight = texrect.height; + + } + else{ + pivotWidth = charWidth; + pivotHeight = charHeight; + } + + if(symPivot < 4){ + pivot_x = (pivotWidth * symPivot) - (pivotWidth / 4); + pivot_y = pivotHeight * 3 / 4; + } + else if(symPivot == 4){ + pivot_x = -pivotWidth - (pivotWidth / 4); + pivot_y = pivotHeight * 3 / 4; + } + else{ + pivot_x = - (pivotWidth / 4); + pivot_y = pivotHeight / 3; + } + + +/* + } + else{ // DC + if(symPivot < 4){ + pivot_x = charWidth * symPivot; + pivot_y = charHeight / 2; + } + else if(symPivot == 4){ + pivot_x = -charWidth; + pivot_y = charHeight / 2; + } + else{ + pivot_x = 0; + pivot_y = charHeight / 8; + } + } +*/ + + // Get the bounding box for the to-be-drawn symbol + int b_width, b_height; + b_width = prule->parm2; + b_height = prule->parm3; + + LLBBox symbox; + double latmin, lonmin, latmax, lonmax; + + if( !m_pdc && fabs( vp->rotation ) > .01) // opengl + { + float cx = vp->pix_width/2.; + float cy = vp->pix_height/2.; + float c = cosf(vp->rotation ); + float s = sinf(vp->rotation ); + float x = r.x - pivot_x -cx; + float y = r.y - pivot_y + b_height -cy; + GetPixPointSingle( x*c - y*s +cx, x*s + y*c +cy, &latmin, &lonmin, vp ); + + x = r.x - pivot_x + b_width -cx; + y = r.y - pivot_y -cy; + GetPixPointSingle( x*c - y*s +cx, x*s + y*c +cy, &latmax, &lonmax, vp ); + } else { + GetPixPointSingle( r.x - pivot_x, r.y - pivot_y + b_height, &latmin, &lonmin, vp ); + GetPixPointSingle( r.x - pivot_x + b_width, r.y - pivot_y, &latmax, &lonmax, vp ); + } + symbox.Set( latmin, lonmin, latmax, lonmax ); + + // Now render the symbol + + if( !m_pdc ) // opengl + { +#ifdef ocpnUSE_GL + glEnable( GL_BLEND ); + + if(texture) { + extern GLenum g_texture_rectangle_format; + + glEnable(GL_TEXTURE_2D); + glEnable( GL_BLEND ); + glBindTexture(GL_TEXTURE_2D, texture); + + int w = texrect.width, h = texrect.height; + + float tx1 = texrect.x, ty1 = texrect.y; + float tx2 = tx1 + w, ty2 = ty1 + h; + +#ifndef USE_ANDROID_GLES2 +// glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + if(g_texture_rectangle_format == GL_TEXTURE_2D) { + wxSize size = m_texSoundings.GLTextureSize(); + tx1 /= size.x, tx2 /= size.x; + ty1 /= size.y, ty2 /= size.y; + } + + glColor3ub( symColor.Red(), symColor.Green(), symColor.Blue() ); + + + { + glPushMatrix(); + + glTranslatef(r.x, r.y, 0); + glRotatef(vp->rotation * 180/PI, 0, 0, -1); + glTranslatef(-pivot_x, -pivot_y, 0); + //glScalef(scale_factor, scale_factor, 1); + + glBegin(GL_QUADS); + glTexCoord2f(tx1, ty1); glVertex2i( 0, 0); + glTexCoord2f(tx2, ty1); glVertex2i( w, 0); + glTexCoord2f(tx2, ty2); glVertex2i( w, h); + glTexCoord2f(tx1, ty2); glVertex2i( 0, h); + glEnd(); + + glPopMatrix(); + } +#else + + + if(g_texture_rectangle_format == GL_TEXTURE_2D) { + + // Normalize the sybmol texture coordinates against the next higher POT size + wxSize size = ChartSymbols::GLTextureSize(); + int rb_x = size.x; + int rb_y = size.y; + + tx1 /= rb_x , tx2 /= rb_x ; + ty1 /= rb_y , ty2 /= rb_y ; + } + + float uv[8]; + float coords[8]; + + // Note swizzle of points to allow TRIANGLE_STRIP drawing + //normal uv + uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1; + uv[6] = tx2; uv[7] = ty2; uv[4] = tx1; uv[5] = ty2; + + w *= scale_factor; + h *= scale_factor; + + // pixels + coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0; + coords[6] = w; coords[7] = h; coords[4] = 0; coords[5] = h; + + glUseProgram( S52texture_2D_shader_program ); + + // Get pointers to the attributes in the program. + GLint mPosAttrib = glGetAttribLocation( S52texture_2D_shader_program, "position" ); + GLint mUvAttrib = glGetAttribLocation( S52texture_2D_shader_program, "aUV" ); + + // Select the active texture unit. + glActiveTexture( GL_TEXTURE0 ); + + // Bind our texture to the texturing target. + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set up the texture sampler to texture unit 0 + GLint texUni = glGetUniformLocation( S52texture_2D_shader_program, "uTex" ); + glUniform1i( texUni, 0 ); + + // Disable VBO's (vertex buffer objects) for attributes. + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); + + // Set the attribute mPosAttrib with the vertices in the screen coordinates... + glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords ); + // ... and enable it. + glEnableVertexAttribArray( mPosAttrib ); + + // Set the attribute mUvAttrib with the vertices in the GL coordinates... + glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv ); + // ... and enable it. + glEnableVertexAttribArray( mUvAttrib ); + + // Rotate + mat4x4 I, Q; + mat4x4_identity(I); + + mat4x4_translate_in_place(I, r.x, r.y, 0); + mat4x4_rotate_Z(Q, I, -vp->rotation); + mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0); + + + GLint matloc = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix"); + glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q); + + // Perform the actual drawing. + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Restore the per-object transform to Identity Matrix + mat4x4 IM; + mat4x4_identity(IM); + GLint matlocf = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix"); + glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM); + + +#endif // GLES2 + glDisable(g_texture_rectangle_format); + } else { /* this is only for legacy mode, or systems without NPOT textures */ + float cr = cosf( vp->rotation ); + float sr = sinf( vp->rotation ); + float ddx = pivot_x * cr + pivot_y * sr; + float ddy = pivot_y * cr - pivot_x * sr; +#ifndef USE_ANDROID_GLES2 + glColor4f( 1, 1, 1, 1 ); + + // Since draw pixels is so slow, lets not draw anything we don't have to + wxRect sym_rect(r.x - ddx, r.y - ddy, b_width, b_height); + if(vp->rv_rect.Intersects(sym_rect) ) { + + glPushAttrib( GL_SCISSOR_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + glDisable( GL_SCISSOR_TEST ); + glDisable( GL_STENCIL_TEST ); + glDisable( GL_DEPTH_TEST ); + + glRasterPos2f( r.x - ddx, r.y - ddy ); + glPixelZoom( 1, -1 ); + glDrawPixels( b_width, b_height, GL_RGBA, GL_UNSIGNED_BYTE, prule->pixelPtr ); + glPixelZoom( 1, 1 ); + + glPopAttrib(); + } +#endif + } + + glDisable( GL_BLEND ); +#endif + } + else { + wxString text; + text.Printf(_T("%d"), symIndex); + m_pdc->SetTextForeground( symColor ); + + m_pdc->DrawText(text, r.x - pivot_x, r.y - pivot_y); + + } + + return true; +} + + // Line Simple Style, OpenGL int s52plib::RenderGLLS( ObjRazRules *rzRules, Rules *rules, ViewPort *vp ) { @@ -5920,6 +6258,10 @@ int s52plib::RenderMPS( ObjRazRules *rzRules, Rules *rules, ViewPort *vp ) double angle = 0; Rules *rules = rzRules->mps->cs_rules->Item(ip); + bool bColorSet = false; + wxColor symColor; + GetGlobalColor(_T("SNDG2"), &symColor); + while( rules ){ // Render a raster or vector symbol, as specified by LUP rules @@ -5930,8 +6272,18 @@ int s52plib::RenderMPS( ObjRazRules *rzRules, Rules *rules, ViewPort *vp ) dryAngle = -vp->rotation * 180./PI; RenderHPGL( rzRules, rules->razRule, r, vp, dryAngle ); } - else if( rules->razRule->definition.SYDF == 'R' ) - RenderRasterSymbol( rzRules, rules->razRule, r, vp, angle ); + else if( rules->razRule->definition.SYDF == 'R' ){ + + // Parse the first rule to determine the color + if(!bColorSet){ + char symColorT = rules->razRule->name.SYNM[5]; + if(symColorT== 'G') + GetGlobalColor(_T("SNDG1"), &symColor); + bColorSet = true; + } + + RenderSoundingSymbol( rzRules, rules->razRule, r, vp, symColor, angle ); + } rules = rules->next; } diff --git a/src/s52plib.h b/src/s52plib.h index f8404ee3..9b9d16c2 100644 --- a/src/s52plib.h +++ b/src/s52plib.h @@ -29,6 +29,7 @@ #include #include "pi_s52s57.h" //types +#include "pi_DepthFont.h" class wxGLContext; @@ -349,6 +350,9 @@ class s52plib { ViewPort *vp, float rot_angle = 0. ); bool RenderRasterSymbol( ObjRazRules *rzRules, Rule *prule, wxPoint &r, ViewPort *vp, float rot_angle = 0. ); + bool RenderSoundingSymbol( ObjRazRules *rzRules, Rule *prule, wxPoint &r, + ViewPort *vp, wxColor symColor, float rot_angle = 0. ); + wxImage RuleXBMToImage( Rule *prule ); bool RenderText( wxDC *pdc, S52_TextC *ptext, int x, int y, @@ -432,6 +436,7 @@ class s52plib { RenderFromHPGL* HPGL; TexFont *m_txf; + DepthFont m_texSoundings; bool m_benableGLLS; DisCat m_nDisplayCategory; @@ -454,7 +459,8 @@ class s52plib { float *workBuf; size_t workBufSize; - + wxFont *m_soundFont; + };