1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  S52 Presentation Library
5  * Authors:   David Register
6  *            Jesper Weissglas
7  *
8  ***************************************************************************
9  *   Copyright (C) 2010 by David S. Register                               *
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  *   This program is distributed in the hope that it will be useful,       *
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
19  *   GNU General Public License for more details.                          *
20  *                                                                         *
21  *   You should have received a copy of the GNU General Public License     *
22  *   along with this program; if not, write to the                         *
23  *   Free Software Foundation, Inc.,                                       *
24  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
25  **************************************************************************/
26 
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
29 
30 #ifndef  WX_PRECOMP
31 #include "wx/wx.h"
32 #endif //precompiled headers
33 
34 #include <math.h>
35 #include <stdlib.h>
36 
37 #include "config.h"
38 
39 #include "georef.h"
40 #include "viewport.h"
41 
42 #include "s52plib.h"
43 #include "mygeom.h"
44 #include "cutil.h"
45 #include "s52utils.h"
46 #include "chartsymbols.h"
47 #include "TexFont.h"
48 #include "ocpn_plugin.h"
49 #include "gdal/cpl_csv.h"
50 #include "DepthFont.h"
51 
52 #include "s57chart.h"
53 #include <wx/image.h>
54 #include <wx/tokenzr.h>
55 #include <wx/fileconf.h>
56 
57 #ifndef PROJECTION_MERCATOR
58     #define PROJECTION_MERCATOR 1
59 #endif
60 
61 
62 #ifdef USE_ANDROID_GLES2
63 #include "linmath.h"
64 #endif
65 
66 #ifdef __OCPN__ANDROID__
67 #include "qdebug.h"
68 #endif
69 
70 extern float g_GLMinCartographicLineWidth;
71 extern float g_GLMinSymbolLineWidth;
72 extern double  g_overzoom_emphasis_base;
73 extern bool    g_oz_vector_scale;
74 extern float g_ChartScaleFactorExp;
75 extern int g_chart_zoom_modifier_vector;
76 
77 float g_scaminScale;
78 
79 extern PFNGLGENBUFFERSPROC                 s_glGenBuffers;
80 extern PFNGLBINDBUFFERPROC                 s_glBindBuffer;
81 extern PFNGLBUFFERDATAPROC                 s_glBufferData;
82 extern PFNGLDELETEBUFFERSPROC              s_glDeleteBuffers;
83 
84 #ifndef USE_ANDROID_GLES2
85 #define glGenBuffers(a,b) (s_glGenBuffers)(a,b);
86 #define glBindBuffer(a,b) (s_glBindBuffer)(a,b);
87 #define glBufferData(a,b,c,d) (s_glBufferData)(a,b,c,d);
88 #define glDeleteBuffers(a,b) (s_glDeleteBuffers)(a,b);
89 #endif
90 
91 void DrawAALine( wxDC *pDC, int x0, int y0, int x1, int y1, wxColour clrLine, int dash, int space );
92 extern bool GetDoubleAttr( S57Obj *obj, const char *AttrName, double &val );
93 void PLIBDrawGLThickLine( float x1, float y1, float x2, float y2, wxPen pen, bool b_hiqual );
94 
95 void LoadS57Config();
96 bool loadS52Shaders();
97 
98 #ifdef ocpnUSE_GL
99 typedef struct {
100     TexFont cache;
101     wxFont  *key;
102 } TexFontCache;
103 
104 #define TXF_CACHE 8
105 static TexFontCache s_txf[TXF_CACHE];
106 #endif
107 
108 #ifdef USE_ANDROID_GLES2
109 
110 GLint S52color_tri_shader_program;
111 GLint S52texture_2D_shader_program;
112 GLint S52texture_2D_ColorMod_shader_program;
113 GLint S52circle_filled_shader_program;
114 GLint S52ring_shader_program;
115 GLint S52Dash_shader_program;
116 GLint S52AP_shader_program;
117 
118 #endif
119 
120 //    Implement all lists
121 #include <wx/listimpl.cpp>
122 WX_DEFINE_LIST( TextObjList );
123 
124 
125 //    Implement all arrays
126 #include <wx/arrimpl.cpp>
127 WX_DEFINE_OBJARRAY(ArrayOfNoshow);
128 
129 //  S52_TextC Implementation
S52_TextC()130 S52_TextC::S52_TextC()
131 {
132     pcol = NULL;
133     pFont = NULL;
134     texobj = 0;
135     bnat = false;
136     bspecial_char = false;
137 }
138 
~S52_TextC()139 S52_TextC::~S52_TextC()
140 {
141     if(texobj){
142         glDeleteTextures(1, (GLuint *)(&this->texobj) );
143     }
144 }
145 
146 //      In GDAL-1.2.0, CSVGetField is not exported.......
147 //      So, make my own simplified copy
148 /************************************************************************/
149 /*                           MyCSVGetField()                            */
150 /*                                                                      */
151 /************************************************************************/
152 
MyPLIBCSVGetField(const char * pszFilename,const char * pszKeyFieldName,const char * pszKeyFieldValue,CSVCompareCriteria eCriteria,const char * pszTargetField)153 const char *MyPLIBCSVGetField( const char * pszFilename, const char * pszKeyFieldName,
154                            const char * pszKeyFieldValue, CSVCompareCriteria eCriteria, const char * pszTargetField )
155 
156 {
157     char **papszRecord;
158     int iTargetField;
159 
160     /* -------------------------------------------------------------------- */
161     /*      Find the correct record.                                        */
162     /* -------------------------------------------------------------------- */
163     papszRecord = CSVScanFileByName( pszFilename, pszKeyFieldName, pszKeyFieldValue, eCriteria );
164 
165     if( papszRecord == NULL ) return "";
166 
167     /* -------------------------------------------------------------------- */
168     /*      Figure out which field we want out of this.                     */
169     /* -------------------------------------------------------------------- */
170     iTargetField = CSVGetFileFieldId( pszFilename, pszTargetField );
171     if( iTargetField < 0 ) return "";
172 
173     if( iTargetField >= CSLCount( papszRecord ) ) return "";
174 
175     return ( papszRecord[iTargetField] );
176 }
177 
GetS57AttributeDecode(wxString & att,int ival)178 wxString GetS57AttributeDecode( wxString& att, int ival )
179 {
180     wxString ret_val = _T("");
181 
182     wxString s57data_dir = *GetpSharedDataLocation();
183     s57data_dir += _T("s57data");
184 
185     if( !s57data_dir.Len() )
186         return ret_val;
187 
188 //  Get the attribute code from the acronym
189     const char *att_code;
190 
191     wxString file = s57data_dir;
192     file.Append( _T("/s57attributes.csv") );
193 
194     if( !wxFileName::FileExists( file ) ) {
195         wxString msg( _T("   Could not open ") );
196         msg.Append( file );
197         wxLogMessage( msg );
198 
199         return ret_val;
200     }
201 
202     att_code = MyPLIBCSVGetField( file.mb_str(), "Acronym",                  // match field
203                               att.mb_str(),               // match value
204                               CC_ExactString, "Code" );             // return field
205 
206     // Now, get a nice description from s57expectedinput.csv
207     //  This will have to be a 2-d search, using ID field and Code field
208 
209     // Ingest, and get a pointer to the ingested table for "Expected Input" file
210     wxString ei_file = s57data_dir;
211     ei_file.Append( _T("/s57expectedinput.csv") );
212 
213     if( !wxFileName::FileExists( ei_file ) ) {
214         wxString msg( _T("   Could not open ") );
215         msg.Append( ei_file );
216         wxLogMessage( msg );
217 
218         return ret_val;
219     }
220 
221     CSVTable *psTable = CSVAccess( ei_file.mb_str() );
222     CSVIngest( ei_file.mb_str() );
223 
224     char **papszFields = NULL;
225     int bSelected = FALSE;
226 
227     /* -------------------------------------------------------------------- */
228     /*      Scan from in-core lines.                                        */
229     /* -------------------------------------------------------------------- */
230     int iline = 0;
231     while( !bSelected && iline + 1 < psTable->nLineCount ) {
232         iline++;
233         papszFields = CSVSplitLine( psTable->papszLines[iline] );
234 
235         if( !strcmp( papszFields[0], att_code ) ) {
236             if( atoi( papszFields[1] ) == ival ) {
237                 ret_val = wxString( papszFields[2], wxConvUTF8 );
238                 bSelected = TRUE;
239             }
240         }
241 
242         CSLDestroy( papszFields );
243     }
244 
245     return ret_val;
246 
247 }
248 
249 
250 //-----------------------------------------------------------------------------
251 //      Comparison Function for LUPArray sorting
252 //      Note Global Scope
253 //-----------------------------------------------------------------------------
254 #ifndef _COMPARE_LUP_DEFN_
255 #define _COMPARE_LUP_DEFN_
256 
CompareLUPObjects(LUPrec * item1,LUPrec * item2)257 int CompareLUPObjects( LUPrec *item1, LUPrec *item2 )
258 {
259     // sort the items by their name...
260     int ir = strcmp( item1->OBCL, item2->OBCL );
261 
262     if( ir != 0 )
263         return ir;
264     int c1 = item1->ATTArray.size();
265     int c2 = item2->ATTArray.size();
266 
267     if( c1 != c2 )
268         return c2 - c1;
269     return item1->nSequence - item2->nSequence;
270 }
271 
272 #endif
273 
274 
275 
276 
277 
278 
279 
280 
281 
282 
283 
284 
285 
286 
287 //-----------------------------------------------------------------------------
288 //      LUPArrayContainer implementation
289 //-----------------------------------------------------------------------------
LUPArrayContainer()290 LUPArrayContainer::LUPArrayContainer()
291 {
292     //   Build the initially empty sorted arrays of LUP Records, per LUP type.
293     //   Sorted on object name, e.g. ACHARE.  Why sorted?  Helps in the S52_LUPLookup method....
294     LUPArray = new wxArrayOfLUPrec( CompareLUPObjects );
295 }
296 
~LUPArrayContainer()297 LUPArrayContainer::~LUPArrayContainer()
298 {
299     if( LUPArray ) {
300         for( unsigned int il = 0; il < LUPArray->GetCount(); il++ )
301             s52plib::DestroyLUP( LUPArray->Item( il ) );
302 
303         LUPArray->Clear();
304         delete LUPArray;
305     }
306 
307     LUPArrayIndexHash::iterator it;
308     for( it = IndexHash.begin(); it != IndexHash.end(); ++it ){
309         free( it->second );
310     }
311 }
312 
GetArrayIndexHelper(const char * objectName)313 LUPHashIndex *LUPArrayContainer::GetArrayIndexHelper( const char *objectName )
314 {
315     // Look for the key
316     wxString key(objectName, wxConvUTF8);
317     LUPArrayIndexHash::iterator it = IndexHash.find( key );
318 
319     if( it == IndexHash.end() ){
320         //      Key not found, needs to be added
321         LUPHashIndex *pindex = (LUPHashIndex *)malloc(sizeof(LUPHashIndex));
322         pindex->n_start = -1;
323         pindex->count = 0;
324         IndexHash[key] = pindex;
325 
326         //      Find the first matching entry in the LUP Array
327         int index = 0;
328         int index_max = LUPArray->GetCount();
329         int first_match = 0;
330         int ocnt = 0;
331         LUPrec *LUPCandidate;
332 
333         //        This technique of extracting proper LUPs depends on the fact that
334         //        the LUPs have been sorted in their array, by OBCL.
335         //        Thus, all the LUPS with the same OBCL will be grouped together
336 
337         while( !first_match && ( index < index_max ) ) {
338             LUPCandidate = LUPArray->Item( index );
339             if( !strcmp( objectName, LUPCandidate->OBCL ) ) {
340                 pindex->n_start = index;
341                 first_match = 1;
342                 ocnt++;
343                 index++;
344                 break;
345             }
346             index++;
347         }
348 
349         while( first_match && ( index < index_max ) ) {
350             LUPCandidate = LUPArray->Item( index );
351             if( !strcmp( objectName, LUPCandidate->OBCL ) ) {
352                 ocnt++;
353             } else {
354                 break;
355             }
356 
357             index++;
358         }
359 
360         pindex->count = ocnt;
361 
362         return pindex;
363     }
364     else
365         return it->second;              // return a pointer to the found record
366 
367 
368 }
369 
370 
371 //-----------------------------------------------------------------------------
372 //      s52plib implementation
373 //-----------------------------------------------------------------------------
s52plib(const wxString & PLib,bool b_forceLegacy)374 s52plib::s52plib( const wxString& PLib, bool b_forceLegacy )
375 {
376     m_plib_file = PLib;
377 
378     pOBJLArray = new wxArrayPtrVoid;
379 
380     condSymbolLUPArray = NULL; // Dynamic Conditional Symbology
381 
382     _symb_sym = NULL;
383 
384     m_txf_ready = false;
385     m_txf = NULL;
386 
387     ChartSymbols::InitializeGlobals();
388 
389     m_bOK = !( S52_load_Plib( PLib, b_forceLegacy ) == 0 );
390 
391     m_bShowS57Text = false;
392     m_bShowS57ImportantTextOnly = false;
393     m_colortable_index = 0;
394 
395     _symb_symR = NULL;
396     bUseRasterSym = false;
397 
398     //      Sensible defaults
399     m_nSymbolStyle = PAPER_CHART;
400     m_nBoundaryStyle = PLAIN_BOUNDARIES;
401     m_nDisplayCategory = OTHER;
402     m_nDepthUnitDisplay = 1; // metres
403 
404     UpdateMarinerParams();
405 
406     ledge = new int[2000];
407     redge = new int[2000];
408 
409     //    Defaults
410     m_VersionMajor = 3;
411     m_VersionMinor = 2;
412 
413     canvas_pix_per_mm = 3.;
414     m_rv_scale_factor = 1.0;
415 
416     //        Set up some default flags
417     m_bDeClutterText = false;
418     m_bShowAtonText = true;
419     m_bShowNationalTexts = false;
420 
421     m_bShowSoundg = true;
422     m_bShowLdisText = true;
423     m_bExtendLightSectors = true;
424 
425     // Set a few initial states
426     AddObjNoshow( "M_QUAL" );
427     m_lightsOff = false;
428     m_anchorOn = true;
429     m_qualityOfDataOn = false;
430 
431     m_SoundingsScaleFactor = 1.0;
432 
433     GenerateStateHash();
434 
435     HPGL = new RenderFromHPGL( this );
436 
437 #ifdef USE_ANDROID_GLES2
438     loadS52Shaders();
439 #endif
440     //workBuf = NULL;;
441     //workBufSize = 0;;
442 
443     //  Set defaults for OCPN version, may be overridden later
444     m_coreVersionMajor = 4;
445     m_coreVersionMinor = 6;
446     m_coreVersionPatch = 0;
447 
448     m_myConfig = PI_GetPLIBStateHash();
449 
450     // GL Options/capabilities
451     m_useStencil = false;
452     m_useStencilAP = false;
453     m_useScissors = false;
454     m_useFBO = false;
455     m_useVBO = false;
456     m_TextureFormat = -1;
457 
458     m_display_size_mm = 300;
459     SetGLPolygonSmoothing( true );
460     SetGLLineSmoothing( true );
461 
462 }
463 
~s52plib()464 s52plib::~s52plib()
465 {
466     delete areaPlain_LAC;
467     delete line_LAC ;
468     delete areaSymbol_LAC;
469     delete pointSimple_LAC;
470     delete pointPaper_LAC;
471 
472     S52_flush_Plib();
473 
474 
475 //      Free the OBJL Array Elements
476     for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ )
477         free( pOBJLArray->Item( iPtr ) );
478 
479     delete pOBJLArray;
480 
481     delete[] ledge;
482     delete[] redge;
483 
484     ChartSymbols::DeleteGlobals();
485 
486     delete HPGL;
487 }
488 
SetOCPNVersion(int major,int minor,int patch)489 void s52plib::SetOCPNVersion(int major, int minor, int patch)
490 {
491     m_coreVersionMajor = major;
492     m_coreVersionMinor = minor;
493     m_coreVersionPatch = patch;
494 }
495 
SetGLOptions(bool b_useStencil,bool b_useStencilAP,bool b_useScissors,bool b_useFBO,bool b_useVBO,int nTextureFormat)496 void s52plib::SetGLOptions(bool b_useStencil,
497                   bool b_useStencilAP,
498                   bool b_useScissors,
499                   bool b_useFBO,
500                   bool b_useVBO,
501                   int  nTextureFormat)
502 {
503     // Set GL Options/capabilities
504     m_useStencil = b_useStencil;
505     m_useStencilAP = b_useStencilAP;
506     m_useScissors = b_useScissors;
507     m_useFBO = b_useFBO;
508     m_useVBO = b_useVBO;
509     m_TextureFormat = nTextureFormat;
510 }
511 
SetPPMM(float ppmm)512 void s52plib::SetPPMM( float ppmm )
513 {
514     canvas_pix_per_mm = ppmm;
515 
516     // We need a supplemental scale factor for HPGL vector symbol rendering.
517     //  This will cause raster and vector symbols to be rendered harmoniously
518 
519     //  We do this by making an arbitrary measurement and declaration:
520     // We declare that the nominal size of a "flare" light rendered as HPGL vector should be roughly twice the
521     // size of a simplified lateral bouy rendered as raster.
522 
523     // Referring to the chartsymbols.xml file, we find that the dimension of a flare light is 810 units,
524     // and a raster BOYLAT is 16 pix.
525 
526     m_rv_scale_factor = 2.0 * (1600. / (810 * ppmm));
527 
528     // Estimate the display size
529 
530     int ww, hh;
531     ::wxDisplaySize( &ww, &hh);
532     m_display_size_mm = wxMax(ww, hh) / GetPPMM();        // accurate enough for internal use
533 
534 
535 }
536 
537 //      Various static helper methods
538 
DestroyLUP(LUPrec * pLUP)539 void s52plib::DestroyLUP( LUPrec *pLUP )
540 {
541     Rules *top = pLUP->ruleList;
542     DestroyRulesChain(top);
543 
544     for(unsigned int i = 0 ; i < pLUP->ATTArray.size() ; i++)
545         free (pLUP->ATTArray[i]);
546 
547     delete pLUP->INST;
548 }
549 
DestroyRulesChain(Rules * top)550 void s52plib::DestroyRulesChain( Rules *top )
551 {
552     while( top != NULL ) {
553         Rules *Rtmp = top->next;
554 
555         free( top->INST0 ); // free the Instruction string head
556 
557         if( top->b_private_razRule ) // need to free razRule?
558         {
559             Rule *pR = top->razRule;
560             delete pR->exposition.LXPO;
561 
562             free( pR->vector.LVCT );
563 
564             delete pR->bitmap.SBTM;
565 
566             free( pR->colRef.SCRF );
567 
568             ClearRulesCache( pR );
569 
570             free( pR );
571         }
572 
573         free( top );
574         top = Rtmp;
575     }
576 
577 }
578 
579 
580 
findLUPDisCat(const char * objectName,LUPname TNAM)581 DisCat s52plib::findLUPDisCat(const char *objectName, LUPname TNAM)
582 {
583     LUPArrayContainer *plac = SelectLUPArrayContainer( TNAM );
584 
585     wxArrayOfLUPrec *LUPArray = SelectLUPARRAY( TNAM  );
586 
587 
588     //      Find the first matching entry in the LUP Array
589     int index = 0;
590     int index_max = LUPArray->GetCount();
591     LUPrec *LUPCandidate;
592 
593 
594     while(  index < index_max  ) {
595         LUPCandidate = LUPArray->Item( index );
596         if( !strcmp( objectName, LUPCandidate->OBCL ) ) {
597             return LUPCandidate->DISC;
598         }
599         index++;
600     }
601 
602     return (DisCat)(-1);
603 }
604 
605 
606 
607 
608 
609 
GetAnchorOn()610 bool s52plib::GetAnchorOn()
611 {
612     //  Investigate and report the logical condition that "Anchoring Condition" is shown
613 
614     int old_vis =  0;
615 
616     if(  MARINERS_STANDARD == GetDisplayCategory()){
617         old_vis = m_anchorOn;
618     }
619     else if(OTHER == GetDisplayCategory())
620         old_vis = true;
621 
622     //other cat
623     //const char * categories[] = { "ACHBRT", "ACHARE", "CBLSUB", "PIPARE", "PIPSOL", "TUNNEL", "SBDARE" };
624 
625     old_vis &= !IsObjNoshow("SBDARE");
626 
627     return (old_vis != 0);
628 }
629 
GetQualityOfData()630 bool s52plib::GetQualityOfData()
631 {
632     //  Investigate and report the logical condition that "Quality of Data Condition" is shown
633 
634     int old_vis =  0;
635 
636     if(  MARINERS_STANDARD == GetDisplayCategory()){
637             for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ ) {
638                 OBJLElement *pOLE = (OBJLElement *) ( pOBJLArray->Item( iPtr ) );
639                 if( !strncmp( pOLE->OBJLName, "M_QUAL", 6 ) ) {
640                     old_vis = pOLE->nViz;
641                     break;
642                 }
643             }
644     }
645     else if(OTHER == GetDisplayCategory())
646         old_vis = true;
647 
648     old_vis &= !IsObjNoshow("M_QUAL");
649 
650     return (old_vis != 0);
651 }
652 
653 
SetGLRendererString(const wxString & renderer)654 void s52plib::SetGLRendererString(const wxString &renderer)
655 {
656 }
657 
658 /*
659  Update the S52 Conditional Symbology Parameter Set to reflect the
660  current state of the library member options.
661  */
662 
UpdateMarinerParams(void)663 void s52plib::UpdateMarinerParams( void )
664 {
665 
666     //      Symbol Style
667     if( SIMPLIFIED == m_nSymbolStyle ) S52_setMarinerParam( S52_MAR_SYMPLIFIED_PNT, 1.0 );
668     else
669         S52_setMarinerParam( S52_MAR_SYMPLIFIED_PNT, 0.0 );
670 
671     //      Boundary Style
672     if( SYMBOLIZED_BOUNDARIES == m_nBoundaryStyle ) S52_setMarinerParam( S52_MAR_SYMBOLIZED_BND,
673             1.0 );
674     else
675         S52_setMarinerParam( S52_MAR_SYMBOLIZED_BND, 0.0 );
676 
677 }
678 
GenerateStateHash()679 void s52plib::GenerateStateHash()
680 {
681     unsigned char state_buffer[512];  // Needs to be at least this big...
682     memset(state_buffer, 0, sizeof(state_buffer));
683 
684     int time = ::wxGetUTCTime();
685     memcpy(state_buffer, &time, sizeof(int));
686 
687     size_t offset = sizeof(int);           // skipping the time int, first element
688 
689     for(int i=0 ; i < S52_MAR_NUM ; i++){
690         if( (offset + sizeof(double)) < sizeof(state_buffer)){
691             double t = S52_getMarinerParam((S52_MAR_param_t) i);
692             memcpy( &state_buffer[offset], &t, sizeof(double));
693 	    offset += sizeof(double);
694         }
695     }
696 
697     for(unsigned int i=0 ; i < m_noshow_array.GetCount() ; i++){
698         if( (offset + 6) < sizeof(state_buffer)){
699             memcpy(&state_buffer[offset], m_noshow_array[i].obj, 6) ;
700             offset += 6;
701         }
702     }
703 
704     if(offset + sizeof(bool) < sizeof(state_buffer))
705         { memcpy(&state_buffer[offset], &m_bShowSoundg, sizeof(bool));  offset += sizeof(bool); }
706 
707     if(offset + sizeof(bool) < sizeof(state_buffer))
708         { memcpy(&state_buffer[offset], &m_bShowS57Text, sizeof(bool));  offset += sizeof(bool); }
709 
710     if(offset + sizeof(bool) < sizeof(state_buffer))
711         { memcpy(&state_buffer[offset], &m_bShowS57ImportantTextOnly, sizeof(bool));  offset += sizeof(bool); }
712 
713     if(offset + sizeof(bool) < sizeof(state_buffer))
714         { memcpy(&state_buffer[offset], &m_bDeClutterText, sizeof(bool)); offset += sizeof(bool); }
715 
716     if(offset + sizeof(bool) < sizeof(state_buffer))
717         { memcpy(&state_buffer[offset], &m_bShowNationalTexts, sizeof(bool));  offset += sizeof(bool); }
718 
719     if(offset + sizeof(bool) < sizeof(state_buffer))
720         { memcpy(&state_buffer[offset], &m_bShowAtonText, sizeof(bool));  offset += sizeof(bool); }
721 
722     if(offset + sizeof(bool) < sizeof(state_buffer))
723         { memcpy(&state_buffer[offset], &m_bShowLdisText, sizeof(bool));  offset += sizeof(bool); }
724 
725     if(offset + sizeof(bool) < sizeof(state_buffer))
726         { memcpy(&state_buffer[offset], &m_bExtendLightSectors, sizeof(bool));  offset += sizeof(bool); }
727 
728     if(offset + sizeof(bool) < sizeof(state_buffer))
729         { memcpy(&state_buffer[offset], &m_nSoundingFactor, sizeof(int));  offset += sizeof(int); }
730 
731     m_state_hash = crc32buf(state_buffer, offset );
732 
733 }
734 
SelectLUPARRAY(LUPname TNAM)735 wxArrayOfLUPrec* s52plib::SelectLUPARRAY( LUPname TNAM  )
736 {
737     switch( TNAM ){
738         case SIMPLIFIED:
739             return pointSimple_LAC->GetLUPArray();
740         case PAPER_CHART:
741             return pointPaper_LAC->GetLUPArray();
742         case LINES:
743             return line_LAC->GetLUPArray();
744         case PLAIN_BOUNDARIES:
745             return areaPlain_LAC->GetLUPArray();
746         case SYMBOLIZED_BOUNDARIES:
747             return areaSymbol_LAC->GetLUPArray();
748         default:
749             return NULL;
750     }
751 }
752 
SelectLUPArrayContainer(LUPname TNAM)753 LUPArrayContainer *s52plib::SelectLUPArrayContainer( LUPname TNAM )
754 {
755     switch( TNAM ){
756         case SIMPLIFIED:
757             return pointSimple_LAC;
758         case PAPER_CHART:
759             return pointPaper_LAC;
760         case LINES:
761             return line_LAC;
762         case PLAIN_BOUNDARIES:
763             return areaPlain_LAC;
764         case SYMBOLIZED_BOUNDARIES:
765             return areaSymbol_LAC;
766         default:
767             return NULL;
768     }
769 }
770 
771 
772 extern Cond condTable[];
773 
FindBestLUP(wxArrayOfLUPrec * LUPArray,unsigned int startIndex,unsigned int count,S57Obj * pObj,bool bStrict)774 LUPrec *s52plib::FindBestLUP( wxArrayOfLUPrec *LUPArray, unsigned int startIndex, unsigned int count, S57Obj *pObj, bool bStrict )
775 {
776     //  Check the parameters
777     if( 0 == count )
778         return NULL;
779     if( startIndex >= LUPArray->GetCount() )
780         return NULL;
781 
782     // setup default return to the first LUP that matches Feature name.
783     LUPrec *LUP = LUPArray->Item( startIndex );
784 
785     int nATTMatch = 0;
786     int countATT = 0;
787     bool bmatch_found = false;
788 
789     if( pObj->att_array == NULL )
790         goto check_LUP;       // object has no attributes to compare, so return "best" LUP
791 
792     for( unsigned int i = 0; i < count; ++i ) {
793         LUPrec *LUPCandidate = LUPArray->Item( startIndex + i );
794 
795         if( !LUPCandidate->ATTArray.size() )
796             continue;        // this LUP has no attributes coded
797 
798         countATT = 0;
799         char *currATT = pObj->att_array;
800         int attIdx = 0;
801 
802         for( unsigned int iLUPAtt = 0; iLUPAtt < LUPCandidate->ATTArray.size(); iLUPAtt++ ) {
803 
804             // Get the LUP attribute name
805             char *slatc = LUPCandidate->ATTArray[iLUPAtt];
806 
807             if( slatc && (strlen(slatc) < 6) )
808                 goto next_LUP_Attr;         // LUP attribute value not UTF8 convertible (never seen in PLIB 3.x)
809 
810             if( slatc ){
811                 char *slatv = slatc + 6;
812                 while( attIdx < pObj->n_attr ) {
813                     if( 0 == strncmp( slatc, currATT, 6 ) ) {
814                         //OK we have an attribute name match
815 
816 
817                         bool attValMatch = false;
818 
819                         // special case (i)
820                         if( !strncmp( slatv, " ", 1 ) ) {        // any object value will match wild card (S52 para 8.3.3.4)
821                             ++countATT;
822                             goto next_LUP_Attr;
823                         }
824 
825                         // special case (ii)
826                         //TODO  Find an ENC with "UNKNOWN" DRVAL1 or DRVAL2 and debug this code
827                         if( !strncmp( slatv, "?", 1) ){          // if LUP attribute value is "undefined"
828 
829                         //  Match if the object does NOT contain this attribute
830                             goto next_LUP_Attr;
831                         }
832 
833 
834                         //checking against object attribute value
835                         S57attVal *v = ( pObj->attVal->Item( attIdx ) );
836 
837                         switch( v->valType ){
838                             case OGR_INT: // S57 attribute type 'E' enumerated, 'I' integer
839                             {
840                                 int LUP_att_val = atoi( slatv );
841                                 if( LUP_att_val == *(int*) ( v->value ) )
842                                     attValMatch = true;
843                                 break;
844                             }
845 
846                             case OGR_INT_LST: // S57 attribute type 'L' list: comma separated integer
847                             {
848                                 int a;
849                                 char ss[41];
850                                 strncpy( ss, slatv, 39 );
851                                 ss[40] = '\0';
852                                 char *s = &ss[0];
853 
854                                 int *b = (int*) v->value;
855                                 sscanf( s, "%d", &a );
856 
857                                 while( *s != '\0' ) {
858                                     if( a == *b ) {
859                                         sscanf( ++s, "%d", &a );
860                                         b++;
861                                         attValMatch = true;
862 
863                                     } else
864                                         attValMatch = false;
865                                 }
866                                 break;
867                             }
868                             case OGR_REAL: // S57 attribute type'F' float
869                             {
870                                 double obj_val = *(double*) ( v->value );
871                                 float att_val = atof( slatv );
872                                 if( fabs( obj_val - att_val ) < 1e-6 )
873                                     if( obj_val == att_val  )
874                                         attValMatch = true;
875                                 break;
876                             }
877 
878                             case OGR_STR: // S57 attribute type'A' code string, 'S' free text
879                             {
880                                 //    Strings must be exact match
881                                 //    n.b. OGR_STR is used for S-57 attribute type 'L', comma-separated list
882 
883                                 //wxString cs( (char *) v->value, wxConvUTF8 ); // Attribute from object
884                                 //if( LATTC.Mid( 6 ) == cs )
885                                 if( !strcmp((char *) v->value, slatv))
886                                     attValMatch = true;
887                                 break;
888                             }
889 
890                             default:
891                                 break;
892                         } //switch
893 
894                         // value match
895                         if( attValMatch )
896                             ++countATT;
897 
898                         goto next_LUP_Attr;
899                     } // if attribute name match
900 
901                     //  Advance to the next S57obj attribute
902                     currATT += 6;
903                     ++attIdx;
904 
905                 } //while
906             } //if
907 
908 next_LUP_Attr:
909 
910             currATT = pObj->att_array; // restart the object attribute list
911             attIdx = 0;
912         } // for iLUPAtt
913 
914         //      Create a "match score", defined as fraction of candidate LUP attributes
915         //      actually matched by feature.
916         //      Used later for resolving "ties"
917 
918         int nattr_matching_on_candidate = countATT;
919         int nattrs_on_candidate = LUPCandidate->ATTArray.size();
920         double candidate_score = ( 1. * nattr_matching_on_candidate )
921         / ( 1. * nattrs_on_candidate );
922 
923         //       According to S52 specs, match must be perfect,
924         //         and the first 100% match is selected
925         if( candidate_score == 1.0 ) {
926             LUP = LUPCandidate;
927             bmatch_found = true;
928             break; // selects the first 100% match
929         }
930 
931     } //for loop
932 
933 
934 check_LUP:
935 //  In strict mode, we require at least one attribute to match exactly
936 
937     if( bStrict ) {
938         if( nATTMatch == 0 ) // nothing matched
939             LUP = NULL;
940     } else {
941         //      If no match found, return the first LUP in the list which has no attributes
942         if( !bmatch_found ) {
943             for( unsigned int j = 0; j < count; ++j ) {
944                 LUPrec *LUPtmp = NULL;
945 
946                 LUPtmp = LUPArray->Item( startIndex + j );
947                 if( !LUPtmp->ATTArray.size() ) {
948                     return LUPtmp;
949                 }
950             }
951         }
952     }
953 
954     return LUP;
955 }
956 
957 
958 
959 
960 // scan foward stop on ; or end-of-record
961 #define SCANFWRD        while( !(*str == ';' || *str == '\037')) ++str;
962 
963 #define INSTRUCTION(s,t)        if(0==strncmp(s,str,2)){\
964                               str+=3;\
965                               r->ruleType = t;\
966                               r->INSTstr  = str;
967 
StringToRules(const wxString & str_in)968 Rules *s52plib::StringToRules( const wxString& str_in )
969 {
970     wxCharBuffer buffer=str_in.ToUTF8();
971     if(!buffer.data())
972         return NULL;
973 
974     size_t len = strlen( buffer.data() );
975     char *str0 = (char *) calloc( len + 1, 1 );
976     memcpy( str0, buffer.data(), len );
977     char *str = str0;
978 
979     Rules *top;
980     Rules *last;
981     char strk[20];
982 
983     //    Allocate and pre-clear the Rules structure
984     Rules *r = (Rules*) calloc( 1, sizeof(Rules) );
985     top = r;
986     last = top;
987 
988     r->INST0 = str0; // save the head for later free
989 
990     while( *str != '\0' ) {
991         if( r->ruleType ) // in the loop, r has been used
992         {
993             r = (Rules*) calloc( 1, sizeof(Rules) );
994             last->next = r;
995             last = r;
996         }
997 
998         // parse Symbology instruction in string
999 
1000         // Special Case for Circular Arc,  (opencpn private)
1001         // Allocate a Rule structure to be used to hold a cached bitmap of the created symbol
1002         INSTRUCTION ( "CA",RUL_ARC_2C )
1003             r->razRule = (Rule*) calloc( 1, sizeof(Rule) );
1004             r->b_private_razRule = true; // mark this raxRule to be free'd later
1005             SCANFWRD
1006         }
1007 
1008         // Special Case for MultPoint Soundings
1009         INSTRUCTION ( "MP",RUL_MUL_SG )
1010             SCANFWRD
1011         }
1012 
1013 // SHOWTEXT
1014         INSTRUCTION ( "TX",RUL_TXT_TX )
1015             SCANFWRD
1016         }
1017 
1018         INSTRUCTION ( "TE",RUL_TXT_TE )
1019             SCANFWRD
1020         }
1021 
1022 // SHOWPOINT
1023 
1024         if( 0 == strncmp( "SY", str, 2 ) ) {
1025             str += 3;
1026             r->ruleType = RUL_SYM_PT;
1027             r->INSTstr = str;
1028 
1029             strncpy( strk, str, 8 );
1030             strk[8] = 0;
1031             wxString key( strk, wxConvUTF8 );
1032 
1033             r->razRule = ( *_symb_sym )[key];
1034 
1035             if( r->razRule == NULL ) r->razRule = ( *_symb_sym )[_T ( "QUESMRK1" )];
1036 
1037             SCANFWRD
1038         }
1039 
1040 // SHOWLINE
1041         INSTRUCTION ( "LS",RUL_SIM_LN )
1042             SCANFWRD
1043         }
1044 
1045         INSTRUCTION ( "LC",RUL_COM_LN )
1046             strncpy( strk, str, 8 );
1047             strk[8] = 0;
1048             wxString key( strk, wxConvUTF8 );
1049 
1050             r->razRule = ( *_line_sym )[key];
1051 
1052             if( r->razRule == NULL ) r->razRule = ( *_symb_sym )[_T ( "QUESMRK1" )];
1053             SCANFWRD
1054         }
1055 
1056         // SHOWAREA
1057         INSTRUCTION ( "AC",RUL_ARE_CO )
1058             SCANFWRD
1059         }
1060 
1061         INSTRUCTION ( "AP",RUL_ARE_PA )
1062             strncpy( strk, str, 8 );
1063             strk[8] = 0;
1064             wxString key( strk, wxConvUTF8 );
1065 
1066             r->razRule = ( *_patt_sym )[key];
1067             if( r->razRule == NULL ) r->razRule = ( *_patt_sym )[_T ( "QUESMRK1V" )];
1068             SCANFWRD
1069         }
1070 
1071         // CALLSYMPROC
1072 
1073         if( 0 == strncmp( "CS", str, 2 ) ) {
1074             str += 3;
1075             r->ruleType = RUL_CND_SY;
1076             r->INSTstr = str;
1077 
1078 //      INSTRUCTION("CS",RUL_CND_SY)
1079             char stt[9];
1080             strncpy( stt, str, 8 );
1081             stt[8] = 0;
1082             wxString index( stt, wxConvUTF8 );
1083             r->razRule = ( *_cond_sym )[index];
1084             if( r->razRule == NULL ) r->razRule = ( *_cond_sym )[_T ( "QUESMRK1" )];
1085             SCANFWRD
1086         }
1087 
1088         ++str;
1089     }
1090 
1091     //  If it should happen that no rule is built, delete the initially allocated rule
1092     if( 0 == top->ruleType ) {
1093         if( top->INST0 ) free( top->INST0 );
1094 
1095         free( top );
1096 
1097         top = NULL;
1098     }
1099 
1100     //   Traverse the entire rule set tree, pruning after first unallocated (dead) rule
1101     r = top;
1102     while( r ) {
1103         if( 0 == r->ruleType ) {
1104             free( r );
1105             last->next = NULL;
1106             break;
1107         }
1108 
1109         last = r;
1110         Rules *n = r->next;
1111         r = n;
1112     }
1113 
1114     //   Traverse the entire rule set tree, adding sequence numbers
1115     r = top;
1116     int i = 0;
1117     while( r ) {
1118         r->n_sequence = i++;
1119 
1120         r = r->next;
1121     }
1122 
1123     return top;
1124 }
1125 
1126 int s52plib::_LUP2rules( LUPrec *LUP, S57Obj *pObj )
1127 {
1128     if( NULL == LUP ) return -1;
1129     // check if already parsed
1130     if( LUP->ruleList != NULL ) {
1131         return 0;
1132     }
1133 
1134     if( LUP->INST != NULL ) {
1135         Rules *top = StringToRules( *LUP->INST );
1136         LUP->ruleList = top;
1137 
1138         return 1;
1139     } else
1140         return 0;
1141 }
1142 
1143 
1144 int s52plib::S52_load_Plib( const wxString& PLib, bool b_forceLegacy )
1145 {
1146 
1147     pAlloc = new wxArrayPtrVoid;
1148 
1149     //   Create the Rule Lookup Hash Tables
1150     _line_sym = new RuleHash; // line
1151     _patt_sym = new RuleHash; // pattern
1152     _symb_sym = new RuleHash; // symbol
1153     _cond_sym = new RuleHash; // conditional
1154 
1155     line_LAC = new LUPArrayContainer;
1156     areaPlain_LAC= new LUPArrayContainer;
1157     areaSymbol_LAC= new LUPArrayContainer;
1158     pointSimple_LAC= new LUPArrayContainer;
1159     pointPaper_LAC= new LUPArrayContainer;
1160 
1161     condSymbolLUPArray = new wxArrayOfLUPrec( CompareLUPObjects ); // dynamic Cond Sym LUPs
1162 
1163     m_unused_color.R = 2;
1164     m_unused_color.G = 2;
1165     m_unused_color.B = 2;
1166     m_unused_wxColor.Set( 2,2,2 );
1167 
1168 #if 0
1169     // First, honor the user attempt for force Lecagy mode.
1170     // Next, try to load symbols using the newer XML/PNG format.
1171     // If this fails, try Legacy S52RAZDS.RLE file.
1172 
1173     if( b_forceLegacy ) {
1174         RazdsParser parser;
1175         useLegacyRaster = true;
1176         if( parser.LoadFile( this, PLib ) ) {
1177             wxString msg( _T("Loaded legacy PLIB data: ") );
1178             msg += PLib;
1179             wxLogMessage( msg );
1180         } else
1181             return 0;
1182     } else {
1183         ChartSymbols chartSymbols;
1184         useLegacyRaster = false;
1185         if( !chartSymbols.LoadConfigFile( this, PLib ) ) {
1186             RazdsParser parser;
1187             useLegacyRaster = true;
1188             if( parser.LoadFile( this, PLib ) ) {
1189                 wxString msg( _T("Loaded legacy PLIB data: ") );
1190                 msg += PLib;
1191                 wxLogMessage( msg );
1192             } else
1193                 return 0;
1194         }
1195     }
1196 #endif
1197     ChartSymbols chartSymbols;
1198     useLegacyRaster = false;
1199     if( !chartSymbols.LoadConfigFile( this, PLib ) ) {
1200         wxString msg( _T("Could not load XML PLib symbol file: ") );
1201         msg += PLib;
1202         wxLogMessage( msg );
1203 
1204         return 0;
1205     }
1206 
1207     //   Initialize the _cond_sym Hash Table from the jump table found in S52CNSY.CPP
1208     //   Hash Table indices are the literal CS Strings, e.g. "RESARE02"
1209     //   Hash Results Values are the Rule *, i.e. the CS procedure entry point
1210 
1211     for( int i = 0; condTable[i].condInst != NULL; ++i ) {
1212         wxString index( condTable[i].name, wxConvUTF8 );
1213         ( *_cond_sym )[index] = (Rule *) ( condTable[i].condInst );
1214     }
1215 
1216     wxString s57data_dir = *GetpSharedDataLocation();
1217     s57data_dir += _T("s57data");
1218 
1219     wxString oc_file( s57data_dir );
1220     oc_file.Append( _T("/s57objectclasses.csv") );
1221 
1222     PreloadOBJLFromCSV( oc_file );
1223 
1224     return 1;
1225 }
1226 
1227 void s52plib::ClearRulesCache( Rule *pR ) //  Clear out any existing cached symbology
1228 {
1229     switch( pR->parm0 ){
1230         case ID_wxBitmap: {
1231             wxBitmap *pbm = (wxBitmap *) ( pR->pixelPtr );
1232             delete pbm;
1233             pR->pixelPtr = NULL;
1234             pR->parm0 = ID_EMPTY;
1235             break;
1236         }
1237         case ID_RGBA: {
1238             unsigned char *p = (unsigned char *) ( pR->pixelPtr );
1239             free( p );
1240             pR->pixelPtr = NULL;
1241             pR->parm0 = ID_EMPTY;
1242             break;
1243         }
1244         case ID_GL_PATT_SPEC: {
1245             render_canvas_parms *pp = (render_canvas_parms *) ( pR->pixelPtr );
1246             free( pp->pix_buff );
1247 #ifdef ocpnUSE_GL
1248             if(pp->OGL_tex_name) glDeleteTextures( 1, (GLuint *) &pp->OGL_tex_name );
1249 #endif
1250             delete pp;
1251             pR->pixelPtr = NULL;
1252             pR->parm0 = ID_EMPTY;
1253             break;
1254         }
1255         case ID_RGB_PATT_SPEC: {
1256             render_canvas_parms *pp = (render_canvas_parms *) ( pR->pixelPtr );
1257             free( pp->pix_buff );
1258             delete pp;
1259             pR->pixelPtr = NULL;
1260             pR->parm0 = ID_EMPTY;
1261             break;
1262         }
1263         case ID_EMPTY:
1264             break;
1265         default:
1266             assert(false);
1267             break;
1268     }
1269 }
1270 
1271 void s52plib::DestroyPatternRuleNode( Rule *pR )
1272 {
1273     DestroyRuleNode(pR);
1274 }
1275 
1276 void s52plib::DestroyRuleNode( Rule *pR )
1277 {
1278     if( !pR )
1279         return;
1280 
1281     delete pR->exposition.LXPO;
1282 
1283     free( pR->vector.LVCT );
1284 
1285     delete pR->bitmap.SBTM;
1286 
1287     free( pR->colRef.SCRF );
1288 
1289     ClearRulesCache( pR ); //  Clear out any existing cached symbology
1290 }
1291 
1292 void s52plib::DestroyRules( RuleHash *rh )
1293 {
1294     RuleHash::iterator it;
1295     Rule *pR;
1296 
1297     for( it = ( *rh ).begin(); it != ( *rh ).end(); ++it ) {
1298         pR = it->second;
1299         DestroyRuleNode( pR );
1300     }
1301 
1302     rh->clear();
1303     delete rh;
1304 }
1305 
1306 void s52plib::FlushSymbolCaches( void )
1307 {
1308     if( !useLegacyRaster ) ChartSymbols::LoadRasterFileForColorTable( m_colortable_index, true );
1309 
1310     RuleHash *rh = _symb_sym;
1311 
1312     if( !rh ) return;
1313 
1314     RuleHash::iterator it;
1315     Rule *pR;
1316 
1317     for( it = ( *rh ).begin(); it != ( *rh ).end(); ++it ) {
1318         pR = it->second;
1319         if( pR ) ClearRulesCache( pR );
1320     }
1321 
1322     //    Flush any pattern definitions
1323     rh = _patt_sym;
1324 
1325     if( !rh ) return;
1326 
1327     for( it = ( *rh ).begin(); it != ( *rh ).end(); ++it ) {
1328         pR = it->second;
1329         if( pR ) ClearRulesCache( pR );
1330     }
1331 
1332     //    OpenGL Hashmaps
1333     CARC_Hash::iterator ita;
1334     for( ita = m_CARC_hashmap.begin(); ita != m_CARC_hashmap.end(); ++ita ) {
1335         CARC_Buffer buffer = ita->second;
1336         delete [] buffer.data;
1337     }
1338     m_CARC_hashmap.clear();
1339 
1340 #ifdef ocpnUSE_GL
1341 #ifndef USE_ANDROID_GLES2
1342     CARC_DL_Hash::iterator itd;
1343     for( itd = m_CARC_DL_hashmap.begin(); itd != m_CARC_DL_hashmap.end(); ++itd ) {
1344         GLuint list = itd->second;
1345         glDeleteLists( list, 1 );
1346     }
1347     m_CARC_DL_hashmap.clear();
1348 #endif
1349 #endif
1350 
1351 
1352     // Flush all texFonts
1353     TexFont *f_cache = 0;
1354     unsigned int i;
1355     for (i = 0; i < TXF_CACHE; i++)
1356     {
1357         if (s_txf[i].key != 0) {
1358             f_cache = &s_txf[i].cache;
1359             f_cache->Delete();
1360             s_txf[i].key = 0;
1361         }
1362     }
1363 
1364 }
1365 
1366 void s52plib::DestroyPattRules( RuleHash *rh )
1367 {
1368     DestroyRules(rh);
1369 }
1370 
1371 
1372 void s52plib::DestroyLUPArray( wxArrayOfLUPrec *pLUPArray )
1373 {
1374     if( pLUPArray ) {
1375         for( unsigned int il = 0; il < pLUPArray->GetCount(); il++ )
1376             DestroyLUP( pLUPArray->Item( il ) );
1377 
1378         pLUPArray->Clear();
1379 
1380         delete pLUPArray;
1381     }
1382 }
1383 
1384 void s52plib::ClearCNSYLUPArray( void )
1385 {
1386     if( condSymbolLUPArray ) {
1387         for( unsigned int i = 0; i < condSymbolLUPArray->GetCount(); i++ )
1388             DestroyLUP( condSymbolLUPArray->Item( i ) );
1389 
1390         condSymbolLUPArray->Clear();
1391     }
1392 }
1393 
1394 bool s52plib::S52_flush_Plib()
1395 {
1396     if(!m_bOK)
1397         return false;
1398 
1399 #ifdef ocpnUSE_GL
1400     //    OpenGL Hashmaps
1401     CARC_Hash::iterator ita;
1402     for( ita = m_CARC_hashmap.begin(); ita != m_CARC_hashmap.end(); ++ita ) {
1403         CARC_Buffer buffer = ita->second;
1404         delete [] buffer.data;
1405     }
1406     m_CARC_hashmap.clear();
1407 
1408 #ifndef USE_ANDROID_GLES2
1409     CARC_DL_Hash::iterator itd;
1410     for( itd = m_CARC_DL_hashmap.begin(); itd != m_CARC_DL_hashmap.end(); ++itd ) {
1411         GLuint list = itd->second;
1412         glDeleteLists( list, 1 );
1413     }
1414     m_CARC_DL_hashmap.clear();
1415 #endif
1416 
1417 #endif
1418 
1419     DestroyLUPArray( condSymbolLUPArray );
1420 
1421 //      Destroy Rules
1422     DestroyRules( _line_sym );
1423     DestroyPattRules( _patt_sym );
1424     DestroyRules( _symb_sym );
1425 
1426     if( _symb_symR ) DestroyRules( _symb_symR );
1427 
1428 //      Special case for CS
1429     _cond_sym->clear();
1430     delete ( _cond_sym );
1431 
1432     for( unsigned int ipa = 0; ipa < pAlloc->GetCount(); ipa++ ) {
1433         void *t = pAlloc->Item( ipa );
1434         free( t );
1435     }
1436 
1437     pAlloc->clear();
1438     delete pAlloc;
1439 
1440     return TRUE;
1441 }
1442 
1443 LUPrec *s52plib::S52_LUPLookup( LUPname LUP_Name, const char * objectName, S57Obj *pObj, bool bStrict )
1444 {
1445     LUPrec *LUP = NULL;
1446 
1447     LUPArrayContainer *plac = SelectLUPArrayContainer( LUP_Name );
1448 
1449     LUPHashIndex *hip = plac->GetArrayIndexHelper( objectName );
1450     int nLUPs = hip->count;
1451     int nStartIndex = hip->n_start;
1452 
1453     LUP = FindBestLUP( plac->GetLUPArray(), nStartIndex, nLUPs, pObj, bStrict );
1454 
1455     return LUP;
1456 }
1457 
1458 
1459 void  s52plib::SetPLIBColorScheme( ColorScheme cs )
1460 {
1461     wxString SchemeName;
1462     switch( cs ){
1463         case GLOBAL_COLOR_SCHEME_DAY:
1464             SchemeName = _T("DAY");
1465             break;
1466         case GLOBAL_COLOR_SCHEME_DUSK:
1467             SchemeName = _T("DUSK");
1468             break;
1469         case GLOBAL_COLOR_SCHEME_NIGHT:
1470             SchemeName = _T("NIGHT");
1471             break;
1472         default:
1473             SchemeName = _T("DAY");
1474             break;
1475     }
1476 
1477     SetPLIBColorScheme( SchemeName );
1478 }
1479 
1480 
1481 
1482 void s52plib::SetPLIBColorScheme( wxString scheme )
1483 {
1484     wxString str_find;
1485     str_find = scheme;
1486     m_colortable_index = 0; // default is the first color in the table
1487 
1488 // Of course, it also depends on the plib version...
1489 // plib version 3.2 calls "DAY" colr as "DAY_BRIGHT"
1490 
1491     if( ( GetMajorVersion() == 3 ) && ( GetMinorVersion() == 2 ) ) {
1492         if( scheme.IsSameAs( _T ( "DAY" ) ) ) str_find = _T ( "DAY_BRIGHT" );
1493     }
1494     m_colortable_index = ChartSymbols::FindColorTable( scheme );
1495 
1496 //    if( !useLegacyRaster ) ChartSymbols::LoadRasterFileForColorTable( m_colortable_index );
1497 
1498     if( !useLegacyRaster ) ChartSymbols::SetColorTableIndex( m_colortable_index );
1499 
1500     m_ColorScheme = scheme;
1501 }
1502 
1503 S52color* s52plib::getColor( const char *colorName )
1504 {
1505     S52color* c;
1506     c = ChartSymbols::GetColor( colorName, m_colortable_index );
1507     return c;
1508 }
1509 
1510 wxColour s52plib::getwxColour( const wxString &colorName )
1511 {
1512     wxColor c;
1513     c = ChartSymbols::GetwxColor( colorName, m_colortable_index );
1514     return c;
1515 }
1516 
1517 //----------------------------------------------------------------------------------
1518 //
1519 //              Object Rendering Module
1520 //
1521 //----------------------------------------------------------------------------------
1522 
1523 //-----------------------------
1524 //
1525 // S52 TEXT COMMAND WORD PARSER
1526 //
1527 //-----------------------------
1528 #define APOS   '\047'
1529 #define MAXL       512
1530 
1531 char *s52plib::_getParamVal( ObjRazRules *rzRules, char *str, char *buf, int bsz )
1532 // Symbology Command Word Parameter Value Parser.
1533 //
1534 //      str: psz to Attribute of interest
1535 //
1536 //      Results:Put in 'buf' one of:
1537 //  1- LUP constant value,
1538 //  2- ENC value,
1539 //  3- LUP default value.
1540 // Return pointer to the next field in the string (delim is ','), NULL to abort
1541 {
1542     wxString value;
1543     int defval = 0; // default value
1544     int len = 0;
1545     char *ret_ptr = str;
1546     char *tmp = buf;
1547 
1548     if(!buf)
1549         return NULL;
1550 
1551     buf[0] = 0;
1552     // parse constant parameter with concatenation operator "'"
1553     if( str != NULL ) {
1554         if( *ret_ptr == APOS ) {
1555             ret_ptr++;
1556             while( *ret_ptr != APOS &&  *ret_ptr != '\0' && len < ( bsz - 1 )) {
1557                 ++len;
1558                 *tmp++ = *ret_ptr++;
1559             }
1560             *tmp = '\0';
1561             ret_ptr++; // skip "'"
1562             ret_ptr++; // skip ","
1563 
1564             return ret_ptr;
1565         }
1566 
1567         while( *ret_ptr != ',' && *ret_ptr != ')' && *ret_ptr != '\0' && len < ( bsz - 1 ) ) {
1568             *tmp++ = *ret_ptr++;
1569             ++len;
1570         }
1571         *tmp = '\0';
1572 
1573         ret_ptr++; // skip ',' or ')'
1574     }
1575     if( len < 6 )
1576         return ret_ptr;
1577 
1578     // chop string if default value present
1579     if( len > 6 && *( buf + 6 ) == '=' ) {
1580         *( buf + 6 ) = '\0';
1581         defval = 1;
1582     }
1583 
1584     value = rzRules->obj->GetAttrValueAsString( buf );
1585     wxCharBuffer buffer=value.ToUTF8();
1586     if(!buffer.data())
1587         return ret_ptr;
1588 
1589     if( value.IsEmpty() ) {
1590         if( defval )
1591             _getParamVal( rzRules, buf + 7, buf, bsz - 7 ); // default value --recursion
1592         else {
1593             return NULL; // abort
1594         }
1595     } else {
1596 
1597         //    Special case for conversion of some vertical (height) attributes to feet
1598         if( ( !strncmp( buf, "VERCLR", 6 ) ) || ( !strncmp( buf, "VERCCL", 6 ) ) || ( !strncmp( buf, "VERCOP", 6 ) ) ) {
1599             switch( m_nDepthUnitDisplay ){
1600                 case 0: // feet
1601                 case 2: // fathoms
1602                     double ft_val;
1603                     value.ToDouble( &ft_val );
1604                     ft_val = ft_val * 3 * 39.37 / 36; // feet
1605                     value.Printf( _T("%4.1f"), ft_val );
1606                     break;
1607                 default:
1608                     break;
1609             }
1610         }
1611 
1612         // special case when ENC returns an index for particular attribute types
1613         if( !strncmp( buf, "NATSUR", 6 ) ) {
1614             wxString natsur_att( _T ( "NATSUR" ) );
1615             wxString result;
1616             wxString svalue = value;
1617             wxStringTokenizer tkz( svalue, _T ( "," ) );
1618 
1619             int icomma = 0;
1620             while( tkz.HasMoreTokens() ) {
1621                 if( icomma )
1622                     result += _T ( "," );
1623 
1624                 wxString token = tkz.GetNextToken();
1625                 long i;
1626                 if( token.ToLong(&i) ){
1627                     wxString nat;
1628                     if( !m_natsur_hash[i].IsEmpty() )            // entry available?
1629                         nat = m_natsur_hash[i];
1630                     else {
1631                         nat = GetS57AttributeDecode( natsur_att, (int)i );
1632                         m_natsur_hash[i] = nat;            // cache the entry
1633                     }
1634 
1635                     if( !nat.IsEmpty() )
1636                         result += nat; // value from ENC
1637                     else
1638                         result += _T ( "unk" );
1639                 }
1640                 else
1641                     result += _T ( "unk" );
1642 
1643                 icomma++;
1644             }
1645 
1646             value = result;
1647         }
1648 
1649         wxCharBuffer buffer=value.ToUTF8();
1650         if(buffer.data()){
1651             unsigned int len = wxMin(strlen(buffer.data()), (unsigned int)bsz-1);
1652             memcpy( buf, buffer.data(), len );
1653             buf[len] = 0;
1654         }
1655         else
1656             *buf = 0;
1657     }
1658 
1659     return ret_ptr;
1660 }
1661 
1662 char *s52plib::_parseTEXT( ObjRazRules *rzRules, S52_TextC *text, char *str0 )
1663 {
1664     char buf[MAXL]; // output string
1665 
1666     char *str = str0;
1667     if( text ) {
1668         memset(buf, 0, 4);
1669         str = _getParamVal( rzRules, str, &text->hjust, MAXL ); // HJUST
1670         str = _getParamVal( rzRules, str, &text->vjust, MAXL ); // VJUST
1671         str = _getParamVal( rzRules, str, &text->space, MAXL ); // SPACE
1672 
1673         // CHARS
1674         str = _getParamVal( rzRules, str, buf, MAXL );
1675         text->style = buf[0];
1676         text->weight = buf[1];
1677         text->width = buf[2];
1678         text->bsize = atoi( buf + 3 );
1679 
1680         str = _getParamVal( rzRules, str, buf, MAXL );
1681         text->xoffs = atoi( buf );
1682         str = _getParamVal( rzRules, str, buf, MAXL );
1683         text->yoffs = atoi( buf );
1684         str = _getParamVal( rzRules, str, buf, MAXL );
1685         text->pcol = getColor( buf );
1686         str = _getParamVal( rzRules, str, buf, MAXL );
1687         text->dis = atoi( buf ); // Text Group, used for "Important" text detection
1688     }
1689     return str;
1690 }
1691 
1692 S52_TextC *s52plib::S52_PL_parseTX( ObjRazRules *rzRules, Rules *rules, char *cmd )
1693 {
1694     S52_TextC *text = NULL;
1695     char *str = NULL;
1696     char val[MAXL]; // value of arg
1697     char strnobjnm[7] = { "NOBJNM" };
1698     char valn[MAXL]; // value of arg
1699 
1700     valn[0] = 0;
1701     str = (char*) rules->INSTstr;
1702 
1703     if( m_bShowNationalTexts && NULL != strstr( str, "OBJNAM" ) ) // in case user wants the national text shown and the rule contains OBJNAM, try to get the value
1704     {
1705         _getParamVal( rzRules, strnobjnm, valn, MAXL );
1706         if( !strcmp( strnobjnm, valn ) )
1707             valn[0] = '\0'; //NOBJNM is not defined
1708         else
1709             valn[MAXL - 1] = '\0'; // make sure the string terminates
1710     }
1711 
1712     str = _getParamVal( rzRules, str, val, MAXL ); // get ATTRIB list
1713 
1714     if( NULL == str ) return 0; // abort this command word if mandatory param absent
1715 
1716     val[MAXL - 1] = '\0'; // make sure the string terminates
1717 
1718     text = new S52_TextC;
1719     str = _parseTEXT( rzRules, text, str );
1720     if( NULL != text )
1721     {
1722         if ( valn[0] != '\0' ) {
1723             text->frmtd = wxString( valn, wxConvUTF8 );
1724             text->bnat = true;
1725         }
1726         else {
1727             text->frmtd = wxString( val, wxConvUTF8 );
1728             text->bnat = false;
1729         }
1730     }
1731 
1732     //  We check to see if the formatted text has any "special" characters
1733     wxCharBuffer buf = text->frmtd.ToUTF8();
1734 
1735     unsigned int n = text->frmtd.Length();
1736     for(unsigned int i=0 ; i < n ; ++i){
1737         unsigned char c =buf.data()[i];
1738         if(c > 127){
1739             text->bspecial_char = true;
1740             break;
1741         }
1742     }
1743 
1744     return text;
1745 }
1746 
1747 S52_TextC *s52plib::S52_PL_parseTE( ObjRazRules *rzRules, Rules *rules, char *cmd )
1748 // same as S52_PL_parseTX put parse 'C' format first
1749 {
1750     char arg[MAXL]; // ATTRIB list
1751     char fmt[MAXL]; // FORMAT
1752     char buf[MAXL]; // output string
1753     char *b = buf;
1754     char *parg = arg;
1755     char *pf = fmt;
1756     S52_TextC *text = NULL;
1757 
1758     char *str = (char*) rules->INSTstr;
1759 
1760     if( str && *str ) {
1761         str = _getParamVal( rzRules, str, fmt, MAXL ); // get FORMAT
1762 
1763         str = _getParamVal( rzRules, str, arg, MAXL ); // get ATTRIB list
1764         if( NULL == str ) return 0; // abort this command word if mandatory param absent
1765 
1766         //*b = *pf;
1767         while( *pf != '\0' ) {
1768 
1769             // begin a convertion specification
1770             if( *pf == '%' ) {
1771                 char val[MAXL]; // value of arg
1772                 char tmp[MAXL] = { '\0' }; // temporary format string
1773                 char *t = tmp;
1774                 int cc = 0; // 1 == Conversion Character found
1775                 //*t = *pf;
1776 
1777                 // get value for this attribute
1778                 parg = _getParamVal( rzRules, parg, val, MAXL );
1779                 if( NULL == parg ) return 0; // abort
1780 
1781                 if( 0 == strcmp( val, "2147483641" ) ) return 0;
1782 
1783                 *t = *pf; // stuff the '%'
1784 
1785                 // scan for end at convertion character
1786                 do {
1787                     *++t = *++pf; // fill conver spec
1788 
1789                     switch( *pf ){
1790                         case 'c':
1791                         case 's':
1792                             b += sprintf( b, tmp, val );
1793                             cc = 1;
1794                             break;
1795                         case 'f':
1796                             b += sprintf( b, tmp, atof( val ) );
1797                             cc = 1;
1798                             break;
1799                         case 'd':
1800                         case 'i':
1801                             b += sprintf( b, tmp, atoi( val ) );
1802                             cc = 1;
1803                             break;
1804                     }
1805                 } while( !cc );
1806                 pf++; // skip conv. char
1807 
1808             } else
1809                 *b++ = *pf++;
1810         }
1811 
1812         *b = 0;
1813         text = new S52_TextC;
1814         str = _parseTEXT( rzRules, text, str );
1815         if( NULL != text ) text->frmtd = wxString( buf, wxConvUTF8 );
1816 
1817         //  We check to see if the formatted text has any "special" characters
1818         wxCharBuffer buf = text->frmtd.ToUTF8();
1819 
1820         unsigned int n = text->frmtd.Length();
1821         for(unsigned int i=0 ; i < n ; ++i){
1822             unsigned char c =buf.data()[i];
1823             if(c > 127){
1824                 text->bspecial_char = true;
1825                 break;
1826             }
1827         }
1828 
1829     }
1830 
1831     return text;
1832 }
1833 
1834 static void rotate(wxRect *r, ViewPort const &vp)
1835 {
1836     float cx = vp.pix_width/2.;
1837     float cy = vp.pix_height/2.;
1838     float c = cosf(vp.rotation );
1839     float s = sinf(vp.rotation );
1840     float x = r->GetX() -cx;
1841     float y = r->GetY() -cy;
1842     r->SetX( x*c - y*s +cx);
1843     r->SetY( x*s + y*c +cy);
1844 }
1845 
1846 bool s52plib::RenderText( wxDC *pdc, S52_TextC *ptext, int x, int y, wxRect *pRectDrawn,
1847                           S57Obj *pobj, bool bCheckOverlap, ViewPort *vp )
1848 {
1849     #ifdef DrawText
1850     #undef DrawText
1851     #define FIXIT
1852     #endif
1853     bool bdraw = true;
1854 
1855     wxFont *scaled_font = ptext->pFont;
1856     wxCoord w_scaled = 0;
1857     wxCoord h_scaled = 0;
1858     wxCoord descent = 0;
1859     wxCoord exlead = 0;
1860 
1861     double sfactor = vp->ref_scale/vp->chart_scale;
1862     double scale_factor = wxMax((sfactor - g_overzoom_emphasis_base)  / 4., 1.);
1863 
1864     if(!g_oz_vector_scale || !vp->b_quilt)
1865         scale_factor = 1.0;
1866 
1867     //  Place an upper bound on the scaled text size
1868         scale_factor = wxMin(scale_factor, 4);
1869 
1870         if( !pdc ) // OpenGL
1871     {
1872 #ifdef ocpnUSE_GL
1873 
1874         bool b_force_no_texture = false;
1875         if(scale_factor > 1.){
1876             b_force_no_texture = true;
1877 
1878             int old_size = ptext->pFont->GetPointSize();
1879             int new_size = old_size * scale_factor;
1880             scaled_font = FindOrCreateFont_PlugIn( new_size, ptext->pFont->GetFamily(),
1881                                                            ptext->pFont->GetStyle(), ptext->pFont->GetWeight(), false,
1882                                                            ptext->pFont->GetFaceName() );
1883             wxScreenDC sdc;
1884             sdc.GetTextExtent( ptext->frmtd, &w_scaled, &h_scaled, &descent, &exlead, scaled_font ); // measure the text
1885 
1886 
1887             // Has font size changed?  If so, clear the cached bitmap, and rebuild it
1888             if( (h_scaled - descent) != ptext->rendered_char_height){
1889                 glDeleteTextures(1, (GLuint *)&ptext->texobj);
1890                 ptext->texobj = 0;
1891             }
1892 
1893             // We cannot get the font ascent value to remove the interline spacing from the font "height".
1894             // So we have to estimate based on conventional Arial metrics
1895             ptext->rendered_char_height = (h_scaled - descent) * 8 / 10;
1896 
1897         }
1898         // We render string with "special" characters the old, hard way, since we don't necessarily have the glyphs in our font,
1899         // or if we do we would need a hashmap to cache and extract them
1900         // And we also do this if the text is to be scaled up artificially.
1901         if( (ptext->bspecial_char) || b_force_no_texture) {
1902             if( !ptext->texobj ) // is texture ready?
1903             {
1904                 wxScreenDC sdc;
1905 
1906                 if(scale_factor <= 1.){
1907                     sdc.GetTextExtent( ptext->frmtd, &w_scaled, &h_scaled, &descent, &exlead, scaled_font ); // measure the text
1908 
1909                     // We cannot get the font ascent value to remove the interline spacing from the font "height".
1910                     // So we have to estimate based on conventional Arial metrics
1911                     ptext->rendered_char_height = (h_scaled - descent) * 8 / 10;
1912                 }
1913 
1914                 ptext->text_width = w_scaled;
1915                 ptext->text_height = h_scaled;
1916 
1917                 /* make power of 2 */
1918                 int tex_w, tex_h;
1919                 for(tex_w = 1; tex_w < ptext->text_width; tex_w *= 2);
1920                 for(tex_h = 1; tex_h < ptext->text_height; tex_h *= 2);
1921 
1922            wxMemoryDC mdc;
1923            wxBitmap bmp( tex_w, tex_h );
1924            mdc.SelectObject( bmp );
1925            mdc.SetFont( *( scaled_font ) );
1926 
1927            if( mdc.IsOk() ) {
1928                //  Render the text as white on black, so that underlying anti-aliasing of
1929                //  wxDC text rendering can be extracted and converted to alpha-channel values.
1930 
1931                mdc.SetBackground( wxBrush( wxColour( 0, 0, 0 ) ) );
1932                mdc.SetBackgroundMode( wxTRANSPARENT );
1933 
1934                mdc.SetTextForeground( wxColour( 255, 255, 255 ) );
1935 
1936                mdc.Clear();
1937 
1938                mdc.DrawText( ptext->frmtd, 0, 0 );
1939                mdc.SelectObject( wxNullBitmap );
1940 
1941                wxImage image = bmp.ConvertToImage();
1942                int ws = image.GetWidth(), hs = image.GetHeight();
1943 
1944                ptext->RGBA_width = ws;
1945                ptext->RGBA_height = hs;
1946                unsigned char *pRGBA = (unsigned char *) malloc( 4 * ws * hs );
1947 
1948                unsigned char *d = image.GetData();
1949                unsigned char *pdest = pRGBA;
1950                S52color *ccolor = ptext->pcol;
1951 
1952                if(d){
1953                    for( int y = 0; y < hs; y++ )
1954                        for( int x = 0; x < ws; x++ ) {
1955                            unsigned char r, g, b;
1956                            int off = ( y * ws + x );
1957 
1958                            r = d[off * 3 + 0];
1959                            g = d[off * 3 + 1];
1960                            b = d[off * 3 + 2];
1961 
1962                            pdest[off * 4 + 0] = ccolor->R;
1963                            pdest[off * 4 + 1] = ccolor->G;
1964                            pdest[off * 4 + 2] = ccolor->B;
1965 
1966                            int alpha = ( r + g + b ) / 3;
1967                            pdest[off * 4 + 3] = (unsigned char) ( alpha & 0xff );
1968                        }
1969                }
1970 
1971 
1972                int draw_width = ptext->RGBA_width;
1973                int draw_height = ptext->RGBA_height;
1974 
1975                glEnable( GL_TEXTURE_2D );
1976 
1977                GLuint texobj;
1978                glGenTextures( 1, &texobj );
1979 
1980                glBindTexture( GL_TEXTURE_2D, texobj );
1981 
1982                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
1983                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
1984                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST/*GL_LINEAR*/ );
1985                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1986 
1987                glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, draw_width, draw_height, 0,
1988                              GL_RGBA, GL_UNSIGNED_BYTE, pRGBA );
1989 
1990                free( pRGBA );
1991 
1992                ptext->texobj = texobj;
1993 
1994            } // mdc OK
1995             } // Building texobj
1996 
1997             //    Render the texture
1998             if( ptext->texobj ) {
1999                 int yadjust = 0;
2000                 int xadjust = 0;
2001 
2002                 //  Adjust the y position to account for the convention that S52 text is drawn
2003                 //  with the lower left corner at the specified point, instead of the wx convention
2004                 //  using upper left corner.
2005                 //  Also, allow for full text height in the bitmap/texture, not the estimated "rendered" height.
2006 
2007                 yadjust =  -ptext->rendered_char_height * 10 / 8;
2008 
2009                 //  Add in the offsets, specified in units of nominal font height
2010                 yadjust += ptext->yoffs * ( ptext->rendered_char_height );
2011                 //  X offset specified in units of average char width
2012                 xadjust += ptext->xoffs * ptext->avgCharWidth;
2013 
2014                 // adjust for text justification
2015                 int w = ptext->text_width;
2016                 switch ( ptext->hjust){
2017                     case '1':               // centered
2018                     xadjust -= w/2;
2019                     break;
2020                     case '2':               // right
2021                      xadjust -= w;
2022                      break;
2023                     case '3':               // left (default)
2024                     default:
2025                         break;
2026                 }
2027 
2028                 switch ( ptext->vjust){
2029                     case '3':               // top
2030                     yadjust += ptext->rendered_char_height;
2031                     break;
2032                     case '2':               // centered
2033                      yadjust += ptext->rendered_char_height/2;
2034                      break;
2035                     case '1':               // bottom (default)
2036                     default:
2037                         break;
2038                 }
2039 
2040                 int xp = x;
2041                 int yp = y;
2042 
2043                 if(fabs(vp->rotation) > 0.01){
2044                     float c = cosf(-vp->rotation );
2045                     float s = sinf(-vp->rotation );
2046                     float x = xadjust;
2047                     float y = yadjust;
2048                     xp += x*c - y*s;
2049                     yp += x*s + y*c;
2050 
2051                 }
2052                 else{
2053                     xp+= xadjust;
2054                     yp+= yadjust;
2055                 }
2056 
2057 
2058                 pRectDrawn->SetX( xp );
2059                 pRectDrawn->SetY( yp );
2060                 pRectDrawn->SetWidth( ptext->text_width );
2061                 pRectDrawn->SetHeight( ptext->text_height );
2062 
2063                 if( bCheckOverlap ) {
2064                     if( CheckTextRectList( *pRectDrawn, ptext ) )
2065                         bdraw = false;
2066                 }
2067 
2068                 if( bdraw ) {
2069 extern GLenum       g_texture_rectangle_format;
2070 #ifndef USE_ANDROID_GLES2
2071 
2072                     int draw_width = ptext->text_width;
2073                     int draw_height = ptext->text_height;
2074 
2075 
2076                     glEnable( GL_BLEND );
2077                     glEnable( GL_TEXTURE_2D );
2078 
2079                     glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
2080 
2081                     glBindTexture( GL_TEXTURE_2D, ptext->texobj );
2082 
2083                     glPushMatrix();
2084                     glTranslatef(xp, yp, 0);
2085 
2086                     /* undo previous rotation to make text level */
2087                     glRotatef(vp->rotation*180/PI, 0, 0, -1);
2088 
2089 
2090                     float tx1 = 0, tx2 = draw_width;
2091                     float ty1 = 0, ty2 = draw_height;
2092 
2093                     if(g_texture_rectangle_format == GL_TEXTURE_2D) {
2094 
2095                         tx1 /= ptext->RGBA_width, tx2 /= ptext->RGBA_width;
2096                         ty1 /= ptext->RGBA_height, ty2 /= ptext->RGBA_height;
2097                     }
2098 
2099 
2100                     glBegin( GL_QUADS );
2101 
2102                     glTexCoord2f( tx1, ty1 );  glVertex2i( 0, 0 );
2103                     glTexCoord2f( tx2, ty1 );  glVertex2i( draw_width, 0 );
2104                     glTexCoord2f( tx2, ty2 );  glVertex2i( draw_width, draw_height );
2105                     glTexCoord2f( tx1, ty2 );  glVertex2i( 0, draw_height );
2106 
2107                     glEnd();
2108 
2109                     glPopMatrix();
2110 
2111                     glDisable( g_texture_rectangle_format );
2112                     glDisable( GL_BLEND );
2113 #else
2114                     glEnable( GL_BLEND );
2115                     glEnable( GL_TEXTURE_2D );
2116 
2117                     float uv[8];
2118                     float coords[8];
2119 
2120                     // Note swizzle of points to allow TRIANGLE_STRIP drawing
2121                     //normal uv
2122                     uv[0] = 0; uv[1] = 0; uv[2] = 1; uv[3] = 0;
2123                     uv[6] = 1; uv[7] = 1; uv[4] = 0; uv[5] = 1;
2124 
2125                     //w *= scale_factor;
2126                     //h *= scale_factor;
2127 
2128                     // pixels
2129                     coords[0] = 0; coords[1] = 0; coords[2] = ptext->RGBA_width; coords[3] = 0;
2130                     coords[6] = ptext->RGBA_width; coords[7] = ptext->RGBA_height; coords[4] = 0; coords[5] = ptext->RGBA_height;
2131 
2132                     glUseProgram( S52texture_2D_shader_program );
2133 
2134                     // Get pointers to the attributes in the program.
2135                     GLint mPosAttrib = glGetAttribLocation( S52texture_2D_shader_program, "position" );
2136                     GLint mUvAttrib  = glGetAttribLocation( S52texture_2D_shader_program, "aUV" );
2137 
2138                     // Select the active texture unit.
2139                     glActiveTexture( GL_TEXTURE0 );
2140 
2141                     // Bind our texture to the texturing target.
2142                     glBindTexture( GL_TEXTURE_2D, ptext->texobj );
2143 
2144                     // Set up the texture sampler to texture unit 0
2145                     GLint texUni = glGetUniformLocation( S52texture_2D_shader_program, "uTex" );
2146                     glUniform1i( texUni, 0 );
2147 
2148                     // Disable VBO's (vertex buffer objects) for attributes.
2149                     glBindBuffer( GL_ARRAY_BUFFER, 0 );
2150                     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
2151 
2152                     // Set the attribute mPosAttrib with the vertices in the screen coordinates...
2153                     glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
2154                     // ... and enable it.
2155                     glEnableVertexAttribArray( mPosAttrib );
2156 
2157                     // Set the attribute mUvAttrib with the vertices in the GL coordinates...
2158                     glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
2159                     // ... and enable it.
2160                     glEnableVertexAttribArray( mUvAttrib );
2161 
2162                     // Rotate
2163                     mat4x4 I, Q;
2164                     mat4x4_identity(I);
2165 
2166                     mat4x4_translate_in_place(I, x, y, 0);
2167                     mat4x4_rotate_Z(Q, I, -vp->rotation);
2168                     mat4x4_translate_in_place(Q, xadjust, yadjust, 0);
2169 
2170 
2171                     GLint matloc = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix");
2172                     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
2173 
2174                     // Perform the actual drawing.
2175                     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
2176 
2177                     // Restore the per-object transform to Identity Matrix
2178                     mat4x4 IM;
2179                     mat4x4_identity(IM);
2180                     GLint matlocf = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix");
2181                     glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
2182 
2183                     glDisable( GL_TEXTURE_2D );
2184                     glDisable( GL_BLEND );
2185 #endif
2186 
2187 
2188                 } // bdraw
2189 
2190             }
2191 
2192             bdraw = true;
2193         }
2194 
2195         else {                                          // render using cached texture glyphs
2196             // rebuild font if needed
2197             TexFont *f_cache = 0;
2198             unsigned int i;
2199             for (i = 0; i < TXF_CACHE; i++)
2200             {
2201                 if (s_txf[i].key == ptext->pFont) {
2202                     f_cache = &s_txf[i].cache;
2203                     break;
2204                 }
2205                 if (s_txf[i].key == 0) {
2206                     break;
2207                 }
2208             }
2209             if (i == TXF_CACHE) {
2210                 i = rand() & (TXF_CACHE -1);
2211             }
2212             if (f_cache == 0) {
2213                 s_txf[i].key = ptext->pFont;
2214                 f_cache = &s_txf[i].cache;
2215                 f_cache->Build(*ptext->pFont);
2216             }
2217 
2218             int w, h;
2219             f_cache->GetTextExtent(ptext->frmtd, &w, &h);
2220 
2221             // We don't store descent/ascent info for font texture cache
2222             // So we have to estimate based on conventional Arial metrics
2223             ptext->rendered_char_height = h * 65 / 100;
2224 
2225             //  Adjust the y position to account for the convention that S52 text is drawn
2226             //  with the lower left corner at the specified point, instead of the wx convention
2227             //  using upper right corner
2228             int yadjust = 0;
2229             int xadjust = 0;
2230 
2231             yadjust =  -ptext->rendered_char_height;
2232 
2233 
2234             //  Add in the offsets, specified in units of nominal font height
2235             yadjust += ptext->yoffs * ( ptext->rendered_char_height );
2236             //  X offset specified in units of average char width
2237             xadjust += ptext->xoffs * ptext->avgCharWidth;
2238 
2239             // adjust for text justification
2240             switch ( ptext->hjust){
2241                 case '1':               // centered
2242                     xadjust -= w/2;
2243                     break;
2244                 case '2':               // right
2245                      xadjust -= w;
2246                      break;
2247                 case '3':               // left (default)
2248                 default:
2249                     break;
2250             }
2251 
2252             switch ( ptext->vjust){
2253                 case '3':               // top
2254                     yadjust += ptext->rendered_char_height;
2255                     break;
2256                 case '2':               // centered
2257                      yadjust += ptext->rendered_char_height/2;
2258                      break;
2259                 case '1':               // bottom (default)
2260                 default:
2261                     break;
2262             }
2263 
2264             int xp = x;
2265             int yp = y;
2266 
2267             if(fabs(vp->rotation) > 0.01){
2268                 float c = cosf(-vp->rotation );
2269                 float s = sinf(-vp->rotation );
2270                 float x = xadjust;
2271                 float y = yadjust;
2272                 xadjust =  x*c - y*s;
2273                 yadjust =  x*s + y*c;
2274             }
2275 
2276             xp+= xadjust;
2277             yp+= yadjust;
2278 
2279             pRectDrawn->SetX( xp );
2280             pRectDrawn->SetY( yp );
2281             pRectDrawn->SetWidth( w );
2282             pRectDrawn->SetHeight( h );
2283 
2284             if( bCheckOverlap ) {
2285                 if(fabs( vp->rotation ) > .01){
2286                     rotate(pRectDrawn, *vp );
2287                 }
2288                 if( CheckTextRectList( *pRectDrawn, ptext ) ) bdraw = false;
2289             }
2290 
2291             if( bdraw ) {
2292 #ifndef USE_ANDROID_GLES2
2293                 wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts"));
2294                 if( wcolor == *wxBLACK )
2295                     glColor3ub( ptext->pcol->R, ptext->pcol->G, ptext->pcol->B );
2296                 else
2297                     glColor3ub( wcolor.Red(), wcolor.Green(), wcolor.Blue() );
2298 
2299                 glEnable( GL_BLEND );
2300                 glEnable( GL_TEXTURE_2D );
2301                 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
2302 
2303                 glPushMatrix();
2304                 glTranslatef(xp, yp, 0);
2305 
2306                 /* undo previous rotation to make text level */
2307                 glRotatef(vp->rotation*180/PI, 0, 0, -1);
2308 
2309                 f_cache->RenderString(ptext->frmtd);
2310                 glPopMatrix();
2311 
2312                 glDisable( GL_TEXTURE_2D );
2313                 glDisable( GL_BLEND );
2314 #else
2315                 glEnable( GL_BLEND );
2316                 glEnable( GL_TEXTURE_2D );
2317 
2318                 wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts"));
2319                 f_cache->SetColor( wcolor );
2320 
2321                 /* undo previous rotation to make text level */
2322                 //glRotatef(vp->rotation*180/PI, 0, 0, -1);
2323 
2324                 f_cache->RenderString(ptext->frmtd, xp, yp);
2325 
2326                 glDisable( GL_TEXTURE_2D );
2327                 glDisable( GL_BLEND );
2328 #endif
2329             }
2330         }
2331 
2332         #endif
2333     } else {                // Not OpenGL
2334             wxFont oldfont = pdc->GetFont(); // save current font
2335 
2336 
2337             if(scale_factor > 1){
2338                 wxFont *pf = ptext->pFont;
2339                 int old_size = pf->GetPointSize();
2340                 int new_size = old_size * scale_factor;
2341                 wxFont *scaled_font = FindOrCreateFont_PlugIn( new_size, pf->GetFamily(),
2342                                                                        pf->GetStyle(), pf->GetWeight(), false,
2343                                                                        pf->GetFaceName() );
2344                 pdc->SetFont( *scaled_font);
2345             }
2346             else{
2347                 pdc->SetFont( *( ptext->pFont ) );
2348             }
2349 
2350             wxCoord w, h, descent, exlead;
2351             pdc->GetTextExtent( ptext->frmtd, &w, &h, &descent, &exlead ); // measure the text
2352 
2353             // We cannot get the font ascent value to remove the interline spacing.
2354             // So we have to estimate based on conventional Arial metrics
2355             int rendered_text_height = (h - descent) * 8 / 10;
2356 
2357             //  Adjust the y position to account for the convention that S52 text is drawn
2358             //  with the lower left corner at the specified point, instead of the wx convention
2359             //  using upper right corner
2360             int yadjust = 0;
2361             int xadjust = 0;
2362 
2363             yadjust =  - ( rendered_text_height );
2364 
2365             //  Add in the offsets, specified in units of nominal font height
2366             yadjust += ptext->yoffs * ( rendered_text_height );
2367 
2368             //  X offset specified in units of average char width
2369             xadjust += ptext->xoffs * ptext->avgCharWidth;
2370 
2371             // adjust for text justification
2372             switch ( ptext->hjust){
2373                 case '1':               // centered
2374                     xadjust -= w/2;
2375                     break;
2376                 case '2':               // right
2377                      xadjust -= w;
2378                      break;
2379                 case '3':               // left (default)
2380                 default:
2381                     break;
2382             }
2383 
2384             switch ( ptext->vjust){
2385                 case '3':               // top
2386                     yadjust += rendered_text_height;
2387                     break;
2388                 case '2':               // centered
2389                      yadjust += rendered_text_height/2;
2390                      break;
2391                 case '1':               // bottom (default)
2392                 default:
2393                     break;
2394             }
2395 
2396             int xp = x;
2397             int yp = y;
2398 
2399 
2400             if(fabs(vp->rotation) > 0.01){
2401                 float cx = vp->pix_width/2.;
2402                 float cy = vp->pix_height/2.;
2403                 float c = cosf(vp->rotation );
2404                 float s = sinf(vp->rotation );
2405                 float x = xp -cx;
2406                 float y = yp -cy;
2407                 xp =  x*c - y*s +cx + vp->rv_rect.x;
2408                 yp =  x*s + y*c +cy + vp->rv_rect.y;
2409 
2410             }
2411 
2412             xp+= xadjust;
2413             yp+= yadjust;
2414 
2415             pRectDrawn->SetX( xp );
2416             pRectDrawn->SetY( yp );
2417             pRectDrawn->SetWidth( w );
2418             pRectDrawn->SetHeight( h );
2419 
2420             if( bCheckOverlap ) {
2421                 if( CheckTextRectList( *pRectDrawn, ptext ) )
2422                     bdraw = false;
2423             }
2424 
2425             if( bdraw ) {
2426                 wxColour wcolor = GetFontColour_PlugIn(_("ChartTexts"));
2427 
2428                 // If the user has not changed the color from BLACK, then use the color specified in the S52 LUP
2429                 if( wcolor == *wxBLACK )
2430                     wcolor = wxColour( ptext->pcol->R, ptext->pcol->G, ptext->pcol->B );
2431                 pdc->SetTextForeground( wcolor );
2432 
2433                 pdc->DrawText( ptext->frmtd, xp, yp );
2434             }
2435 
2436             pdc->SetFont( oldfont ); // restore last font
2437 
2438     }
2439     return bdraw;
2440 
2441 
2442     #ifdef FIXIT
2443     #undef FIXIT
2444     #define DrawText DrawTextA
2445     #endif
2446 
2447 }
2448 
2449 
2450 
2451 //    Return true if test_rect overlaps any rect in the current text rectangle list, except itself
2452 bool s52plib::CheckTextRectList( const wxRect &test_rect, S52_TextC *ptext )
2453 {
2454     //    Iterate over the current object list, looking at rText
2455 
2456     for( TextObjList::Node *node = m_textObjList.GetFirst(); node; node = node->GetNext() ) {
2457         wxRect *pcurrent_rect = &( node->GetData()->rText );
2458 
2459         if( pcurrent_rect->Intersects( test_rect ) ) {
2460             if( node->GetData() != ptext )
2461                 return true;
2462 
2463         }
2464     }
2465     return false;
2466 }
2467 
2468 bool s52plib::TextRenderCheck( ObjRazRules *rzRules )
2469 {
2470     if( !m_bShowS57Text ) return false;
2471 
2472     if(rzRules->obj->bIsAton ){
2473 
2474        if( !strncmp( rzRules->obj->FeatureName, "LIGHTS", 6 ) ) {
2475            if( !m_bShowLdisText )
2476                return false;
2477        }
2478        else{
2479            if( !m_bShowAtonText )
2480                return false;
2481        }
2482     }
2483 
2484 
2485 
2486     // Declutter LIGHTS descriptions
2487     if( ( rzRules->obj->bIsAton ) && ( !strncmp( rzRules->obj->FeatureName, "LIGHTS", 6 ) ) ){
2488         if( lastLightLat == rzRules->obj->m_lat && lastLightLon == rzRules->obj->m_lon ){
2489             return false;       // only render text for the first object at this lat/lon
2490         }
2491         else{
2492             lastLightLat = rzRules->obj->m_lat;
2493             lastLightLon = rzRules->obj->m_lon;
2494         }
2495     }
2496 
2497     //    An optimization for CM93 charts.
2498     //    Don't show the text associated with some objects, since CM93 database includes _texto objects aplenty
2499     if( ( (int)rzRules->obj->auxParm3 == (int)PI_CHART_TYPE_CM93 )
2500         || ( (int)rzRules->obj->auxParm3 == (int)PI_CHART_TYPE_CM93COMP ) ) {
2501         if( !strncmp( rzRules->obj->FeatureName, "BUAARE", 6 ) )
2502             return false;
2503         else if( !strncmp( rzRules->obj->FeatureName, "SEAARE", 6 ) )
2504             return false;
2505         else if( !strncmp( rzRules->obj->FeatureName, "LNDRGN", 6 ) )
2506             return false;
2507         else if( !strncmp( rzRules->obj->FeatureName, "LNDARE", 6 ) )
2508             return false;
2509         }
2510 
2511     return true;
2512 }
2513 
2514 int s52plib::RenderT_All( ObjRazRules *rzRules, Rules *rules, ViewPort *vp, bool bTX )
2515 {
2516     if( !TextRenderCheck( rzRules ) ) return 0;
2517 
2518     S52_TextC *text = NULL;
2519     bool b_free_text = false;
2520 
2521     //  The first Ftext object is cached in the S57Obj.
2522     //  If not present, create it on demand
2523     if( !rzRules->obj->bFText_Added ) {
2524         if( bTX ) text = S52_PL_parseTX( rzRules, rules, NULL );
2525         else
2526             text = S52_PL_parseTE( rzRules, rules, NULL );
2527 
2528         if( text ) {
2529             rzRules->obj->bFText_Added = true;
2530             rzRules->obj->FText = text;
2531             rzRules->obj->FText->rul_seq_creator = rules->n_sequence;
2532         }
2533     }
2534 
2535     //    S57Obj already contains a cached text object
2536     //    If it was created by this Rule earlier, then render it
2537     //    Otherwise, create a new text object, render it, and delete when done
2538     //    This will be slower, obviously, but happens infrequently enough?
2539     else {
2540         if( rules->n_sequence == rzRules->obj->FText->rul_seq_creator ) text = rzRules->obj->FText;
2541         else {
2542             if( bTX ) text = S52_PL_parseTX( rzRules, rules, NULL );
2543             else
2544                 text = S52_PL_parseTE( rzRules, rules, NULL );
2545 
2546             b_free_text = true;
2547         }
2548 
2549     }
2550 
2551     if( text ) {
2552         if( m_bShowS57ImportantTextOnly && ( text->dis >= 20 ) ) {
2553             if( b_free_text ) delete text;
2554             return 0;
2555         }
2556 
2557         //    Establish a font
2558         if( !text->pFont ) {
2559 
2560             // Process the font specifications from the LUP symbolizatio rule
2561             int spec_weight = text->weight - 0x30;
2562             wxFontWeight fontweight;
2563             if( spec_weight < 5 )
2564                 fontweight = wxFONTWEIGHT_LIGHT;
2565             else{
2566                 if( spec_weight == 5 )
2567                     fontweight = wxFONTWEIGHT_NORMAL;
2568                 else
2569                     fontweight = wxFONTWEIGHT_BOLD;
2570             }
2571 
2572             wxFont *specFont = FindOrCreateFont_PlugIn( text->bsize, wxFONTFAMILY_SWISS,  wxFONTSTYLE_NORMAL, fontweight );
2573 
2574             //Get the width of a single average character in the spec font
2575             wxScreenDC dc;
2576             dc.SetFont(*specFont);
2577             int width;
2578             dc.GetTextExtent(_T("X"), &width, NULL, NULL, NULL, specFont);
2579             text->avgCharWidth = width;
2580 
2581             //    If we have loaded a legacy S52 compliant PLIB,
2582             //    then we should use the formal font selection as required by S52 specifications.
2583             //    Otherwise, we have our own plan...
2584 
2585             if( useLegacyRaster ) {
2586                 text->pFont = specFont;
2587             } else {
2588                 int spec_weight = text->weight - 0x30;
2589                 wxFontWeight fontweight;
2590                 if( spec_weight < 5 ) fontweight = wxFONTWEIGHT_LIGHT;
2591                 else
2592                     if( spec_weight == 5 ) fontweight = wxFONTWEIGHT_NORMAL;
2593                     else
2594                         fontweight = wxFONTWEIGHT_BOLD;
2595 
2596                 wxFont sys_font = *wxNORMAL_FONT;
2597                 int default_size = sys_font.GetPointSize();
2598 
2599 #ifdef __WXOSX__
2600                 default_size += 1;     // default to 1pt larger than system UI font
2601 #else
2602                 default_size += 2;     // default to 2pt larger than system UI font
2603 #endif
2604 
2605                 wxFont *templateFont = GetOCPNScaledFont_PlugIn(_("ChartTexts"), default_size );
2606 
2607                 // NOAA ENC fles requests font size up to 20 points, which looks very
2608                 // disproportioned. Let's scale those sizes down to more reasonable values.
2609                 int fontSize = text->bsize;
2610 
2611                 if( fontSize > 18 ) fontSize -= 8;
2612                 else
2613                     if( fontSize > 13 ) fontSize -= 3;
2614 
2615                 // Now factor in the users selected font size.
2616                 fontSize += templateFont->GetPointSize() - 10;
2617 
2618                 // In no case should font size be less than 10, since it becomes unreadable
2619                 fontSize = wxMax(10, fontSize);
2620 
2621                 text->pFont = FindOrCreateFont_PlugIn( fontSize, wxFONTFAMILY_SWISS,
2622                         templateFont->GetStyle(), fontweight, false, templateFont->GetFaceName() );
2623             }
2624         }
2625 
2626         //  Render text at declared x/y of object
2627         wxPoint r;
2628         GetPointPixSingle( rzRules, rzRules->obj->y, rzRules->obj->x, &r, vp );
2629 
2630         wxRect rect;
2631         bool bwas_drawn = RenderText( m_pdc, text, r.x, r.y, &rect, rzRules->obj, m_bDeClutterText, vp );
2632 
2633         //  If this is an un-cached text render, it probably means that a single object has two or more
2634         //  text renders in its rule set.  RDOCAL is one example.  There are others
2635         //  We need to cache only the first text structure, but should update the render rectangle
2636         //  to reflect all texts rendered for this object,  in order to process the declutter logic.
2637         bool b_dupok = false;
2638         if( b_free_text ) {
2639             delete text;
2640 
2641             if(!bwas_drawn){
2642                 return 1;
2643             }
2644             else{                               // object was drawn
2645                 text = rzRules->obj->FText;
2646 
2647                 wxRect r0 = text->rText;
2648                 r0 = r0.Union(rect);
2649                 text->rText = r0;
2650 
2651                 b_dupok = true;                 // OK to add a duplicate text structure to the declutter list, just for this case.
2652             }
2653         }
2654         else
2655             text->rText = rect;
2656 
2657 
2658         //      If this text was actually drawn, add a pointer to its rect to the de-clutter list if it doesn't already exist
2659         if( m_bDeClutterText ) {
2660             if( bwas_drawn ) {
2661                 bool b_found = false;
2662                 for( TextObjList::Node *node = m_textObjList.GetFirst(); node; node =  node->GetNext() ) {
2663                     S52_TextC *oc = node->GetData();
2664 
2665                     if( oc == text ) {
2666                         if(!b_dupok)
2667                             b_found = true;
2668                         break;
2669                     }
2670                 }
2671                 if( !b_found )
2672                     m_textObjList.Append( text );
2673             }
2674         }
2675 
2676         //  Update the object Bounding box
2677         //  so that subsequent drawing operations will redraw the item fully
2678         //  and so that cursor hit testing includes both the text and the object
2679 
2680 //            if ( rzRules->obj->Primitive_type == GEO_POINT )
2681         {
2682             double latmin, lonmin, latmax, lonmax;
2683 
2684             GetPixPointSingleNoRotate( rect.GetX(), rect.GetY() + rect.GetHeight(), &latmin, &lonmin, vp );
2685             GetPixPointSingleNoRotate( rect.GetX() + rect.GetWidth(), rect.GetY(), &latmax, &lonmax, vp );
2686             LLBBox bbtext;
2687             bbtext.Set( latmin, lonmin, latmax, lonmax );
2688 
2689             rzRules->obj->BBObj.Expand( bbtext );
2690         }
2691     }
2692 
2693     return 1;
2694 }
2695 
2696 // Text
2697 int s52plib::RenderTX( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
2698 {
2699     return RenderT_All( rzRules, rules, vp, true );
2700 }
2701 
2702 // Text formatted
2703 int s52plib::RenderTE( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
2704 {
2705     return RenderT_All( rzRules, rules, vp, false );
2706 }
2707 
2708 bool s52plib::RenderHPGL( ObjRazRules *rzRules, Rule *prule, wxPoint &r, ViewPort *vp, float rot_angle )
2709 {
2710     float fsf = 100 / canvas_pix_per_mm;
2711 
2712 
2713     float xscale = 1.0;
2714     //  Set the onscreen size of the symbol
2715     //  Compensate for various display resolutions
2716     //  Develop empirically, making a flare light about 6 mm long
2717     double pix_factor = GetPPMM() / 6.0;
2718     xscale *= pix_factor;
2719 
2720 
2721     if( (!strncmp(rzRules->obj->FeatureName, "TSSLPT", 6))
2722         || (!strncmp(rzRules->obj->FeatureName, "DWRTPT", 6))
2723         || (!strncmp(rzRules->obj->FeatureName, "TWRTPT", 6))
2724         || (!strncmp(rzRules->obj->FeatureName, "RCTLPT", 6))
2725     ){
2726         // assume the symbol length
2727         float sym_length = 30;
2728         float scaled_length = sym_length / vp->view_scale_ppm;
2729         float target_length = 1852;
2730 
2731         xscale = target_length / scaled_length;
2732         xscale = wxMin(xscale, 1.0);
2733         xscale = wxMax(.4, xscale);
2734 
2735         //printf("scaled length: %g   xscale: %g\n", scaled_length, xscale);
2736 
2737 
2738         fsf *= xscale;
2739     }
2740 
2741     xscale *= g_ChartScaleFactorExp;
2742 
2743     //  Special case for GEO_AREA objects with centred symbols
2744     if( rzRules->obj->Primitive_type == GEO_AREA ) {
2745         wxPoint r;
2746         GetPointPixSingle( rzRules, rzRules->obj->y, rzRules->obj->x, &r, vp );
2747 
2748         double latdraw, londraw;                // position of the drawn symbol with pivot applied
2749         GetPixPointSingleNoRotate( r.x + ((prule->pos.symb.pivot_x.SYCL - prule->pos.symb.bnbox_x.SBXC) / fsf),
2750                                    r.y + ((prule->pos.symb.pivot_y.SYRW - prule->pos.symb.bnbox_y.SBXR) / fsf),
2751                                    &latdraw, &londraw, vp );
2752 
2753         if( !rzRules->obj->BBObj.Contains( latdraw, londraw ) ) // Symbol reference point is outside base area object
2754              return 1;
2755     }
2756 
2757     double render_angle = rot_angle;
2758 
2759     //  Very special case for ATON flare lights at 135 degrees, the standard render angle.
2760     //  We don't want them to rotate with the viewport.
2761     if(rzRules->obj->bIsAton && (!strncmp(rzRules->obj->FeatureName, "LIGHTS", 6))  && (fabs(rot_angle - 135.0) < 1.) ){
2762         render_angle -= vp->rotation * 180./PI;
2763 
2764         //  And, due to popular request, we make the flare lights a little bit smaller than S52 specifications
2765         xscale = xscale * 6. / 7.;
2766     }
2767 
2768     int width = prule->pos.symb.bnbox_x.SBXC + prule->pos.symb.bnbox_w.SYHL;
2769     width *= 4; // Grow the drawing bitmap to allow for rotation of symbols with highly offset pivot points
2770     width = (int) ( width / fsf );
2771 
2772     int height = prule->pos.symb.bnbox_y.SBXR + prule->pos.symb.bnbox_h.SYVL;
2773     height *= 4;
2774     height = (int) ( height / fsf );
2775 
2776     int origin_x = prule->pos.symb.bnbox_x.SBXC;
2777     int origin_y = prule->pos.symb.bnbox_y.SBXR;
2778     wxPoint origin(origin_x, origin_y);
2779 
2780     int pivot_x = prule->pos.symb.pivot_x.SYCL;
2781     int pivot_y = prule->pos.symb.pivot_y.SYRW;
2782     wxPoint pivot( pivot_x, pivot_y );
2783 
2784     char *str = prule->vector.LVCT;
2785     char *col = prule->colRef.LCRF;
2786     wxPoint r0( (int) ( pivot_x / fsf ), (int) ( pivot_y / fsf ) );
2787 
2788     HPGL->SetVP(vp);
2789 
2790     if( !m_pdc ) { // OpenGL Mode, do a direct render
2791         HPGL->SetTargetOpenGl();
2792         HPGL->Render( str, col, r, pivot, origin, xscale, render_angle, true );
2793 
2794         //  Update the object Bounding box
2795         //  so that subsequent drawing operations will redraw the item fully
2796 
2797         int r_width = prule->pos.symb.bnbox_w.SYHL;
2798         r_width = (int) ( r_width / fsf );
2799         int r_height = prule->pos.symb.bnbox_h.SYVL;
2800         r_height = (int) ( r_height / fsf );
2801         int maxDim = wxMax(r_height, r_width);
2802 
2803         double latmin, lonmin, latmax, lonmax;
2804         GetPixPointSingleNoRotate( r.x - maxDim, r.y + maxDim, &latmin, &lonmin, vp );
2805         GetPixPointSingleNoRotate( r.x + maxDim, r.y - maxDim, &latmax,  &lonmax, vp );
2806         LLBBox symbox;
2807         symbox.Set( latmin, lonmin, latmax, lonmax );
2808 
2809         rzRules->obj->BBObj.Expand( symbox );
2810 
2811     } else {
2812 
2813 #if (( defined(__WXGTK__) || defined(__WXMAC__) ) && !wxCHECK_VERSION(2,9,4))
2814         wxBitmap *pbm = new wxBitmap( width, height );
2815 #else
2816         wxBitmap *pbm = new wxBitmap( width, height, 32 );
2817 # if !wxCHECK_VERSION(2,9,4)
2818         pbm->UseAlpha();
2819 # endif
2820 #endif
2821         wxMemoryDC mdc( *pbm );
2822         if(!mdc.IsOk()){
2823             wxString msg;
2824             msg.Printf(_T("RenderHPGL: width %d  height %d"), width, height);
2825             wxLogMessage(msg);
2826             return false;
2827         }
2828 
2829 #if wxUSE_GRAPHICS_CONTEXT
2830         wxGCDC gdc( mdc );
2831         HPGL->SetTargetGCDC( &gdc );
2832 #else
2833         wxMemoryDC &gdc( mdc );
2834         HPGL->SetTargetDC( &gdc );
2835 #endif
2836         HPGL->Render( str, col, r0, pivot, origin, xscale, (double) rot_angle, true );
2837 
2838         int bm_width = ( gdc.MaxX() - gdc.MinX() ) + 4;
2839         int bm_height = ( gdc.MaxY() - gdc.MinY() ) + 4;
2840         int bm_orgx = wxMax ( 0, gdc.MinX()-2 );
2841         int bm_orgy = wxMax ( 0, gdc.MinY()-2 );
2842         int screenOriginX = r.x + ( bm_orgx - (int) ( pivot_x / fsf ) );
2843         int screenOriginY = r.y + ( bm_orgy - (int) ( pivot_y / fsf ) );
2844 
2845         //      Pre-clip the sub-bitmap to avoid assert errors
2846         if( ( bm_height + bm_orgy ) > height ) bm_height = height - bm_orgy;
2847         if( ( bm_width + bm_orgx ) > width ) bm_width = width - bm_orgx;
2848 
2849         mdc.SelectObject( wxNullBitmap );
2850 
2851         //  Grab a copy of the existing screen DC rectangle
2852         wxBitmap targetBm( bm_width, bm_height, 24 );
2853         wxMemoryDC targetDc( targetBm );
2854         if(!targetDc.IsOk())
2855             return false;
2856         targetDc.Blit( 0, 0, bm_width, bm_height, m_pdc, screenOriginX, screenOriginY );
2857 
2858 
2859 #if wxUSE_GRAPHICS_CONTEXT /*&& (( defined(__WXGTK__) || defined(__WXMAC__) ) && !wxCHECK_VERSION(2,9,4))*/
2860         //  Re-render onto the screen-grab copy, since wxDC::DrawBitmap() for alpha channel bitmaps is broken somehow in wxGCDC
2861         wxGCDC targetGcdc( targetDc );
2862         r0 -= wxPoint( bm_orgx, bm_orgy );
2863         HPGL->SetTargetGCDC( &targetGcdc );
2864         HPGL->Render( str, col, r0, pivot,origin, xscale, (double) rot_angle, true );
2865 #else
2866         //  We can use the bitmap already rendered
2867         //  Get smallest containing bitmap
2868         wxBitmap *sbm = new wxBitmap( pbm->GetSubBitmap( wxRect( bm_orgx, bm_orgy, bm_width, bm_height ) ) );
2869 
2870         //  render the symbol graphics onto the screen-grab copy, with transparency...
2871         targetDc.DrawBitmap( *sbm, 0, 0 );
2872         delete sbm;
2873 #endif
2874 
2875         //  Render the final bitmap onto the screen DC
2876         m_pdc->Blit( screenOriginX, screenOriginY, bm_width, bm_height, &targetDc, 0, 0 );
2877 
2878         // Clean up
2879         delete pbm;
2880         targetDc.SelectObject( wxNullBitmap );
2881 
2882         //  Update the object Bounding box
2883         //  so that subsequent drawing operations will redraw the item fully
2884 
2885         double latmin, lonmin, latmax, lonmax;
2886         GetPixPointSingleNoRotate( r.x + prule->parm2, r.y + prule->parm3 + bm_height, &latmin, &lonmin, vp );
2887         GetPixPointSingleNoRotate( r.x + prule->parm2 + bm_width, r.y + prule->parm3, &latmax,  &lonmax, vp );
2888         LLBBox symbox;
2889         symbox.Set( latmin, lonmin, latmax, lonmax );
2890 
2891         rzRules->obj->BBObj.Expand( symbox );
2892     }
2893 
2894     return true;
2895 }
2896 
2897 //-----------------------------------------------------------------------------------------
2898 //      Instantiate a Symbol or Pattern stored as XBM ascii in a rule
2899 //      Producing a wxImage
2900 //-----------------------------------------------------------------------------------------
2901 wxImage s52plib::RuleXBMToImage( Rule *prule )
2902 {
2903     //      Decode the color definitions
2904     wxArrayPtrVoid *pColorArray = new wxArrayPtrVoid;
2905 
2906     int i = 0;
2907     char *cstr = prule->colRef.SCRF;
2908 
2909     char colname[6];
2910     int nl = strlen( cstr );
2911 
2912     while( i < nl ) {
2913         i++;
2914 
2915         strncpy( colname, &cstr[i], 5 );
2916         colname[5] = 0;
2917         S52color *pColor = getColor( colname );
2918 
2919         pColorArray->Add( (void *) pColor );
2920 
2921         i += 5;
2922     }
2923 
2924     //      Get geometry
2925     int width = prule->pos.line.bnbox_w.SYHL;
2926     int height = prule->pos.line.bnbox_h.SYVL;
2927 
2928     wxString gstr( *prule->bitmap.SBTM ); // the bit array
2929 
2930     wxImage Image( width, height );
2931 
2932     for( int iy = 0; iy < height; iy++ ) {
2933         wxString thisrow = gstr( iy * width, width ); // extract a row
2934 
2935         for( int ix = 0; ix < width; ix++ ) {
2936             int cref = (int) ( thisrow[ix] - 'A' ); // make an index
2937             if( cref >= 0 ) {
2938                 S52color *pthisbitcolor = (S52color *) ( pColorArray->Item( cref ) );
2939                 Image.SetRGB( ix, iy, pthisbitcolor->R, pthisbitcolor->G, pthisbitcolor->B );
2940             } else {
2941                 Image.SetRGB( ix, iy, m_unused_color.R, m_unused_color.G, m_unused_color.B );
2942             }
2943 
2944         }
2945     }
2946 
2947     pColorArray->Clear();
2948     delete pColorArray;
2949     return Image;
2950 }
2951 
2952 //
2953 //      Render Raster Symbol
2954 //      Symbol is instantiated as a bitmap the first time it is needed
2955 //      and re-built on color scheme change
2956 //
2957 bool s52plib::RenderRasterSymbol( ObjRazRules *rzRules, Rule *prule, wxPoint &r, ViewPort *vp,
2958                                   float rot_angle )
2959 {
2960     double scale_factor = 1.0;
2961 
2962     scale_factor *=  g_ChartScaleFactorExp;
2963     scale_factor *= g_scaminScale;
2964 
2965     if(m_display_size_mm < 200){                //about 8 inches, implying some sort of smaller mobile device
2966         //  Set the onscreen size of the symbol
2967         //  Compensate for various display resolutions
2968         //  Develop empirically, making a buoy about 4 mm tall
2969         double boyHeight = 21. / GetPPMM();           // from raster symbol definitions, boylat is xx pix high
2970 
2971         double targetHeight0 = 4.0;
2972 
2973         // But we want to scale the size for smaller displays
2974         double displaySize = m_display_size_mm;
2975         displaySize = wxMax(displaySize, 100);
2976 
2977         float targetHeight = wxMin(targetHeight0, displaySize / 30);
2978 
2979         double pix_factor = targetHeight / boyHeight;
2980 
2981         //qDebug() << "scaleing" << m_display_size_mm  << targetHeight0 << targetHeight << GetPPMM() << boyHeight << pix_factor;
2982 
2983         // for Hubert, and my moto
2984         //scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082
2985         // My nvidia tab
2986         //scaleing 144.78 144 4 4 12.6667 1.65789 2.4127
2987         // judgement: all OK
2988 
2989 
2990         scale_factor *= pix_factor;
2991     }
2992 
2993     if(g_oz_vector_scale && vp->b_quilt){
2994         double sfactor = vp->ref_scale/vp->chart_scale;
2995         scale_factor = wxMax((sfactor - g_overzoom_emphasis_base)  / 4., scale_factor);
2996         scale_factor = wxMin(scale_factor, 20);
2997     }
2998 
2999     // a few special cases here
3000     if( !strncmp(rzRules->obj->FeatureName, "notmrk", 6 )
3001         || !strncmp(rzRules->obj->FeatureName, "NOTMRK", 6)
3002         || !strncmp(prule->name.SYNM, "ADDMRK", 6)
3003         )
3004     {
3005         // get the symbol size
3006         wxRect trect;
3007         ChartSymbols::GetGLTextureRect( trect, prule->name.SYNM );
3008 
3009         int scale_dim = wxMax(trect.width, trect.height);
3010 
3011         double scaled_size = scale_dim / vp->view_scale_ppm;
3012 
3013         double target_size = 100;               // roughly, meters maximum scaled size for these inland signs
3014 
3015         double xscale = target_size / scaled_size;
3016         xscale = wxMin(xscale, 1.0);
3017         xscale = wxMax(.2, xscale);
3018 
3019         scale_factor *= xscale;
3020     }
3021 
3022     int pivot_x = prule->pos.line.pivot_x.SYCL;
3023     int pivot_y = prule->pos.line.pivot_y.SYRW;
3024 
3025     pivot_x *= scale_factor;
3026     pivot_y *= scale_factor;
3027 
3028     // For opengl, hopefully the symbols are loaded in a texture
3029     unsigned int texture = 0;
3030     wxRect texrect;
3031     if(!m_pdc) {
3032       texture = ChartSymbols::GetGLTextureRect(texrect, prule->name.SYNM);
3033       if(texture) {
3034           prule->parm2 = texrect.width * scale_factor;
3035           prule->parm3 = texrect.height * scale_factor;
3036       }
3037     }
3038 
3039     if( m_pdc || !texture ) {
3040 
3041         //    Check to see if any cached data is valid
3042         bool b_dump_cache = false;
3043         if( prule->pixelPtr ) {
3044 
3045             // Detect switches between DC and GL modes, flush if switch occurs
3046             if( m_pdc ) {
3047                 if( prule->parm0 != ID_wxBitmap ) b_dump_cache = true;
3048             } else {
3049                 if( prule->parm0 != ID_RGBA ) b_dump_cache = true;
3050             }
3051         }
3052 
3053         // If the requested scaled symbol size is not the same as is currently cached,
3054         // we have to dump the cache
3055         wxRect trect;
3056         ChartSymbols::GetGLTextureRect( trect, prule->name.SYNM );
3057         if(prule->parm2 != trect.width * scale_factor)
3058             b_dump_cache = true;
3059 
3060         wxBitmap *pbm = NULL;
3061         wxImage Image;
3062 
3063         //Instantiate the symbol if necessary
3064         if( ( prule->pixelPtr == NULL ) || ( prule->parm1 != m_colortable_index ) || b_dump_cache ) {
3065             Image = useLegacyRaster ?
3066                 RuleXBMToImage( prule ) : ChartSymbols::GetImage( prule->name.SYNM );
3067 
3068             // delete any old private data
3069             ClearRulesCache( prule );
3070 
3071             // always display something, TMARDEF1 as width of 2
3072             int w0 = wxMax(1, Image.GetWidth() * scale_factor);
3073             int h0 = wxMax(1, Image.GetHeight() * scale_factor);
3074             Image.Rescale(w0 , h0 , wxIMAGE_QUALITY_HIGH);
3075 
3076             int w = Image.GetWidth();
3077             int h = Image.GetHeight();
3078 
3079             if( !m_pdc )          // opengl, not using textures
3080             {
3081                 //    Get the glRGBA format data from the wxImage
3082                 unsigned char *d = Image.GetData();
3083                 unsigned char *a = Image.GetAlpha();
3084 
3085                 Image.SetMaskColour( m_unused_wxColor.Red(), m_unused_wxColor.Green(),
3086                                      m_unused_wxColor.Blue() );
3087                 unsigned char mr, mg, mb;
3088                 if( !a && !Image.GetOrFindMaskColour( &mr, &mg, &mb ) )
3089                     printf( "trying to use mask to draw a bitmap without alpha or mask\n" );
3090 
3091                 unsigned char *e = (unsigned char *) malloc( w * h * 4 );
3092                 // XXX FIXME a or e ?
3093                 if( d && a){
3094                     for( int y = 0; y < h; y++ ) {
3095                         for( int x = 0; x < w; x++ ) {
3096                             unsigned char r, g, b;
3097                             int off = ( y * w + x );
3098                             r = d[off * 3 + 0];
3099                             g = d[off * 3 + 1];
3100                             b = d[off * 3 + 2];
3101 
3102                             e[off * 4 + 0] = r;
3103                             e[off * 4 + 1] = g;
3104                             e[off * 4 + 2] = b;
3105 
3106                             e[off * 4 + 3] =
3107                                 a ? a[off] : ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
3108                         }
3109                     }
3110                 }
3111 
3112                 //      Save the bitmap ptr and aux parms in the rule
3113                 prule->pixelPtr = e;
3114                 prule->parm0 = ID_RGBA;
3115                 prule->parm1 = m_colortable_index;
3116                 prule->parm2 = w;
3117                 prule->parm3 = h;
3118             } else {
3119                 //      Make the masked Bitmap
3120                 if( useLegacyRaster ) {
3121                     pbm = new wxBitmap( Image );
3122                     wxMask *pmask = new wxMask( *pbm, m_unused_wxColor );
3123                     pbm->SetMask( pmask );
3124                 }
3125 
3126                 bool b_has_trans = false;
3127 #if (defined(__WXGTK__) || defined(__WXMAC__))
3128 
3129                 //    Blitting of wxBitmap with transparency in wxGTK is broken....
3130                 //    We can do it the hard way, by manually alpha blending the
3131                 //    symbol with a clip taken from the current screen DC contents.
3132 
3133                 //    Inspect the symbol image, to see if it actually has alpha transparency
3134                 if(Image.HasAlpha())
3135                 {
3136                     unsigned char *a = Image.GetAlpha();
3137                     for(int i = 0; i < Image.GetHeight(); i++, a++)
3138                     {
3139                         for(int j = 0; j < Image.GetWidth(); j++)
3140                         {
3141                             if((*a) && (*a != 255)) {
3142                                 b_has_trans = true;
3143                                 break;
3144                             }
3145                         }
3146                         if(b_has_trans)
3147                             break;
3148                     }
3149                 }
3150 #ifdef __WXMAC__
3151                 b_has_trans = true;
3152 #endif
3153 
3154                 //    If the symbol image has no transparency, then a standard wxDC:Blit() will work
3155                 if(!b_has_trans) {
3156                     pbm = new wxBitmap( Image, -1 );
3157                     wxMask *pmask = new wxMask( *pbm, m_unused_wxColor );
3158                     pbm->SetMask( pmask );
3159                 }
3160 
3161 #else
3162                 if( !useLegacyRaster ) {
3163                     pbm = new wxBitmap( Image, 32 );                // windows
3164                     wxMask *pmask = new wxMask( *pbm, m_unused_wxColor );
3165                     pbm->SetMask( pmask );
3166                 }
3167 #endif
3168 
3169                 //      Save the bitmap ptr and aux parms in the rule
3170                 prule->pixelPtr = pbm;
3171                 prule->parm0 = ID_wxBitmap;
3172                 prule->parm1 = m_colortable_index;
3173                 prule->parm2 = w;
3174                 prule->parm3 = h;
3175 
3176             }
3177         }               // instantiation
3178     }
3179 
3180     //        Get the bounding box for the to-be-drawn symbol
3181     int b_width, b_height;
3182     b_width = prule->parm2;
3183     b_height = prule->parm3;
3184 
3185     LLBBox symbox;
3186     double latmin, lonmin, latmax, lonmax;
3187 
3188     if( !m_pdc && fabs( vp->rotation ) > .01)          // opengl
3189     {
3190         float cx = vp->pix_width/2.;
3191         float cy = vp->pix_height/2.;
3192         float c = cosf(vp->rotation );
3193         float s = sinf(vp->rotation );
3194         float x = r.x - pivot_x -cx;
3195         float y = r.y - pivot_y + b_height -cy;
3196         GetPixPointSingle( x*c - y*s +cx, x*s + y*c +cy, &latmin, &lonmin, vp );
3197 
3198         x = r.x - pivot_x + b_width -cx;
3199         y = r.y - pivot_y -cy;
3200         GetPixPointSingle( x*c - y*s +cx, x*s + y*c +cy, &latmax, &lonmax, vp );
3201     } else {
3202         GetPixPointSingle( r.x - pivot_x, r.y - pivot_y + b_height, &latmin, &lonmin, vp );
3203         GetPixPointSingle( r.x - pivot_x + b_width, r.y - pivot_y, &latmax, &lonmax, vp );
3204     }
3205     symbox.Set( latmin, lonmin, latmax, lonmax );
3206 
3207     //  Special case for GEO_AREA objects with centred symbols
3208      if( rzRules->obj->Primitive_type == GEO_AREA ) {
3209          if( !rzRules->obj->BBObj.IntersectIn( symbox ) ) // Symbol is wholly outside base object
3210              return true;
3211      }
3212 
3213     //      Now render the symbol
3214 
3215     if( !m_pdc )          // opengl
3216     {
3217 #ifdef ocpnUSE_GL
3218         glEnable( GL_BLEND );
3219 
3220         if(texture) {
3221             extern GLenum       g_texture_rectangle_format;
3222 
3223             glEnable(GL_TEXTURE_2D);
3224             glBindTexture(GL_TEXTURE_2D, texture);
3225 
3226             int w = texrect.width, h = texrect.height;
3227 
3228             float tx1 = texrect.x, ty1 = texrect.y;
3229             float tx2 = tx1 + w, ty2 = ty1 + h;
3230 
3231 #ifndef USE_ANDROID_GLES2
3232             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
3233             if(g_texture_rectangle_format == GL_TEXTURE_2D) {
3234                 wxSize size = ChartSymbols::GLTextureSize();
3235                 tx1 /= size.x, tx2 /= size.x;
3236                 ty1 /= size.y, ty2 /= size.y;
3237             }
3238 
3239             {
3240                 glPushMatrix();
3241 
3242                 glTranslatef(r.x, r.y, 0);
3243                 glRotatef(vp->rotation * 180/PI, 0, 0, -1);
3244                 glTranslatef(-pivot_x, -pivot_y, 0);
3245                 glScalef(scale_factor, scale_factor, 1);
3246 
3247                 glBegin(GL_QUADS);
3248                     glTexCoord2f(tx1, ty1);    glVertex2i( 0, 0);
3249                     glTexCoord2f(tx2, ty1);    glVertex2i( w, 0);
3250                     glTexCoord2f(tx2, ty2);    glVertex2i( w, h);
3251                     glTexCoord2f(tx1, ty2);    glVertex2i( 0, h);
3252                 glEnd();
3253 
3254                 glPopMatrix();
3255             }
3256 #else
3257 
3258 
3259             if(g_texture_rectangle_format == GL_TEXTURE_2D) {
3260 
3261                 // Normalize the sybmol texture coordinates against the next higher POT size
3262                 wxSize size = ChartSymbols::GLTextureSize();
3263 #if 0
3264                                 int i=1;
3265                                 while(i < size.x) i <<= 1;
3266                                 int rb_x = i;
3267 
3268                                 i=1;
3269                                 while(i < size.y) i <<= 1;
3270                                 int rb_y = i;
3271 
3272 #else
3273                 int rb_x = size.x;
3274                 int rb_y = size.y;
3275 #endif
3276                 //qDebug() << "texnorm" << rb_x << rb_y;
3277                 tx1 /= rb_x , tx2 /= rb_x ;
3278                 ty1 /= rb_y , ty2 /= rb_y ;
3279                 }
3280 
3281             float uv[8];
3282             float coords[8];
3283 
3284             // Note swizzle of points to allow TRIANGLE_STRIP drawing
3285             //normal uv
3286             uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1;
3287             uv[6] = tx2; uv[7] = ty2; uv[4] = tx1; uv[5] = ty2;
3288 
3289             w *= scale_factor;
3290             h *= scale_factor;
3291 
3292             // pixels
3293             coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0;
3294             coords[6] = w; coords[7] = h; coords[4] = 0; coords[5] = h;
3295 
3296             glUseProgram( S52texture_2D_shader_program );
3297 
3298             // Get pointers to the attributes in the program.
3299             GLint mPosAttrib = glGetAttribLocation( S52texture_2D_shader_program, "position" );
3300             GLint mUvAttrib  = glGetAttribLocation( S52texture_2D_shader_program, "aUV" );
3301 
3302             // Select the active texture unit.
3303             glActiveTexture( GL_TEXTURE0 );
3304 
3305             // Bind our texture to the texturing target.
3306             glBindTexture( GL_TEXTURE_2D, texture );
3307 
3308             // Set up the texture sampler to texture unit 0
3309             GLint texUni = glGetUniformLocation( S52texture_2D_shader_program, "uTex" );
3310             glUniform1i( texUni, 0 );
3311 
3312             // Disable VBO's (vertex buffer objects) for attributes.
3313             glBindBuffer( GL_ARRAY_BUFFER, 0 );
3314             glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
3315 
3316             // Set the attribute mPosAttrib with the vertices in the screen coordinates...
3317             glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
3318             // ... and enable it.
3319             glEnableVertexAttribArray( mPosAttrib );
3320 
3321             // Set the attribute mUvAttrib with the vertices in the GL coordinates...
3322             glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
3323             // ... and enable it.
3324             glEnableVertexAttribArray( mUvAttrib );
3325 
3326             // Rotate
3327             mat4x4 I, Q;
3328             mat4x4_identity(I);
3329 
3330             mat4x4_translate_in_place(I, r.x, r.y, 0);
3331             mat4x4_rotate_Z(Q, I, -vp->rotation);
3332             mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0);
3333 
3334 
3335             GLint matloc = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix");
3336             glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
3337 
3338             // Perform the actual drawing.
3339             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3340 
3341             // Restore the per-object transform to Identity Matrix
3342             mat4x4 IM;
3343             mat4x4_identity(IM);
3344             GLint matlocf = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix");
3345             glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
3346 
3347 
3348 #endif          // GLES2
3349             glDisable(g_texture_rectangle_format);
3350         } else { /* this is only for legacy mode, or systems without NPOT textures */
3351             float cr = cosf( vp->rotation );
3352             float sr = sinf( vp->rotation );
3353             float ddx = pivot_x * cr + pivot_y * sr;
3354             float ddy = pivot_y * cr - pivot_x * sr;
3355 #ifndef USE_ANDROID_GLES2
3356             glColor4f( 1, 1, 1, 1 );
3357 
3358             //  Since draw pixels is so slow, lets not draw anything we don't have to
3359             wxRect sym_rect(r.x - ddx, r.y - ddy, b_width, b_height);
3360             if(vp->rv_rect.Intersects(sym_rect) ) {
3361 
3362                 glPushAttrib( GL_SCISSOR_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
3363 
3364                 glDisable( GL_SCISSOR_TEST );
3365                 glDisable( GL_STENCIL_TEST );
3366                 glDisable( GL_DEPTH_TEST );
3367 
3368                 glRasterPos2f( r.x - ddx, r.y - ddy );
3369                 glPixelZoom( 1, -1 );
3370                 glDrawPixels( b_width, b_height, GL_RGBA, GL_UNSIGNED_BYTE, prule->pixelPtr );
3371                 glPixelZoom( 1, 1 );
3372 
3373                 glPopAttrib();
3374             }
3375 #endif
3376         }
3377 
3378         glDisable( GL_BLEND );
3379 #endif
3380     } else {
3381 
3382         if( !( prule->pixelPtr ) )                // This symbol requires manual alpha blending
3383         {
3384             //    Don't bother if the symbol is off the true screen,
3385             //    as for instance when an area-centered symbol is called for.
3386             if( ( r.x - pivot_x  < vp->pix_width ) && ( r.y - pivot_y < vp->pix_height ) ) {
3387 
3388                 // Get the current screen contents to a wxImage
3389                 wxBitmap b1( b_width, b_height, -1 );
3390                 wxMemoryDC mdc1( b1 );
3391                 mdc1.Blit( 0, 0, b_width, b_height, m_pdc, r.x - pivot_x, r.y - pivot_y, wxCOPY );
3392                 wxImage im_back = b1.ConvertToImage();
3393 
3394                 //    Get the scaled symbol as a wxImage
3395                 wxImage im_sym = ChartSymbols::GetImage( prule->name.SYNM );
3396                 im_sym.Rescale(b_width, b_height, wxIMAGE_QUALITY_HIGH);
3397 
3398                 wxImage im_result( b_width, b_height );
3399                 unsigned char *pdest = im_result.GetData();
3400                 unsigned char *pback = im_back.GetData();
3401                 unsigned char *psym = im_sym.GetData();
3402 
3403                 unsigned char *asym = NULL;
3404                 if( im_sym.HasAlpha() )
3405                     asym = im_sym.GetAlpha();
3406 
3407                 //    Do alpha blending, the hard way
3408 
3409                 if(pdest && psym && pback){
3410                     for( int i = 0; i < b_height; i++ ) {
3411                         for( int j = 0; j < b_width; j++ ) {
3412                             double alpha = 1.0;
3413                             if(asym)
3414                                 alpha = ( *asym++ ) / 256.0;
3415                             unsigned char r = ( *psym++ * alpha ) + ( *pback++ * ( 1.0 - alpha ) );
3416                             *pdest++ = r;
3417                             unsigned char g = ( *psym++ * alpha ) + ( *pback++ * ( 1.0 - alpha ) );
3418                             *pdest++ = g;
3419                             unsigned char b = ( *psym++ * alpha ) + ( *pback++ * ( 1.0 - alpha ) );
3420                             *pdest++ = b;
3421                         }
3422                     }
3423                 }
3424 
3425                 wxBitmap result( im_result );
3426                 wxMemoryDC result_dc( result );
3427 
3428                 m_pdc->Blit( r.x - pivot_x, r.y - pivot_y, b_width, b_height, &result_dc, 0, 0,  wxCOPY, false );
3429 
3430                 result_dc.SelectObject( wxNullBitmap );
3431                 mdc1.SelectObject( wxNullBitmap );
3432             }
3433         } else {
3434             //      Get the symbol bitmap into a memory dc
3435             wxBitmap &bmp = (wxBitmap &) ( *( (wxBitmap *) ( prule->pixelPtr ) ) );
3436             wxMemoryDC mdc(bmp);
3437 
3438             //      Blit it into the target dc, with mask
3439             m_pdc->Blit( r.x - pivot_x, r.y - pivot_y, bmp.GetWidth(), bmp.GetHeight(), &mdc, 0, 0, wxCOPY, true );
3440 
3441             mdc.SelectObject( wxNullBitmap );
3442 
3443         }
3444 // Debug
3445 //if(m_pdc){
3446 //m_pdc->SetPen(wxPen(*wxGREEN, 1));
3447 //m_pdc->SetBrush(wxBrush(*wxGREEN, wxTRANSPARENT));
3448 //m_pdc->DrawRectangle(r.x - pivot_x, r.y - pivot_y, b_width, b_height);
3449 //}
3450 
3451     }
3452 
3453     //  Update the object Bounding box
3454     //  so that subsequent drawing operations will redraw the item fully
3455     //  We expand the object's BBox to account for objects rendered by multiple symbols, such as SOUNGD.
3456     //  so that expansions are cumulative.
3457     if( rzRules->obj->Primitive_type == GEO_POINT )
3458         rzRules->obj->BBObj.Expand( symbox );
3459 
3460     //  Dump the cache for next time
3461     if(g_oz_vector_scale && (scale_factor > 1.0))
3462         ClearRulesCache( prule );
3463 
3464     return true;
3465 }
3466 
3467 // SYmbol
3468 int s52plib::RenderSY( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
3469 {
3470     float angle = 0;
3471     double orient;
3472 
3473     if( rules->razRule != NULL ) {
3474         if( rules->INSTstr[8] == ',' ) // supplementary parameter assumed to be angle, seen in LIGHTSXX
3475                 {
3476             char sangle[10];
3477             int cp = 0;
3478             while( rules->INSTstr[cp + 9] && ( rules->INSTstr[cp + 9] != ')' ) ) {
3479                 sangle[cp] = rules->INSTstr[cp + 9];
3480                 cp++;
3481             }
3482             sangle[cp] = 0;
3483             int angle_i = atoi( sangle );
3484             angle = angle_i;
3485         }
3486 
3487         if( GetDoubleAttr( rzRules->obj, "ORIENT", orient ) ) // overriding any LIGHTSXX angle, probably TSSLPT
3488                 {
3489             angle = orient;
3490             if( strncmp( rzRules->obj->FeatureName, "LIGHTS", 6 ) == 0 ) {
3491                 angle += 180;
3492                 if( angle > 360 ) angle -= 360;
3493             }
3494         }
3495 
3496         //  Render symbol at object's x/y
3497         wxPoint r, r1;
3498         GetPointPixSingle( rzRules, rzRules->obj->y, rzRules->obj->x, &r, vp );
3499 
3500          //  Render a raster or vector symbol, as specified by LUP rules
3501         if( rules->razRule->definition.SYDF == 'V' ){
3502             RenderHPGL( rzRules, rules->razRule, r, vp, angle );
3503         }
3504         else{
3505             if( rules->razRule->definition.SYDF == 'R' )
3506                 RenderRasterSymbol( rzRules, rules->razRule, r, vp, angle );
3507         }
3508     }
3509 
3510     return 0;
3511 
3512 }
3513 
3514 bool s52plib::RenderSoundingSymbol( ObjRazRules *rzRules, Rule *prule, wxPoint &r, ViewPort *vp,
3515                                    wxColor symColor, float rot_angle )
3516 {
3517     double scale_factor = 1.0;
3518 
3519     scale_factor *=  g_ChartScaleFactorExp;
3520     scale_factor *= g_scaminScale;
3521 
3522     if(m_display_size_mm < 200){                //about 8 inches, implying some sort of smaller mobile device
3523         //  Set the onscreen size of the symbol
3524         //  Compensate for various display resolutions
3525         //  Develop empirically, making a buoy about 4 mm tall
3526         double boyHeight = 21. / GetPPMM();           // from raster symbol definitions, boylat is xx pix high
3527 
3528         double targetHeight0 = 4.0;
3529 
3530         // But we want to scale the size for smaller displays
3531         double displaySize = m_display_size_mm;
3532         displaySize = wxMax(displaySize, 100);
3533 
3534         float targetHeight = wxMin(targetHeight0, displaySize / 30);
3535 
3536         double pix_factor = targetHeight / boyHeight;
3537 
3538         //qDebug() << "scaleing" << m_display_size_mm  << targetHeight0 << targetHeight << GetPPMM() << boyHeight << pix_factor;
3539 
3540         // for Hubert, and my moto
3541         //scaleing 93.98 93 4 3.33333 12.7312 1.64949 2.02082
3542         // My nvidia tab
3543         //scaleing 144.78 144 4 4 12.6667 1.65789 2.4127
3544         // judgement: all OK
3545 
3546 
3547         //scale_factor *= pix_factor;
3548     }
3549 
3550     wxFontWeight fontWeight = wxFONTWEIGHT_NORMAL;
3551     wxString fontFacename = wxEmptyString;
3552     double defaultHeight = 3.0;
3553 
3554 #ifdef __OCPN__ANDROID__
3555     fontWeight = wxFONTWEIGHT_BOLD;
3556     fontFacename = _T("Roboto");
3557     defaultHeight = 2.2;
3558 #endif
3559 
3560         // calculate the required point size to give specified height
3561     int point_size = 6;
3562     bool not_done = true;
3563     wxScreenDC sdc;
3564     int charWidth, charHeight, charDescent;
3565     while((point_size < 20) && not_done){
3566         wxFont *tentativeFont = FindOrCreateFont_PlugIn( point_size, wxFONTFAMILY_SWISS,  wxFONTSTYLE_NORMAL, fontWeight, false, fontFacename );
3567         sdc.GetTextExtent( _T("0"), &charWidth, &charHeight, &charDescent, NULL, tentativeFont ); // measure the text
3568         double font_size_mm = (double)(charHeight- charDescent) / GetPPMM();
3569 
3570         if(font_size_mm >= (defaultHeight * scale_factor)){
3571             not_done = false;
3572             break;
3573         }
3574         point_size++;
3575     }
3576 
3577     double postmult =  m_SoundingsScaleFactor;
3578     if((postmult <= 2.0) && (postmult >= 0.5)){
3579         point_size *= postmult;
3580         scale_factor *= postmult;
3581         charWidth *= postmult;
3582     }
3583 
3584     // Build the texDepth object, if required
3585     if(!m_pdc){                         // OpenGL
3586         if(!m_texSoundings.IsBuilt() || (fabs(m_texSoundings.GetScale() - scale_factor) > 0.1) ){
3587             m_texSoundings.Delete();
3588 
3589             m_soundFont = FindOrCreateFont_PlugIn( point_size, wxFONTFAMILY_SWISS,  wxFONTSTYLE_NORMAL, fontWeight, false, fontFacename );
3590             m_texSoundings.Build(m_soundFont, scale_factor);        //texSounding owns the font
3591         }
3592     }
3593     else{
3594         m_soundFont = FindOrCreateFont_PlugIn( point_size, wxFONTFAMILY_SWISS,  wxFONTSTYLE_NORMAL, fontWeight, false, fontFacename );
3595         m_pdc->SetFont(*m_soundFont);
3596         charHeight -= charDescent;
3597     }
3598 
3599     int pivot_x;
3600     int pivot_y;
3601 
3602     // Parse the symbol name
3603 
3604     //  The digit
3605     char symDigit = prule->name.SYNM[7];
3606     int symIndex = symDigit - 0x30;
3607 
3608     //  The pivot point offset group
3609     char symCPivot = prule->name.SYNM[6];
3610     int symPivot = symCPivot - 0x30;
3611 
3612     int pivotWidth, pivotHeight;
3613     // For opengl, the symbols are loaded in a texture
3614     unsigned int texture = 0;
3615     wxRect texrect;
3616     if(!m_pdc) {                // GL
3617       texture = m_texSoundings.GetTexture();
3618       m_texSoundings.GetGLTextureRect(texrect, symIndex);
3619 
3620       if(texture) {
3621           prule->parm2 = texrect.width;
3622           prule->parm3 = texrect.height;
3623       }
3624 
3625       pivotWidth = texrect.width;
3626       pivotHeight = texrect.height;
3627 
3628     }
3629     else{
3630       pivotWidth = charWidth;
3631       pivotHeight = charHeight;
3632     }
3633 
3634     if(symPivot < 4){
3635           pivot_x = (pivotWidth * symPivot) - (pivotWidth / 4);
3636           pivot_y = pivotHeight  / 2;
3637       }
3638     else if(symPivot == 4){
3639           pivot_x = -pivotWidth - (pivotWidth / 4);
3640           pivot_y = pivotHeight / 2;
3641     }
3642     else{
3643           pivot_x = - (pivotWidth / 4);
3644           pivot_y = pivotHeight / 5;
3645     }
3646 
3647     //        Get the bounding box for the to-be-drawn symbol
3648     int b_width, b_height;
3649     b_width = prule->parm2;
3650     b_height = prule->parm3;
3651 
3652     LLBBox symbox;
3653     double latmin, lonmin, latmax, lonmax;
3654 
3655     if( !m_pdc && fabs( vp->rotation ) > .01)          // opengl
3656     {
3657         float cx = vp->pix_width/2.;
3658         float cy = vp->pix_height/2.;
3659         float c = cosf(vp->rotation );
3660         float s = sinf(vp->rotation );
3661         float x = r.x - pivot_x -cx;
3662         float y = r.y - pivot_y + b_height -cy;
3663         GetPixPointSingle( x*c - y*s +cx, x*s + y*c +cy, &latmin, &lonmin, vp );
3664 
3665         x = r.x - pivot_x + b_width -cx;
3666         y = r.y - pivot_y -cy;
3667         GetPixPointSingle( x*c - y*s +cx, x*s + y*c +cy, &latmax, &lonmax, vp );
3668     } else {
3669         GetPixPointSingle( r.x - pivot_x, r.y - pivot_y + b_height, &latmin, &lonmin, vp );
3670         GetPixPointSingle( r.x - pivot_x + b_width, r.y - pivot_y, &latmax, &lonmax, vp );
3671     }
3672     symbox.Set( latmin, lonmin, latmax, lonmax );
3673 
3674     //      Now render the symbol
3675 
3676     if( !m_pdc )          // opengl
3677     {
3678 #ifdef ocpnUSE_GL
3679         glEnable( GL_BLEND );
3680 
3681         if(texture) {
3682             extern GLenum       g_texture_rectangle_format;
3683 
3684             glEnable(GL_TEXTURE_2D);
3685             glEnable( GL_BLEND );
3686             glBindTexture(GL_TEXTURE_2D, texture);
3687 
3688             int w = texrect.width, h = texrect.height;
3689 
3690             float tx1 = texrect.x, ty1 = texrect.y;
3691             float tx2 = tx1 + w, ty2 = ty1 + h;
3692 
3693 #ifndef USE_ANDROID_GLES2
3694 //            glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
3695             glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
3696 
3697             if(g_texture_rectangle_format == GL_TEXTURE_2D) {
3698                 wxSize size = m_texSoundings.GLTextureSize();
3699                 tx1 /= size.x, tx2 /= size.x;
3700                 ty1 /= size.y, ty2 /= size.y;
3701             }
3702 
3703             glColor3ub( symColor.Red(), symColor.Green(), symColor.Blue() );
3704 
3705 
3706             {
3707                 glPushMatrix();
3708 
3709                 glTranslatef(r.x, r.y, 0);
3710                 glRotatef(vp->rotation * 180/PI, 0, 0, -1);
3711                 glTranslatef(-pivot_x, -pivot_y, 0);
3712                 //glScalef(scale_factor, scale_factor, 1);
3713 
3714                 glBegin(GL_QUADS);
3715                     glTexCoord2f(tx1, ty1);    glVertex2i( 0, 0);
3716                     glTexCoord2f(tx2, ty1);    glVertex2i( w, 0);
3717                     glTexCoord2f(tx2, ty2);    glVertex2i( w, h);
3718                     glTexCoord2f(tx1, ty2);    glVertex2i( 0, h);
3719                 glEnd();
3720 
3721                 glPopMatrix();
3722             }
3723 #else
3724 
3725 
3726             if(g_texture_rectangle_format == GL_TEXTURE_2D) {
3727 
3728                 // Normalize the sybmol texture coordinates against the next higher POT size
3729                 wxSize size = m_texSoundings.GLTextureSize();
3730                 int rb_x = size.x;
3731                 int rb_y = size.y;
3732 
3733                 tx1 /= rb_x , tx2 /= rb_x ;
3734                 ty1 /= rb_y , ty2 /= rb_y ;
3735                 }
3736 
3737             float uv[8];
3738             float coords[8];
3739 
3740             // Note swizzle of points to allow TRIANGLE_STRIP drawing
3741             //normal uv
3742             uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1;
3743             uv[6] = tx2; uv[7] = ty2; uv[4] = tx1; uv[5] = ty2;
3744 
3745 
3746             // pixels
3747             coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0;
3748             coords[6] = w; coords[7] = h; coords[4] = 0; coords[5] = h;
3749 
3750             glUseProgram( S52texture_2D_ColorMod_shader_program );
3751 
3752             float colorv[4];
3753             colorv[0] = symColor.Red() / float(256);
3754             colorv[1] = symColor.Green() / float(256);
3755             colorv[2] = symColor.Blue() / float(256);
3756             colorv[3] = 1.0;
3757 
3758             GLint colloc = glGetUniformLocation(S52texture_2D_ColorMod_shader_program,"color");
3759             glUniform4fv(colloc, 1, colorv);
3760 
3761             // Get pointers to the attributes in the program.
3762             GLint mPosAttrib = glGetAttribLocation( S52texture_2D_ColorMod_shader_program, "position" );
3763             GLint mUvAttrib  = glGetAttribLocation( S52texture_2D_ColorMod_shader_program, "aUV" );
3764 
3765             // Select the active texture unit.
3766             glActiveTexture( GL_TEXTURE0 );
3767 
3768             // Bind our texture to the texturing target.
3769             glBindTexture( GL_TEXTURE_2D, texture );
3770 
3771             // Set up the texture sampler to texture unit 0
3772             GLint texUni = glGetUniformLocation( S52texture_2D_ColorMod_shader_program, "uTex" );
3773             glUniform1i( texUni, 0 );
3774 
3775             // Disable VBO's (vertex buffer objects) for attributes.
3776             glBindBuffer( GL_ARRAY_BUFFER, 0 );
3777             glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
3778 
3779             // Set the attribute mPosAttrib with the vertices in the screen coordinates...
3780             glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
3781             // ... and enable it.
3782             glEnableVertexAttribArray( mPosAttrib );
3783 
3784             // Set the attribute mUvAttrib with the vertices in the GL coordinates...
3785             glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
3786             // ... and enable it.
3787             glEnableVertexAttribArray( mUvAttrib );
3788 
3789             // Rotate
3790             mat4x4 I, Q;
3791             mat4x4_identity(I);
3792 
3793             mat4x4_translate_in_place(I, r.x, r.y, 0);
3794             mat4x4_rotate_Z(Q, I, -vp->rotation);
3795             mat4x4_translate_in_place(Q, -pivot_x, -pivot_y, 0);
3796 
3797 
3798             GLint matloc = glGetUniformLocation(S52texture_2D_ColorMod_shader_program,"TransformMatrix");
3799             glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
3800 
3801             // Perform the actual drawing.
3802             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3803 
3804             // Restore the per-object transform to Identity Matrix
3805             mat4x4 IM;
3806             mat4x4_identity(IM);
3807             GLint matlocf = glGetUniformLocation(S52texture_2D_ColorMod_shader_program,"TransformMatrix");
3808             glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
3809 
3810 
3811 #endif          // GLES2
3812             glDisable(g_texture_rectangle_format);
3813         } else { /* this is only for legacy mode, or systems without NPOT textures */
3814             float cr = cosf( vp->rotation );
3815             float sr = sinf( vp->rotation );
3816             float ddx = pivot_x * cr + pivot_y * sr;
3817             float ddy = pivot_y * cr - pivot_x * sr;
3818 #ifndef USE_ANDROID_GLES2
3819             glColor4f( 1, 1, 1, 1 );
3820 
3821             //  Since draw pixels is so slow, lets not draw anything we don't have to
3822             wxRect sym_rect(r.x - ddx, r.y - ddy, b_width, b_height);
3823             if(vp->rv_rect.Intersects(sym_rect) ) {
3824 
3825                 glPushAttrib( GL_SCISSOR_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
3826 
3827                 glDisable( GL_SCISSOR_TEST );
3828                 glDisable( GL_STENCIL_TEST );
3829                 glDisable( GL_DEPTH_TEST );
3830 
3831                 glRasterPos2f( r.x - ddx, r.y - ddy );
3832                 glPixelZoom( 1, -1 );
3833                 glDrawPixels( b_width, b_height, GL_RGBA, GL_UNSIGNED_BYTE, prule->pixelPtr );
3834                 glPixelZoom( 1, 1 );
3835 
3836                 glPopAttrib();
3837             }
3838 #endif
3839         }
3840 
3841         glDisable( GL_BLEND );
3842 #endif
3843     }
3844     else {
3845             wxString text;
3846             text.Printf(_T("%d"), symIndex);
3847             m_pdc->SetTextForeground( symColor );
3848 
3849             m_pdc->DrawText(text, r.x - pivot_x, r.y - pivot_y);
3850 
3851     }
3852 
3853     return true;
3854 }
3855 
3856 
3857 
3858 
3859 // Line Simple Style, OpenGL
3860 int s52plib::RenderGLLS( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
3861 {
3862     // for now don't use vbo model in non-mercator
3863     if(vp->m_projection_type != PROJECTION_MERCATOR)
3864         return RenderLS(rzRules, rules, vp);
3865 
3866     if( !m_benableGLLS )                        // root chart cannot support VBO model, for whatever reason
3867         return RenderLS(rzRules, rules, vp);
3868 
3869 #ifndef USE_ANDROID_GLES2
3870     double scale_factor = vp->ref_scale/vp->chart_scale;
3871     if(scale_factor > 10.0)
3872         return RenderLS(rzRules, rules, vp);
3873 #endif
3874 
3875     if( !rzRules->obj->m_chart_context->chart )
3876         return RenderLS(rzRules, rules, vp);    // this is where S63 PlugIn gets caught
3877 
3878     if(( vp->GetBBox().GetMaxLon() >= 180.) || (vp->GetBBox().GetMinLon() <= -180.))
3879         return RenderLS(rzRules, rules, vp);    // cm03 has trouble at IDL
3880 
3881     bool b_useVBO = false;
3882     float *vertex_buffer = 0;
3883 
3884     if(rzRules->obj->auxParm2 > 0)             // Has VBO been defined and uploaded?
3885         b_useVBO = true;
3886 
3887     if( !b_useVBO ){
3888 #if 0
3889         if( rzRules->obj->m_chart_context->chart ){
3890             vertex_buffer = rzRules->obj->m_chart_context->chart->GetLineVertexBuffer();
3891         }
3892         else {
3893             vertex_buffer = rzRules->obj->m_chart_context->vertex_buffer;
3894         }
3895 
3896 
3897         if(!vertex_buffer)
3898             return RenderLS(rzRules, rules, vp);    // this is where cm93 gets caught
3899 #else
3900         vertex_buffer = rzRules->obj->m_chart_context->vertex_buffer;
3901 
3902 #endif
3903     }
3904 
3905 
3906 #ifdef ocpnUSE_GL
3907 
3908     char *str = (char*) rules->INSTstr;
3909 
3910 #ifdef USE_ANDROID_GLES2
3911     if( (!strncmp( str, "DASH", 4 ) ) || ( !strncmp( str, "DOTT", 4 ) ) )
3912         return RenderLS_Dash_GLSL(rzRules, rules, vp);
3913 #endif
3914 
3915 
3916     LLBBox BBView = vp->GetBBox();
3917 
3918     //  Allow a little slop in calculating whether a segment
3919     //  is within the requested Viewport
3920     double margin = BBView.GetLonRange() * .05;
3921     BBView.EnLarge( margin );
3922 
3923     //  Get the current display priority
3924     //  Default comes from the LUP, unless overridden
3925     int priority_current = rzRules->LUP->DPRI - '0';
3926     if(rzRules->obj->m_DPRI >= 0)
3927         priority_current = rzRules->obj->m_DPRI;
3928 
3929     line_segment_element *ls_list = rzRules->obj->m_ls_list;
3930 
3931     S52color *c = getColor( str + 7 ); // Colour
3932     int w = atoi( str + 5 ); // Width
3933     if(w > 1)
3934         int yyp = 4;
3935 
3936 #ifndef ocpnUSE_GLES // linestipple is emulated poorly
3937     glColor3ub( c->R, c->G, c->B );
3938 #endif
3939 
3940     //    Set drawing width
3941     float lineWidth = w;
3942     lineWidth = wxMax(g_GLMinCartographicLineWidth, w);
3943 
3944     // Manage super high density displays
3945     float target_w_mm = 0.5 * w;
3946     if(GetPPMM() > 7){               // arbitrary
3947         target_w_mm = ((float)w) / 6.0;  // Target width in mm
3948                                                //  The value "w" comes from S52 library CNSY procedures, in "nominal" pixels
3949                                                // the value "6" comes from semi-standard LCD display densities
3950                                                // or something like 0.18 mm pitch, or 6 pix per mm.
3951         lineWidth =  wxMax(g_GLMinCartographicLineWidth, target_w_mm * GetPPMM());
3952     }
3953 
3954     glDisable( GL_LINE_SMOOTH );
3955     glDisable( GL_BLEND );
3956 
3957 #ifdef __OCPN__ANDROID__
3958 //     if( w > 1 )
3959 //         lineWidth = wxMin(lineWidth, parms[1]);
3960     glLineWidth(lineWidth);
3961 
3962 #else
3963     glLineWidth(lineWidth);
3964     if(lineWidth > 4.0 && m_GLLineSmoothing){
3965         glEnable( GL_LINE_SMOOTH );
3966         glEnable( GL_BLEND );
3967     }
3968 #endif
3969 
3970 #ifndef ocpnUSE_GLES // linestipple is emulated poorly
3971     if( !strncmp( str, "DASH", 4 ) ) {
3972         glLineStipple( 1, 0x3F3F );
3973         glEnable( GL_LINE_STIPPLE );
3974     }
3975     else if( !strncmp( str, "DOTT", 4 ) ) {
3976         glLineStipple( 1, 0x3333 );
3977         glEnable( GL_LINE_STIPPLE );
3978     }
3979     else
3980         glDisable( GL_LINE_STIPPLE );
3981 #endif
3982 
3983 
3984 #ifndef USE_ANDROID_GLES2
3985     glColor3ub( c->R, c->G, c->B );
3986 
3987     glPushMatrix();
3988 
3989     // Set up the OpenGL transform matrix for this object
3990     //  Transform from Simple Mercator (relative to chart reference point) to screen coordinates.
3991 
3992     //  First, the VP transform
3993     glTranslatef( vp->pix_width / 2, vp->pix_height/2, 0 );
3994     glScalef( vp->view_scale_ppm, -vp->view_scale_ppm, 0 );
3995     glTranslatef( -rzRules->sm_transform_parms->easting_vp_center, -rzRules->sm_transform_parms->northing_vp_center, 0 );
3996 
3997     //  Next, the per-object transform
3998     if( rzRules->obj->m_chart_context->chart ){
3999         glTranslatef( rzRules->obj->x_origin, rzRules->obj->y_origin, 0);
4000         glScalef( rzRules->obj->x_rate, rzRules->obj->y_rate, 0 );
4001     }
4002 
4003     glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
4004 
4005 #endif
4006 
4007     //   Has line segment PBO been allocated for this chart?
4008     if(b_useVBO){
4009         glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm2);
4010     }
4011 
4012 
4013 #ifdef USE_ANDROID_GLES2
4014     glUseProgram(S52color_tri_shader_program);
4015 
4016     // Disable VBO's (vertex buffer objects) for attributes.
4017     if(!b_useVBO)
4018         glBindBuffer( GL_ARRAY_BUFFER, 0 );
4019     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
4020 
4021     GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
4022 
4023     float angle = 0;
4024 
4025     // We cannot use the prepared shader uniforms, as we can (and should for performance) do the per-object transforms
4026     // all at once in the shader matrix.
4027     // But we also must restore the prepared matrix for later rendering of other object classes.
4028 
4029     // Build Transform matrix
4030     //  First, the VP transform
4031     mat4x4 I, Q;
4032     mat4x4_identity(I);
4033 
4034     // Scale per object
4035     I[0][0] *= rzRules->obj->x_rate;
4036     I[1][1] *= rzRules->obj->y_rate;
4037 
4038     // Translate per object
4039     I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - rzRules->obj->x_origin) * vp->view_scale_ppm ;
4040     I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center - rzRules->obj->y_origin) * -vp->view_scale_ppm ;
4041 
4042     // Scale for screen
4043     I[0][0] *= vp->view_scale_ppm;
4044     I[1][1] *= -vp->view_scale_ppm;
4045 
4046     //Rotate
4047     mat4x4_rotate_Z(Q, I, angle);
4048 
4049     // Translate for screen
4050     Q[3][0] += vp->pix_width / 2;
4051     Q[3][1] += vp->pix_height / 2;
4052 
4053     GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"TransformMatrix");
4054     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
4055 
4056     float colorv[4];
4057     colorv[0] = c->R / float(256);
4058     colorv[1] = c->G / float(256);
4059     colorv[2] = c->B / float(256);
4060     colorv[3] = 1.0;
4061 
4062     GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
4063     glUniform4fv(colloc, 1, colorv);
4064 
4065     if(!b_useVBO){
4066         unsigned char *buffer = (unsigned char *)vertex_buffer;
4067         float *bufBase = (float *)buffer;
4068         glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufBase);
4069         glEnableVertexAttribArray(pos);
4070     }
4071 
4072 #endif
4073 
4074     // from above ls_list is the first drawable segment
4075     while( ls_list){
4076 
4077         if( ls_list->priority == priority_current  )
4078         {
4079             size_t seg_vbo_offset = 0;
4080             size_t point_count = 0;
4081 
4082             //  Check visibility of the segment
4083             bool b_drawit = false;
4084             if( (ls_list->ls_type == TYPE_EE) || (ls_list->ls_type == TYPE_EE_REV) ){
4085 //                 if((BBView.GetMinLat() < ls_list->pedge->edgeBBox.GetMaxLat() && BBView.GetMaxLat() > ls_list->pedge->edgeBBox.GetMinLat()) &&
4086 //                     ((BBView.GetMinLon() <= ls_list->pedge->edgeBBox.GetMaxLon() && BBView.GetMaxLon() >= ls_list->pedge->edgeBBox.GetMinLon()) ||
4087 //                     (BBView.GetMaxLon() >=  180 && BBView.GetMaxLon() - 360 > ls_list->pedge->edgeBBox.GetMinLon()) ||
4088 //                     (BBView.GetMinLon() <= -180 && BBView.GetMinLon() + 360 < ls_list->pedge->edgeBBox.GetMaxLon())))
4089                 {
4090                     // render the segment
4091                         b_drawit = true;
4092                         seg_vbo_offset = ls_list->pedge->vbo_offset;
4093                         point_count = ls_list->pedge->nCount;
4094                  }
4095 
4096             }
4097             else{
4098 //                 if((BBView.GetMinLat() < ls_list->pcs->cs_lat_avg && BBView.GetMaxLat() > ls_list->pcs->cs_lat_avg) &&
4099 //                     ((BBView.GetMinLon() <= ls_list->pcs->cs_lon_avg && BBView.GetMaxLon() >= ls_list->pcs->cs_lon_avg) ||
4100 //                     (BBView.GetMaxLon() >=  180 && BBView.GetMaxLon() - 360 > ls_list->pcs->cs_lon_avg) ||
4101 //                     (BBView.GetMinLon() <= -180 && BBView.GetMinLon() + 360 < ls_list->pcs->cs_lon_avg)))
4102                 {
4103                     // render the segment
4104                         b_drawit = true;
4105                         seg_vbo_offset = ls_list->pcs->vbo_offset;
4106                         point_count = 2;
4107                  }
4108             }
4109 
4110 
4111 
4112 
4113 
4114             if( b_drawit) {
4115                 // render the segment
4116 
4117 #ifndef USE_ANDROID_GLES2
4118                 if(b_useVBO){
4119                     glVertexPointer(2, GL_FLOAT, 2 * sizeof(float), (GLvoid *)(seg_vbo_offset));
4120                     glDrawArrays(GL_LINE_STRIP, 0, point_count);
4121                 }
4122                 else{
4123                     glVertexPointer(2, GL_FLOAT, 2 * sizeof(float), (unsigned char *)vertex_buffer + seg_vbo_offset);
4124                     glDrawArrays(GL_LINE_STRIP, 0, point_count);
4125                 }
4126 #else
4127 
4128 //                unsigned char *buffer = (unsigned char *)vertex_buffer + seg_vbo_offset;
4129 //                float *bufBase = (float *)buffer;
4130 //                glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufBase);
4131 //                glEnableVertexAttribArray(pos);
4132 
4133                 if(b_useVBO){
4134                     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (GLvoid *)(seg_vbo_offset));
4135                     glEnableVertexAttribArray(pos);
4136                     glDrawArrays(GL_LINE_STRIP, 0, point_count);
4137                 }
4138                 else{
4139 #if 1
4140                     unsigned char *buffer = (unsigned char *)vertex_buffer;
4141                     buffer += seg_vbo_offset;
4142                     float *bufBase = (float *)buffer;
4143                     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufBase);
4144                     glEnableVertexAttribArray(pos);
4145                     glDrawArrays(GL_LINE_STRIP, 0, point_count);
4146 
4147 #else
4148                     unsigned char *buffer = (unsigned char *)vertex_buffer;
4149                     float *bufBase = (float *)buffer;
4150                     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufBase);
4151                     glEnableVertexAttribArray(pos);
4152                     glDrawArrays(GL_LINE_STRIP, seg_vbo_offset/(2 * sizeof(float)), point_count);
4153 #endif
4154                 }
4155 
4156 #endif
4157             }
4158         }
4159         ls_list = ls_list->next;
4160     }
4161 
4162      if(b_useVBO)
4163          glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
4164 
4165 #ifndef USE_ANDROID_GLES2
4166     glDisableClientState(GL_VERTEX_ARRAY);            // deactivate vertex array
4167     glPopMatrix();
4168 #else
4169     // Restore shader TransForm Matrix to identity.
4170     mat4x4 IM;
4171     mat4x4_identity(IM);
4172     GLint matlocf = glGetUniformLocation(S52color_tri_shader_program,"TransformMatrix");
4173     glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
4174 #endif
4175 
4176 
4177     glDisable( GL_LINE_STIPPLE );
4178     glDisable( GL_LINE_SMOOTH );
4179     glDisable( GL_BLEND );
4180 
4181 #endif                  // OpenGL
4182 
4183     return 1;
4184 }
4185 
4186 
4187 // Line Simple Style
4188 int s52plib::RenderLS( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
4189 {
4190     // catch legacy PlugIns (e.g.s63_pi)
4191     if( rzRules->obj->m_n_lsindex  && !rzRules->obj->m_ls_list)
4192         return RenderLSLegacy(rzRules, rules, vp);
4193 
4194     // catch improperly coded edge arrays, usually seen on cm93
4195     if( !rzRules->obj->m_n_lsindex  && !rzRules->obj->m_ls_list)
4196         return 0;
4197 
4198     S52color *c;
4199     int w;
4200 
4201     char *str = (char*) rules->INSTstr;
4202     c = getColor( str + 7 ); // Colour
4203     wxColour color( c->R, c->G, c->B );
4204     w = atoi( str + 5 ); // Width
4205 
4206     double scale_factor = vp->ref_scale/vp->chart_scale;
4207     double scaled_line_width = wxMax((scale_factor - g_overzoom_emphasis_base), 1);
4208     bool b_wide_line = g_oz_vector_scale && vp->b_quilt && (scale_factor > g_overzoom_emphasis_base);
4209 
4210     wxPen wide_pen(*wxBLACK_PEN);
4211     wxDash dashw[2];
4212     dashw[0] = 3;
4213     dashw[1] = 1;
4214 
4215     if( b_wide_line)
4216     {
4217         int w = wxMax(scaled_line_width, 2);            // looks better
4218         w = wxMin(w, 50);                               // upper bound
4219         wide_pen.SetWidth( w );
4220         wide_pen.SetColour(color);
4221 
4222         if( !strncmp( str, "DOTT", 4 ) ) {
4223             dashw[0] = 1;
4224             wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
4225             wide_pen.SetDashes( 2, dashw );
4226         }
4227         else if( !strncmp( str, "DASH", 4 ) ){
4228             wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
4229             if( m_pdc){ //DC mode
4230                 dashw[0] = 1;
4231                 dashw[1] = 2;
4232             }
4233 
4234             wide_pen.SetDashes( 2, dashw );
4235         }
4236     }
4237 
4238     wxPen thispen(color, w, wxPENSTYLE_SOLID);
4239     wxDash dash1[2];
4240 
4241     if( m_pdc) //DC mode
4242     {
4243         if( !strncmp( str, "DOTT", 4 ) ) {
4244             thispen.SetStyle(wxPENSTYLE_USER_DASH);
4245             dash1[0] = 1;
4246             dash1[1] = 2;
4247             thispen.SetDashes( 2, dash1 );
4248         }
4249         else if( !strncmp( str, "DASH", 4 ) ){
4250             thispen.SetStyle(wxPENSTYLE_SHORT_DASH);
4251         }
4252 
4253         if(b_wide_line)
4254             m_pdc->SetPen( wide_pen );
4255         else
4256             m_pdc->SetPen( thispen );
4257 
4258     }
4259 #ifdef ocpnUSE_GL
4260     else // OpenGL mode
4261     {
4262 #ifndef ocpnUSE_GLES
4263         glColor3ub( c->R, c->G, c->B );
4264 #endif
4265         glDisable( GL_LINE_SMOOTH );
4266 
4267         //    Set drawing width
4268         if( w > 1 ) {
4269             GLint parms[2];
4270             glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
4271             if( w > parms[1] )
4272                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, parms[1]) );
4273             else
4274                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, w) );
4275         } else
4276             glLineWidth( wxMax(g_GLMinCartographicLineWidth, 1) );
4277 
4278 #ifndef ocpnUSE_GLES // linestipple is emulated poorly
4279             if( !strncmp( str, "DASH", 4 ) ) {
4280                 glLineStipple( 1, 0x3F3F );
4281                 glEnable( GL_LINE_STIPPLE );
4282             }
4283             else if( !strncmp( str, "DOTT", 4 ) ) {
4284                 glLineStipple( 1, 0x3333 );
4285                 glEnable( GL_LINE_STIPPLE );
4286             }
4287             else
4288                 glDisable( GL_LINE_STIPPLE );
4289 #endif
4290 
4291 #ifndef __OCPN__ANDROID__
4292             if(w >= 2 && m_GLLineSmoothing){
4293                 glEnable( GL_LINE_SMOOTH );
4294                 glEnable( GL_BLEND );
4295             }
4296 #endif
4297     }
4298 #endif
4299 
4300 
4301     //    Get a true pixel clipping/bounding box from the vp
4302     wxPoint pbb = vp->GetPixFromLL( vp->clat, vp->clon );
4303     int xmin_ = pbb.x - (vp->rv_rect.width / 2) - (4 * scaled_line_width);
4304     int xmax_ = xmin_ + vp->rv_rect.width + (8 * scaled_line_width);
4305     int ymin_ = pbb.y - (vp->rv_rect.height / 2) - (4 * scaled_line_width) ;
4306     int ymax_ = ymin_ + vp->rv_rect.height + (8 * scaled_line_width);
4307 
4308     int x0, y0, x1, y1;
4309 
4310     //  Get the current display priority
4311     //  Default comes from the LUP, unless overridden
4312     int priority_current = rzRules->LUP->DPRI - '0';
4313     if(rzRules->obj->m_DPRI >= 0)
4314         priority_current = rzRules->obj->m_DPRI;
4315 
4316     if( rzRules->obj->m_ls_list )
4317     {
4318         float *ppt;
4319 
4320         unsigned char *vbo_point = (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
4321         line_segment_element *ls = rzRules->obj->m_ls_list;
4322 
4323 #ifdef ocpnUSE_GL
4324 #ifndef USE_ANDROID_GLES2
4325         if( !m_pdc && !b_wide_line)
4326             glBegin( GL_LINES );
4327 #endif
4328 #endif
4329         while(ls){
4330             if( ls->priority == priority_current  ) {
4331 
4332                 int nPoints;
4333             // fetch the first point
4334                 if( (ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV) ){
4335                     ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
4336                     nPoints = ls->pedge->nCount;
4337                 }
4338                 else{
4339                     ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
4340                     nPoints = 2;
4341                 }
4342 
4343                 wxPoint l;
4344                 GetPointPixSingle( rzRules, ppt[1], ppt[0], &l, vp );
4345                 ppt += 2;
4346 
4347                 for(int ip=0 ; ip < nPoints - 1 ; ip++){
4348                     wxPoint r;
4349                     GetPointPixSingle( rzRules, ppt[1], ppt[0], &r, vp );
4350                             //        Draw the edge as point-to-point
4351                     x0 = l.x, y0 = l.y;
4352                     x1 = r.x, y1 = r.y;
4353 
4354                         // Do not draw null segments
4355                     if( ( x0 != x1 ) || ( y0 != y1 ) ){
4356 
4357                         if(m_pdc){
4358 
4359                             if( cohen_sutherland_line_clip_i( &x0, &y0, &x1, &y1, xmin_, xmax_,
4360                                     ymin_, ymax_ ) != Invisible )
4361                                     m_pdc->DrawLine( x0, y0, x1, y1 );
4362                         }
4363 #ifdef ocpnUSE_GL
4364 #ifndef USE_ANDROID_GLES2
4365                         else {
4366                             // simplified faster test, let opengl do the rest
4367                             if((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) &&
4368                                 (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) {
4369                                 if(!b_wide_line) {
4370                                     glVertex2i( x0, y0 );
4371                                     glVertex2i( x1, y1 );
4372                                 } else
4373                                     PLIBDrawGLThickLine( x0, y0, x1, y1, wide_pen, true );
4374                             }
4375                         }
4376 #endif
4377 #endif
4378                     }
4379 
4380                     l = r;
4381                     ppt += 2;
4382                 }
4383             }
4384 
4385             ls = ls->next;
4386         }
4387 #ifdef ocpnUSE_GL
4388 #ifndef USE_ANDROID_GLES2
4389     if(!m_pdc && !b_wide_line)
4390             glEnd();
4391 #endif
4392 #endif
4393     }
4394 
4395 #ifdef ocpnUSE_GL
4396     if( !m_pdc ){
4397         glDisable( GL_LINE_STIPPLE );
4398         glDisable( GL_LINE_SMOOTH );
4399         glDisable( GL_BLEND );
4400     }
4401 #endif
4402     return 1;
4403 }
4404 
4405 // Line Simple Style
4406 int s52plib::RenderLSLegacy( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
4407 {
4408     if( !rzRules->obj->m_chart_context->chart )
4409         return RenderLSPlugIn( rzRules, rules, vp );
4410 
4411     // Must be cm93
4412     S52color *c;
4413     int w;
4414 
4415     char *str = (char*) rules->INSTstr;
4416     c = getColor( str + 7 ); // Colour
4417     wxColour color( c->R, c->G, c->B );
4418     w = atoi( str + 5 ); // Width
4419 
4420     double scale_factor = vp->ref_scale/vp->chart_scale;
4421     double scaled_line_width = wxMax((scale_factor - g_overzoom_emphasis_base), 1);
4422     bool b_wide_line = g_oz_vector_scale && vp->b_quilt && (scale_factor > g_overzoom_emphasis_base);
4423 
4424     wxPen wide_pen(*wxBLACK_PEN);
4425     wxDash dashw[2];
4426     dashw[0] = 3;
4427     dashw[1] = 1;
4428 
4429     if( b_wide_line)
4430     {
4431         int w = wxMax(scaled_line_width, 2);            // looks better
4432         w = wxMin(w, 50);                               // upper bound
4433         wide_pen.SetWidth( w );
4434         wide_pen.SetColour(color);
4435 
4436         if( !strncmp( str, "DOTT", 4 ) ) {
4437             dashw[0] = 1;
4438             wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
4439             wide_pen.SetDashes( 2, dashw );
4440         }
4441         else if( !strncmp( str, "DASH", 4 ) ){
4442             wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
4443             if( m_pdc){ //DC mode
4444                 dashw[0] = 1;
4445                 dashw[1] = 2;
4446             }
4447 
4448             wide_pen.SetDashes( 2, dashw );
4449         }
4450     }
4451 
4452     wxPen thispen(color, w, wxPENSTYLE_SOLID);
4453     wxDash dash1[2];
4454 
4455     if( m_pdc) //DC mode
4456     {
4457         if( !strncmp( str, "DOTT", 4 ) ) {
4458             thispen.SetStyle(wxPENSTYLE_USER_DASH);
4459             dash1[0] = 1;
4460             dash1[1] = 2;
4461             thispen.SetDashes( 2, dash1 );
4462         }
4463         else if( !strncmp( str, "DASH", 4 ) ){
4464             thispen.SetStyle(wxPENSTYLE_SHORT_DASH);
4465         }
4466 
4467         if(b_wide_line)
4468             m_pdc->SetPen( wide_pen );
4469         else
4470             m_pdc->SetPen( thispen );
4471 
4472     }
4473 
4474 #ifdef ocpnUSE_GL
4475     else // OpenGL mode
4476     {
4477 #ifndef __OCPN__ANDROID__
4478         glColor3ub( c->R, c->G, c->B );
4479 #endif
4480         glDisable( GL_LINE_SMOOTH );
4481 
4482         //    Set drawing width
4483         if( w > 1 ) {
4484             GLint parms[2];
4485             glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
4486             if( w > parms[1] )
4487                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, parms[1]) );
4488             else
4489                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, w) );
4490         } else
4491             glLineWidth( wxMax(g_GLMinCartographicLineWidth, 1) );
4492 
4493 #ifndef ocpnUSE_GLES // linestipple is emulated poorly
4494         if( !strncmp( str, "DASH", 4 ) ) {
4495             glLineStipple( 1, 0x3F3F );
4496             glEnable( GL_LINE_STIPPLE );
4497         }
4498         else if( !strncmp( str, "DOTT", 4 ) ) {
4499             glLineStipple( 1, 0x3333 );
4500             glEnable( GL_LINE_STIPPLE );
4501         }
4502         else
4503             glDisable( GL_LINE_STIPPLE );
4504 #endif
4505 
4506 #ifndef __OCPN__ANDROID__
4507         if(w >= 2 && m_GLLineSmoothing){
4508             glEnable( GL_LINE_SMOOTH );
4509             glEnable( GL_BLEND );
4510         }
4511 #endif
4512     }
4513 #endif
4514 
4515     //    Get a true pixel clipping/bounding box from the vp
4516     wxPoint pbb = vp->GetPixFromLL( vp->clat, vp->clon );
4517     int xmin_ = pbb.x - (vp->rv_rect.width / 2) - (4 * scaled_line_width);
4518     int xmax_ = xmin_ + vp->rv_rect.width + (8 * scaled_line_width);
4519     int ymin_ = pbb.y - (vp->rv_rect.height / 2) - (4 * scaled_line_width) ;
4520     int ymax_ = ymin_ + vp->rv_rect.height + (8 * scaled_line_width);
4521 
4522     int x0, y0, x1, y1;
4523 
4524 
4525     if( rzRules->obj->m_n_lsindex ) {
4526         VE_Hash *ve_hash;
4527         VC_Hash *vc_hash;
4528         ve_hash = (VE_Hash *)rzRules->obj->m_chart_context->m_pve_hash;             // This is cm93
4529         vc_hash = (VC_Hash *)rzRules->obj->m_chart_context->m_pvc_hash;
4530 
4531 
4532         //  Get the current display priority
4533         //  Default comes from the LUP, unless overridden
4534         int priority_current = rzRules->LUP->DPRI - '0';
4535         if(rzRules->obj->m_DPRI >= 0)
4536             priority_current = rzRules->obj->m_DPRI;
4537 
4538         int *index_run;
4539         float *ppt;
4540 
4541         VC_Element *pnode;
4542 
4543 #ifdef ocpnUSE_GL
4544 #ifndef USE_ANDROID_GLES2
4545         if(!b_wide_line)
4546             glBegin( GL_LINES );
4547 #endif
4548 
4549 #ifdef USE_ANDROID_GLES2
4550             glUseProgram(S52color_tri_shader_program);
4551 
4552             float fBuf[4];
4553             GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
4554             glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), fBuf);
4555             glEnableVertexAttribArray(pos);
4556 
4557             float colorv[4];
4558             colorv[0] = c->R / float(256);
4559             colorv[1] = c->G / float(256);
4560             colorv[2] = c->B / float(256);
4561             colorv[3] = 1.0;
4562 
4563             GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
4564             glUniform4fv(colloc, 1, colorv);
4565 #endif
4566 
4567 #endif
4568         for( int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++ ) {
4569             int seg_index = iseg * 3;
4570             index_run = &rzRules->obj->m_lsindex_array[seg_index];
4571 
4572             //  Get first connected node
4573             unsigned int inode = *index_run++;
4574 
4575             //  Get the edge
4576             unsigned int enode = *index_run++;
4577             VE_Element *pedge = 0;
4578             if(enode)
4579                 pedge = (*ve_hash)[enode];
4580 
4581             //  Get last connected node
4582             unsigned int jnode = *index_run++;
4583 
4584             int nls;
4585             if(pedge) {
4586                 //  Here we decide to draw or not based on the highest priority seen for this segment
4587                 //  That is, if this segment is going to be drawn at a higher priority later, then "continue", and don't draw it here.
4588 
4589                 // This logic is not perfectly right for one case:
4590                 // If the segment has only two end connected nodes, and no intermediate edge,
4591                 // then we have no good way to evaluate the priority.
4592                 // This is due to the fact that priority is only precalculated for edge segments, not connector nodes.
4593                 // Only thing to do is take the conservative approach and draw the segment, in this case.
4594                 if( pedge->nCount && pedge->max_priority != priority_current )
4595                     continue;
4596                 nls = pedge->nCount + 1;
4597             } else
4598                 nls = 1;
4599 
4600             wxPoint l;
4601             bool lastvalid = false;
4602             for( int ipc = 0; ipc < nls + 1; ipc++ ) {
4603                 ppt = 0;
4604                 if( ipc == 0 ) {
4605                     if( inode ) {
4606                         pnode = (*vc_hash)[inode];
4607                         if( pnode )
4608                             ppt = pnode->pPoint;
4609                     }
4610                 } else if(ipc == nls) {
4611                     if( ( jnode ) ) {
4612                         pnode = (*vc_hash)[jnode];
4613                         if( pnode )
4614                             ppt = pnode->pPoint;
4615                     }
4616                 } else if(pedge)
4617                     ppt = pedge->pPoints + 2*(ipc-1);
4618 
4619                 if(ppt) {
4620                     wxPoint r;
4621                     GetPointPixSingle( rzRules, ppt[1], ppt[0], &r, vp );
4622 
4623                     if(r.x != INVALID_COORD) {
4624                         if(lastvalid) {
4625                             //        Draw the edge as point-to-point
4626                             x0 = l.x, y0 = l.y;
4627                             x1 = r.x, y1 = r.y;
4628 
4629                             // Do not draw null segments
4630                             if( ( x0 == x1 ) && ( y0 == y1 ) ) continue;
4631 
4632                             if( m_pdc ) {
4633                                 if( cohen_sutherland_line_clip_i( &x0, &y0, &x1, &y1, xmin_, xmax_,
4634                                                                   ymin_, ymax_ ) != Invisible )
4635                                     m_pdc->DrawLine( x0, y0, x1, y1 );
4636                             }
4637 #ifdef ocpnUSE_GL
4638                             else {
4639 
4640 #ifdef USE_ANDROID_GLES2
4641 
4642                                 fBuf[0] = x0;
4643                                 fBuf[1] = y0;
4644                                 fBuf[2] = x1;
4645                                 fBuf[3] = y1;
4646 
4647                                 glDrawArrays(GL_LINES, 0, 2);
4648 
4649 #else
4650                                 // simplified faster test, let opengl do the rest
4651                                 if((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) &&
4652                                    (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) {
4653                                     if(!b_wide_line) {
4654                                         glVertex2i( x0, y0 );
4655                                         glVertex2i( x1, y1 );
4656                                     } else
4657                                         PLIBDrawGLThickLine( x0, y0, x1, y1, wide_pen, true );
4658                                 }
4659 #endif
4660                             }
4661 #endif
4662                         }
4663 
4664                         l = r;
4665                         lastvalid = true;
4666                     } else
4667                         lastvalid = false;
4668                 } else
4669                     lastvalid = false;
4670             }
4671         }
4672 #ifdef ocpnUSE_GL
4673 #ifndef USE_ANDROID_GLES2
4674         if(!b_wide_line)
4675             glEnd();
4676 #endif
4677 #endif
4678     }
4679 #ifdef ocpnUSE_GL
4680     if( !m_pdc ){
4681         glDisable( GL_LINE_STIPPLE );
4682         glDisable( GL_LINE_SMOOTH );
4683         glDisable( GL_BLEND );
4684     }
4685 #endif
4686     return 1;
4687 }
4688 
4689 class PI_connector_segment              // This was extracted verbatim from S63_pi private definition
4690 {
4691 public:
4692     void *start;
4693     void *end;
4694     SegmentType type;
4695     int vbo_offset;
4696     int max_priority;
4697 };
4698 
4699 int s52plib::RenderLSPlugIn( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
4700 {
4701 #ifndef USE_ANDROID_GLES2
4702     S52color *c;
4703     int w;
4704 
4705     char *str = (char*) rules->INSTstr;
4706     c = getColor( str + 7 ); // Colour
4707     wxColour color( c->R, c->G, c->B );
4708     w = atoi( str + 5 ); // Width
4709 
4710     double scale_factor = vp->ref_scale/vp->chart_scale;
4711     double scaled_line_width = wxMax((scale_factor - g_overzoom_emphasis_base), 1);
4712     bool b_wide_line = g_oz_vector_scale && vp->b_quilt && (scale_factor > g_overzoom_emphasis_base);
4713 
4714     wxPen wide_pen(*wxBLACK_PEN);
4715     wxDash dashw[2];
4716     dashw[0] = 3;
4717     dashw[1] = 1;
4718 
4719     if( b_wide_line)
4720     {
4721         int w = wxMax(scaled_line_width, 2);            // looks better
4722         w = wxMin(w, 50);                               // upper bound
4723         wide_pen.SetWidth( w );
4724         wide_pen.SetColour(color);
4725 
4726         if( !strncmp( str, "DOTT", 4 ) ) {
4727             dashw[0] = 1;
4728             wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
4729             wide_pen.SetDashes( 2, dashw );
4730         }
4731         else if( !strncmp( str, "DASH", 4 ) ){
4732             wide_pen.SetStyle(wxPENSTYLE_USER_DASH);
4733             if( m_pdc){ //DC mode
4734                 dashw[0] = 1;
4735                 dashw[1] = 2;
4736             }
4737 
4738             wide_pen.SetDashes( 2, dashw );
4739         }
4740     }
4741 
4742     wxPen thispen(color, w, wxPENSTYLE_SOLID);
4743     wxDash dash1[2];
4744 
4745     if( m_pdc) //DC mode
4746     {
4747         if( !strncmp( str, "DOTT", 4 ) ) {
4748             thispen.SetStyle(wxPENSTYLE_USER_DASH);
4749             dash1[0] = 1;
4750             dash1[1] = 2;
4751             thispen.SetDashes( 2, dash1 );
4752         }
4753         else if( !strncmp( str, "DASH", 4 ) ){
4754             thispen.SetStyle(wxPENSTYLE_SHORT_DASH);
4755         }
4756 
4757         if(b_wide_line)
4758             m_pdc->SetPen( wide_pen );
4759         else
4760             m_pdc->SetPen( thispen );
4761 
4762     }
4763 
4764     #ifdef ocpnUSE_GL
4765     else // OpenGL mode
4766     {
4767         glColor3ub( c->R, c->G, c->B );
4768 
4769         //    Set drawing width
4770         if( w > 1 ) {
4771             GLint parms[2];
4772             glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
4773             if( w > parms[1] )
4774                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, parms[1]) );
4775             else
4776                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, w) );
4777         } else
4778             glLineWidth( wxMax(g_GLMinCartographicLineWidth, 1) );
4779 
4780         #ifndef ocpnUSE_GLES // linestipple is emulated poorly
4781             if( !strncmp( str, "DASH", 4 ) ) {
4782                 glLineStipple( 1, 0x3F3F );
4783                 glEnable( GL_LINE_STIPPLE );
4784             }
4785             else if( !strncmp( str, "DOTT", 4 ) ) {
4786                 glLineStipple( 1, 0x3333 );
4787                 glEnable( GL_LINE_STIPPLE );
4788             }
4789             else
4790                 glDisable( GL_LINE_STIPPLE );
4791             #endif
4792 
4793     }
4794     #endif
4795 
4796 
4797     //    Get a true pixel clipping/bounding box from the vp
4798     wxPoint pbb = vp->GetPixFromLL( vp->clat, vp->clon );
4799     int xmin_ = pbb.x - (vp->rv_rect.width / 2) - (4 * scaled_line_width);
4800     int xmax_ = xmin_ + vp->rv_rect.width + (8 * scaled_line_width);
4801     int ymin_ = pbb.y - (vp->rv_rect.height / 2) - (4 * scaled_line_width) ;
4802     int ymax_ = ymin_ + vp->rv_rect.height + (8 * scaled_line_width);
4803 
4804     int x0, y0, x1, y1;
4805 
4806     //  Get the current display priority
4807     //  Default comes from the LUP, unless overridden
4808     int priority_current = rzRules->LUP->DPRI - '0';
4809     if(rzRules->obj->m_DPRI >= 0)
4810         priority_current = rzRules->obj->m_DPRI;
4811 
4812     if( rzRules->obj->m_ls_list_legacy )
4813     {
4814                  float *ppt;
4815 
4816                 VE_Element *pedge;
4817 
4818 
4819                 PI_connector_segment *pcs;
4820 
4821                 unsigned char *vbo_point = (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
4822                 PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
4823 
4824                 #ifdef ocpnUSE_GL
4825                 if(!b_wide_line && !m_pdc)
4826                     glBegin( GL_LINES );
4827                 #endif
4828 
4829                     while(ls){
4830                         if( ls->priority == priority_current  ) {
4831 
4832                             int nPoints;
4833                             // fetch the first point
4834                             if(ls->type == TYPE_EE){
4835                                 pedge = (VE_Element *)ls->private0;
4836                                 ppt = (float *)(vbo_point + pedge->vbo_offset);
4837                                 nPoints = pedge->nCount;
4838                             }
4839                             else{
4840                                 pcs = (PI_connector_segment *)ls->private0;
4841                                 ppt = (float *)(vbo_point + pcs->vbo_offset);
4842                                 nPoints = 2;
4843                             }
4844 
4845                             wxPoint l;
4846                             GetPointPixSingle( rzRules, ppt[1], ppt[0], &l, vp );
4847                             ppt += 2;
4848 
4849                             for(int ip=0 ; ip < nPoints - 1 ; ip++){
4850                                 wxPoint r;
4851                                 GetPointPixSingle( rzRules, ppt[1], ppt[0], &r, vp );
4852                                 //        Draw the edge as point-to-point
4853                                 x0 = l.x, y0 = l.y;
4854                                 x1 = r.x, y1 = r.y;
4855 
4856                                 // Do not draw null segments
4857                                 if( ( x0 != x1 ) || ( y0 != y1 ) ){
4858 
4859                                     if(m_pdc){
4860 
4861                                         if( cohen_sutherland_line_clip_i( &x0, &y0, &x1, &y1, xmin_, xmax_,
4862                                             ymin_, ymax_ ) != Invisible )
4863                                             m_pdc->DrawLine( x0, y0, x1, y1 );
4864                                     }
4865                                     #ifdef ocpnUSE_GL
4866                                     else {
4867                                         // simplified faster test, let opengl do the rest
4868                                         if((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) &&
4869                                             (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) {
4870                                             if(!b_wide_line) {
4871                                                 glVertex2i( x0, y0 );
4872                                                 glVertex2i( x1, y1 );
4873                                             } else
4874                                                 PLIBDrawGLThickLine( x0, y0, x1, y1, wide_pen, true );
4875                                             }
4876                                     }
4877                                     #endif
4878 
4879                                 }
4880 
4881                                 l = r;
4882                                 ppt += 2;
4883                             }
4884                         }
4885 
4886                         ls = ls->next;
4887                     }
4888                     #ifdef ocpnUSE_GL
4889                     if(!b_wide_line && !m_pdc)
4890                         glEnd();
4891                     #endif
4892     }
4893 
4894     #ifdef ocpnUSE_GL
4895     if( !m_pdc )
4896         glDisable( GL_LINE_STIPPLE );
4897     #endif
4898 
4899 #endif
4900         return 1;
4901 }
4902 
4903 // Line Simple Style, Dashed, using GLSL
4904 int s52plib::RenderLS_Dash_GLSL( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
4905 {
4906 #ifdef USE_ANDROID_GLES2
4907 #if 0
4908         GLuint format = GL_RGBA;
4909         GLuint internalformat = format;
4910         int stride = 4;
4911 
4912         int tex_w = 32;
4913         int dash = 16;
4914         unsigned char bufDot[ 2 * tex_w * stride];
4915         for(int i=0 ; i < tex_w ; i++){
4916             bufDot[i * stride] = 255;
4917             bufDot[i * stride +1] = 0;
4918             bufDot[i * stride +2] = 0;
4919             if(i < dash)
4920                 bufDot[i * stride + 3] = 255;
4921             else
4922                 bufDot[i * stride + 3] = 0;
4923 
4924  //           bufDot[i * stride + 3] = 255;
4925 
4926         }
4927 
4928 //         for(int i=0 ; i < sizeof(bufDot) ; i++){
4929 //             bufDot[i] = 255;
4930 //         }
4931 
4932  /*       for(int i=0 ; i < tex_w ; i++){
4933             bufDot[i * stride] = 255;
4934             bufDot[i * stride +1] = 0;
4935             bufDot[i * stride +2] = 0;
4936             if(i < dash)
4937                 bufDot[i * stride + 3] = 255;
4938             else
4939                 bufDot[i * stride + 3] = 0;
4940         }
4941  */
4942         GLuint textureDot = -1;
4943 
4944         glGenTextures( 1, &textureDot );
4945         glBindTexture( GL_TEXTURE_2D, textureDot );
4946 
4947         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
4948         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
4949         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST/*GL_LINEAR*/ );
4950         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
4951 
4952         glTexImage2D( GL_TEXTURE_2D, 0, internalformat, tex_w, 1, 0,  format, GL_UNSIGNED_BYTE, bufDot );
4953 
4954         glEnable( GL_TEXTURE_2D );
4955 
4956         glEnable( GL_BLEND );
4957         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
4958 
4959 #endif
4960 
4961 //    ocpnDC *odc = new ocpnDC(*cc1->GetglCanvas());
4962 
4963     S52color *c;
4964 
4965     char *str = (char*) rules->INSTstr;
4966     c = getColor( str + 7 ); // Colour
4967     wxColour color( c->R, c->G, c->B );
4968     int w = atoi( str + 5 ); // Width
4969 
4970     double scale_factor = vp->ref_scale/vp->chart_scale;
4971     double scaled_line_width = wxMax((scale_factor - g_overzoom_emphasis_base), 1);
4972 
4973     wxPen thispen(color, w, wxPENSTYLE_SOLID);
4974 
4975     wxDash dash1[2];
4976 
4977     if( !strncmp( str, "DOTT", 4 ) ) {
4978         thispen.SetStyle(wxPENSTYLE_USER_DASH);
4979         dash1[0] = 1;
4980         dash1[1] = 2;
4981         thispen.SetDashes( 2, dash1 );
4982     }
4983     else if( !strncmp( str, "DASH", 4 ) ){
4984         thispen.SetStyle(wxPENSTYLE_USER_DASH);
4985         dash1[0] = 2;
4986         dash1[1] = 4;
4987         thispen.SetDashes( 2, dash1 );
4988     }
4989 
4990 //    odc->SetPen( thispen );
4991 
4992         //    Set GL drawing width
4993 /*
4994         if( w > 1 ) {
4995             GLint parms[2];
4996             glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
4997             if( w > parms[1] )
4998                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, parms[1]) );
4999             else
5000                 glLineWidth( wxMax(g_GLMinCartographicLineWidth, w) );
5001         } else
5002             glLineWidth( wxMax(g_GLMinCartographicLineWidth, 1) );
5003 
5004         if(w >= 2){
5005             glEnable( GL_LINE_SMOOTH );
5006             glEnable( GL_BLEND );
5007         }
5008 */
5009 
5010    //    Set drawing width
5011 
5012    float lineWidth = w;
5013    GLint parms[2];
5014    glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
5015    GLint parmsa[2];
5016    glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parmsa[0] );
5017    GLint parmsb[2];
5018    glGetIntegerv( GL_SMOOTH_LINE_WIDTH_GRANULARITY, &parmsb[0] );
5019 
5020    if( w > 1 ) {
5021        if( w > parms[1] )
5022            lineWidth = wxMax(g_GLMinCartographicLineWidth, parms[1]);
5023        else
5024            lineWidth = wxMax(g_GLMinCartographicLineWidth, w);
5025    } else
5026        lineWidth = wxMax(g_GLMinCartographicLineWidth, 1);
5027 
5028    // Manage super high density displays
5029        float target_w_mm = 0.5 * w;;
5030        if(GetPPMM() > 7){               // arbitrary, leaves average desktop/laptop display untweaked...
5031             target_w_mm = ((float)w) / 6.0;  // Target width in mm
5032         //  The value "w" comes from S52 library CNSY procedures, in "nominal" pixels
5033         // the value "6" comes from semi-standard LCD display densities
5034         // or something like 0.18 mm pitch, or 6 pix per mm.
5035             lineWidth =  wxMax(g_GLMinCartographicLineWidth, target_w_mm * GetPPMM() );
5036        }
5037 
5038        glDisable( GL_LINE_SMOOTH );
5039        glEnable( GL_BLEND );                    // for shader
5040 
5041 #ifdef __OCPN__ANDROID__
5042        lineWidth = wxMin(lineWidth, parms[1]);
5043        glLineWidth(lineWidth);
5044 
5045 #else
5046        glLineWidth(lineWidth);
5047        if(lineWidth > 4.0){
5048            glEnable( GL_LINE_SMOOTH );
5049        }
5050 #endif
5051 
5052     //    Get a true pixel clipping/bounding box from the vp
5053     wxPoint pbb = vp->GetPixFromLL( vp->clat, vp->clon );
5054     int xmin_ = pbb.x - (vp->rv_rect.width / 2) - (4 * scaled_line_width);
5055     int xmax_ = xmin_ + vp->rv_rect.width + (8 * scaled_line_width);
5056     int ymin_ = pbb.y - (vp->rv_rect.height / 2) - (4 * scaled_line_width) ;
5057     int ymax_ = ymin_ + vp->rv_rect.height + (8 * scaled_line_width);
5058 
5059     int x0, y0, x1, y1;
5060 
5061     //  Get the current display priority
5062     //  Default comes from the LUP, unless overridden
5063     int priority_current = rzRules->LUP->DPRI - '0';
5064     if(rzRules->obj->m_DPRI >= 0)
5065         priority_current = rzRules->obj->m_DPRI;
5066 
5067     glUseProgram( S52Dash_shader_program );
5068 
5069     // Get pointers to the attributes in the program.
5070     GLint mPosAttrib = glGetAttribLocation( S52Dash_shader_program, "position" );
5071 
5072     GLint startPos  = glGetUniformLocation( S52Dash_shader_program, "startPos" );
5073     GLint texWidth  = glGetUniformLocation( S52Dash_shader_program, "texWidth" );
5074 
5075     float colorv[4];
5076     colorv[0] = color.Red() / float(256);
5077     colorv[1] = color.Green() / float(256);
5078     colorv[2] = color.Blue() / float(256);
5079     colorv[3] = 1.0; //transparency;
5080 
5081     GLint colloc = glGetUniformLocation(S52Dash_shader_program,"color");
5082     glUniform4fv(colloc, 1, colorv);
5083 
5084     // Select the active texture unit.
5085     glActiveTexture( GL_TEXTURE0 );
5086 
5087     // Bind our texture to the texturing target.
5088     //                            glBindTexture( GL_TEXTURE_2D, textureDot );
5089 
5090     // Set up the texture sampler to texture unit 0
5091     GLint texUni = glGetUniformLocation( S52Dash_shader_program, "uTex" );
5092     glUniform1i( texUni, 0 );
5093 
5094     float width = 32; //tex_w;
5095     glUniform1f(texWidth, width);
5096 
5097     // Disable VBO's (vertex buffer objects) for attributes.
5098     glBindBuffer( GL_ARRAY_BUFFER, 0 );
5099     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
5100 
5101 
5102     // Rotate
5103     mat4x4 I, Q;
5104     mat4x4_identity(I);
5105 
5106     //mat4x4_rotate_Z(Q, I, vp->rotation);
5107 
5108     // Translate
5109     //             Q[3][0] = r.x-pivot_x;
5110     //             Q[3][1] = r.y-pivot_y;
5111 
5112 
5113     //mat4x4 X;
5114     //mat4x4_mul(X, (float (*)[4])vp->vp_transform, Q);
5115 
5116     GLint matloc = glGetUniformLocation(S52Dash_shader_program,"TransformMatrix");
5117     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)I);
5118 
5119     if( rzRules->obj->m_ls_list )
5120     {
5121         float *ppt;
5122 
5123         unsigned char *vbo_point = (unsigned char *)rzRules->obj->m_chart_context->chart->GetLineVertexBuffer();;
5124         line_segment_element *ls = rzRules->obj->m_ls_list;
5125 
5126             while(ls){
5127                 if( ls->priority == priority_current  ) {
5128 
5129                     int nPoints;
5130                     // fetch the first point
5131                     if( (ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV) ){
5132                         ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
5133                         nPoints = ls->pedge->nCount;
5134                     }
5135                     else{
5136                         ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
5137                         nPoints = 2;
5138                     }
5139 
5140                     wxPoint l;
5141                     GetPointPixSingle( rzRules, ppt[1], ppt[0], &l, vp );
5142                     ppt += 2;
5143 
5144                     for(int ip=0 ; ip < nPoints - 1 ; ip++){
5145                         wxPoint r;
5146                         GetPointPixSingle( rzRules, ppt[1], ppt[0], &r, vp );
5147                         //        Draw the edge as point-to-point
5148                         x0 = l.x, y0 = l.y;
5149                         x1 = r.x, y1 = r.y;
5150 
5151                         // Do not draw null segments
5152                         if( ( x0 != x1 ) || ( y0 != y1 ) ){
5153 
5154 #if 0
5155                                 if( cohen_sutherland_line_clip_i( &x0, &y0, &x1, &y1, xmin_, xmax_,
5156                                     ymin_, ymax_ ) != Invisible )
5157                                     odc->DrawLine( x0, y0, x1, y1, true );
5158 #else
5159 
5160 
5161 
5162                             // segment must be at least on-screen....
5163                             if((x0 > xmin_ || x1 > xmin_) && (x0 < xmax_ || x1 < xmax_) &&
5164                                 (y0 > ymin_ || y1 > ymin_) && (y0 < ymax_ || y1 < ymax_)) {
5165 
5166                                 //  And intersecting the current clip rectangle
5167                                 int xa = x0;
5168                                 int xw = x1-x0;
5169                                 if(xw < 0){
5170                                     xa = x1;
5171                                     xw = -xw;
5172                                 }
5173                                 int ya = y0;
5174                                 int yh = y1-y0;
5175                                 if(yh < 0){
5176                                     ya = y1;
5177                                     yh = -yh;
5178                                 }
5179 
5180                                 wxRect rseg(xa, ya, xw, yh);
5181                                 rseg.Inflate(1);        // avoid zero width/height
5182                                 if(rseg.Intersects(m_last_clip_rect))
5183                                 {
5184                                     float coords[4];
5185 
5186                                     coords[0] = x0; coords[1] = y0; coords[2] = x1; coords[3] = y1;
5187 
5188                                     float start[2];
5189                                     start[0] = x0; start[1] = GetOCPNCanvasWindow()->GetSize().y - y0;
5190                                     glUniform2fv(startPos, 1, start);
5191 
5192 
5193                                     // Set the attribute mPosAttrib with the vertices in the screen coordinates...
5194                                     glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
5195                                     glEnableVertexAttribArray( mPosAttrib );
5196 
5197                                     // Perform the actual drawing.
5198                                     glDrawArrays(GL_LINES, 0, 2);
5199                                 }
5200                             }
5201 
5202 #endif
5203 
5204                         }
5205 
5206                         l = r;
5207                         ppt += 2;
5208                     }
5209                 }
5210 
5211                 ls = ls->next;
5212             }
5213     }
5214 
5215 //    delete odc;
5216 #endif
5217 
5218         return 1;
5219 }
5220 
5221 // Line Complex
5222 int s52plib::RenderLC( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
5223 {
5224     //     if(rzRules->obj->Index != 7574)
5225     //         return 0;
5226 
5227     // catch cm93 and legacy PlugIns (e.g.s63_pi)
5228     if( rzRules->obj->m_n_lsindex  && !rzRules->obj->m_ls_list)
5229         return RenderLCLegacy(rzRules, rules, vp);
5230 
5231     wxPoint r;
5232 
5233 
5234     int isym_len = rules->razRule->pos.line.bnbox_w.SYHL + (rules->razRule->pos.line.bnbox_x.LBXC - rules->razRule->pos.line.pivot_x.LICL);
5235     float sym_len = isym_len * canvas_pix_per_mm / 100;
5236     float sym_factor = 1.0; ///1.50;                        // gives nicer effect
5237 
5238     //      Create a color for drawing adjustments outside of HPGL renderer
5239     char *tcolptr = rules->razRule->colRef.LCRF;
5240     S52color *c = getColor( tcolptr + 1 ); // +1 skips "n" in HPGL SPn format
5241     int w = 1; // arbitrary width
5242     wxColour color( c->R, c->G, c->B );
5243     double LOD = 2.0 / vp->view_scale_ppm;              // empirical value, by experiment
5244     LOD = 0; //wxMin(LOD, 10.0);
5245 
5246     //  Get the current display priority
5247     //  Default comes from the LUP, unless overridden
5248     int priority_current = rzRules->LUP->DPRI - '0';
5249     if(rzRules->obj->m_DPRI >= 0)
5250         priority_current = rzRules->obj->m_DPRI;
5251 
5252     if( rzRules->obj->m_n_lsindex ) {
5253 
5254 
5255         // Calculate the size of a work buffer
5256         int max_points = 0;
5257         if( rzRules->obj->m_n_edge_max_points > 0 )
5258             max_points = rzRules->obj->m_n_edge_max_points;
5259         else{
5260             line_segment_element *lsa = rzRules->obj->m_ls_list;
5261 
5262             while(lsa){
5263 
5264                 if( (lsa->ls_type == TYPE_EE) || (lsa->ls_type == TYPE_EE_REV) )
5265                     max_points += lsa->pedge->nCount;
5266                 else
5267                     max_points += 2;
5268 
5269                 lsa = lsa->next;
5270             }
5271         }
5272 
5273         float *ppt;
5274         unsigned char *vbo_point = (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer; //chart->GetLineVertexBuffer();
5275 
5276         //  Allocate some storage for converted points
5277         wxPoint *ptp = (wxPoint *) malloc( ( max_points ) * sizeof(wxPoint) );
5278         double *pdp = (double *)malloc( 2 * ( max_points ) * sizeof(double) );
5279         int *mask = (int *)malloc(  ( max_points ) * sizeof(int) );
5280 
5281         line_segment_element *ls = rzRules->obj->m_ls_list;
5282 
5283         unsigned int index = 0;
5284         unsigned int idouble = 0;
5285         int nls = 0;
5286         wxPoint lp;
5287 
5288         int ndraw = 0;
5289         while(ls){
5290             if( 1/*ls->priority == priority_current*/  ) {
5291 
5292 
5293                 //transcribe the segment in the proper order into the output buffer
5294                 int nPoints;
5295                 int idir = 1;
5296                 bool bcon = false;
5297                 // fetch the first point
5298                 if( (ls->ls_type == TYPE_EE) || (ls->ls_type == TYPE_EE_REV) ){
5299                     ppt = (float *)(vbo_point + ls->pedge->vbo_offset);
5300                     nPoints = ls->pedge->nCount;
5301                     if(ls->ls_type == TYPE_EE_REV)
5302                         idir = -1;
5303 
5304                 }
5305                 else{
5306                     ppt = (float *)(vbo_point + ls->pcs->vbo_offset);
5307                     nPoints = 2;
5308                     bcon = true;
5309                 }
5310 
5311 
5312                 int vbo_index = 0;
5313                 int vbo_inc = 2;
5314                 if( (idir == -1) && !bcon){
5315                     vbo_index = (nPoints-1) * 2;
5316                     vbo_inc = -2;
5317                 }
5318 
5319                 double offset = 0;
5320                 for(int ip=0 ; ip < nPoints ; ip++){
5321                     wxPoint r;
5322                     GetPointPixSingle( rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r, vp );
5323                     if( 1/*(r.x != lp.x) || (r.y != lp.y)*/ ){
5324                         mask[index] = (ls->priority == priority_current)?1:0;
5325                         ptp[index++] = r;
5326                         pdp[idouble++] = ppt[vbo_index];
5327                         pdp[idouble++] = ppt[vbo_index + 1];
5328 
5329                         nls++;
5330                     }
5331                     else{               // sKipping point
5332                     }
5333 
5334                     lp = r;
5335                     vbo_index += vbo_inc;
5336                 }
5337 
5338             }  // priority
5339 
5340             // inspect the next segment to see if it can be connected, or if the chain breaks
5341             int idir = 1;
5342             bool bcon = false;
5343             if(ls->next){
5344 
5345                 int nPoints_next;
5346                 line_segment_element *lsn = ls->next;
5347                 // fetch the first point
5348                 if( (lsn->ls_type == TYPE_EE) || (lsn->ls_type == TYPE_EE_REV) ){
5349                     ppt = (float *)(vbo_point + lsn->pedge->vbo_offset);
5350                     nPoints_next = lsn->pedge->nCount;
5351                     if(lsn->ls_type == TYPE_EE_REV)
5352                         idir = -1;
5353 
5354                 }
5355                 else{
5356                     ppt = (float *)(vbo_point + lsn->pcs->vbo_offset);
5357                     nPoints_next = 2;
5358                     bcon = true;
5359                 }
5360 
5361                 wxPoint ptest;
5362                 if(bcon)
5363                     GetPointPixSingle( rzRules, ppt[1], ppt[0], &ptest, vp );
5364 
5365                 else{
5366                     if(idir == 1)
5367                         GetPointPixSingle( rzRules, ppt[1], ppt[0], &ptest, vp );
5368 
5369                     else{
5370                     // fetch the last point
5371                         int index_last_next = (nPoints_next-1) * 2;
5372                         GetPointPixSingle( rzRules, ppt[index_last_next +1], ppt[index_last_next], &ptest, vp );
5373                     }
5374                 }
5375 
5376                 // try to match the correct point in this segment with the last point in the previous segment
5377 
5378                 if(lp != ptest)         // not connectable?
5379                 {
5380                     if(nls){
5381                         wxPoint2DDouble *pReduced = 0;
5382                         int *pMaskOut = 0;
5383                         int nPointReduced = reduceLOD( LOD, nls, pdp, &pReduced, mask, &pMaskOut);
5384 
5385                         wxPoint *ptestp = (wxPoint *) malloc( ( max_points ) * sizeof(wxPoint) );
5386                         GetPointPixArray( rzRules, pReduced, ptestp, nPointReduced, vp );
5387                         free(pReduced);
5388 
5389                         draw_lc_poly( m_pdc, color, w, ptestp, pMaskOut, nPointReduced, sym_len, sym_factor, rules->razRule, vp );
5390                         free(ptestp);
5391                         free(pMaskOut);
5392 
5393                         ndraw++;
5394                     }
5395 
5396                     nls = 0;
5397                     index = 0;
5398                     idouble = 0;
5399                     lp = wxPoint(0,0);
5400                 }
5401 
5402 
5403             }
5404             else{
5405                 // no more segments, so render what is available
5406                 if(nls){
5407                     wxPoint2DDouble *pReduced = 0;
5408                     int *pMaskOut = 0;
5409                     int nPointReduced = reduceLOD( LOD, nls, pdp, &pReduced, mask, &pMaskOut);
5410 
5411                     wxPoint *ptestp = (wxPoint *) malloc( ( max_points ) * sizeof(wxPoint) );
5412                     GetPointPixArray( rzRules, pReduced, ptestp, nPointReduced, vp );
5413                     free(pReduced);
5414 
5415                     draw_lc_poly( m_pdc, color, w, ptestp, pMaskOut, nPointReduced, sym_len, sym_factor, rules->razRule, vp );
5416                     free( ptestp );
5417                     free(pMaskOut);
5418 
5419                 }
5420             }
5421 
5422             ls = ls->next;
5423         }
5424 
5425 
5426         free( ptp );
5427         free(pdp);
5428         free(mask);
5429     }
5430 
5431     return 1;
5432 }
5433 
5434 
5435 int s52plib::reduceLOD( double LOD_meters, int nPoints, double *source, wxPoint2DDouble **dest, int *maskIn, int **maskOut)
5436 {
5437     //      Reduce the LOD of this linestring
5438     std::vector<int> index_keep;
5439     if(nPoints > 5 && (LOD_meters > .01)){
5440 	index_keep.push_back(0);
5441 	index_keep.push_back(nPoints-1);
5442 	index_keep.push_back(nPoints-2);
5443 
5444         DouglasPeucker(source, 1, nPoints-2, LOD_meters, &index_keep);
5445 
5446     }
5447     else {
5448 	index_keep.resize(nPoints);
5449 	// Consider using std::iota here when there is C++11 support.
5450         for(int i = 0 ; i < nPoints ; i++)
5451             index_keep[i] = i;
5452     }
5453 
5454     wxPoint2DDouble *pReduced = (wxPoint2DDouble *)malloc( ( index_keep.size() ) * sizeof(wxPoint2DDouble) );
5455     *dest = pReduced;
5456 
5457     int *pmaskOut = NULL;
5458     if(maskIn){
5459         *maskOut = (int *)malloc( ( index_keep.size() ) * sizeof(int) );
5460         pmaskOut = *maskOut;
5461     }
5462 
5463     double *ppr = source;
5464     int ir = 0;
5465     for(int ip = 0 ; ip < nPoints ; ip++)
5466     {
5467         double x = *ppr++;
5468         double y = *ppr++;
5469         int maskval = 1;
5470         if(maskIn)
5471             maskval = maskIn[ip];
5472         //printf("LOD:  %10g  %10g\n", x, y);
5473 
5474         for(unsigned int j=0 ; j < index_keep.size() ; j++){
5475             if(index_keep[j] == ip){
5476                 if(pmaskOut) pmaskOut[ir] = maskval;
5477                 pReduced[ir++] = wxPoint2DDouble(x, y);
5478                 break;
5479             }
5480         }
5481     }
5482 
5483     return index_keep.size();
5484 }
5485 
5486 
5487 // Line Complex
5488 int s52plib::RenderLCLegacy( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
5489 {
5490     if( !rzRules->obj->m_chart_context->chart )
5491         return RenderLCPlugIn( rzRules, rules, vp );
5492 
5493     //  Must be cm93
5494 
5495     wxPoint r;
5496 
5497     int isym_len = rules->razRule->pos.line.bnbox_w.SYHL;
5498     float sym_len = isym_len * canvas_pix_per_mm / 100;
5499     float sym_factor = 1.0; ///1.50;                        // gives nicer effect
5500 
5501 //      Create a color for drawing adjustments outside of HPGL renderer
5502     char *tcolptr = rules->razRule->colRef.LCRF;
5503     S52color *c = getColor( tcolptr + 1 ); // +1 skips "n" in HPGL SPn format
5504     int w = 1; // arbitrary width
5505     wxColour color( c->R, c->G, c->B );
5506 
5507     //  Get the current display priority
5508     //  Default comes from the LUP, unless overridden
5509     int priority_current = rzRules->LUP->DPRI - '0';
5510     if(rzRules->obj->m_DPRI >= 0)
5511         priority_current = rzRules->obj->m_DPRI;
5512 
5513     if( rzRules->obj->m_n_lsindex ) {
5514 
5515         VE_Hash *ve_hash = (VE_Hash *)rzRules->obj->m_chart_context->m_pve_hash;
5516         VC_Hash *vc_hash = (VC_Hash *)rzRules->obj->m_chart_context->m_pvc_hash;
5517 
5518         unsigned int nls_max;
5519         if( rzRules->obj->m_n_edge_max_points > 0 ) // size has been precalculated on SENC load
5520             nls_max = rzRules->obj->m_n_edge_max_points;
5521         else {
5522             //  Calculate max malloc size required
5523             nls_max = 0;
5524             int *index_run_x = rzRules->obj->m_lsindex_array;
5525             for( int imseg = 0; imseg < rzRules->obj->m_n_lsindex; imseg++ ) {
5526                 index_run_x++; //Skip cNode
5527                 //  Get the edge
5528                 unsigned int enode = *index_run_x;
5529                 if( enode ){
5530                     VE_Element *pedge = (*ve_hash)[enode];
5531                     if(pedge){
5532                         if( pedge->nCount > nls_max )
5533                             nls_max = pedge->nCount;
5534                     }
5535                 }
5536                 index_run_x += 2;
5537             }
5538             rzRules->obj->m_n_edge_max_points = nls_max; // Got it, cache for next time
5539         }
5540 
5541         //  Allocate some storage for converted points
5542         wxPoint *ptp = (wxPoint *) malloc( ( nls_max + 2 ) * sizeof(wxPoint) ); // + 2 allows for end nodes
5543 
5544         int *index_run;
5545         float *ppt;
5546         double easting, northing;
5547         wxPoint pra( 0, 0 );
5548         VC_Element *pnode;
5549 
5550         for( int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++ ) {
5551             int seg_index = iseg * 3;
5552             index_run = &rzRules->obj->m_lsindex_array[seg_index];
5553 
5554             //  Get first connected node
5555             unsigned int inode = *index_run++;
5556             if( inode ) {
5557                 pnode = (*vc_hash)[inode];
5558                 if( pnode ) {
5559                     ppt = pnode->pPoint;
5560                     easting = *ppt++;
5561                     northing = *ppt;
5562                     GetPointPixSingle( rzRules, (float) northing, (float) easting, &pra, vp );
5563                 }
5564                 ptp[0] = pra; // insert beginning node
5565             }
5566 
5567             //  Get the edge
5568             unsigned int enode = *index_run++;
5569             VE_Element *pedge = 0;
5570             if(enode)
5571                 pedge = (*ve_hash)[enode];
5572 
5573             int nls = 0;
5574             if(pedge){
5575             //  Here we decide to draw or not based on the highest priority seen for this segment
5576             //  That is, if this segment is going to be drawn at a higher priority later, then don't draw it here.
5577 
5578             // This logic is not perfectly right for one case:
5579             // If the segment has only two end connected nodes, and no intermediate edge,
5580             // then we have no good way to evaluate the priority.
5581             // This is due to the fact that priority is only precalculated for edge segments, not connector nodes.
5582             // Only thing to do is take the conservative approach and draw the segment, in this case.
5583             if( pedge->nCount ){
5584                 if( pedge->max_priority != priority_current ) continue;
5585             }
5586 
5587                 if( pedge->max_priority != priority_current ) continue;
5588 
5589                 nls = pedge->nCount;
5590 
5591                 ppt = pedge->pPoints;
5592                 for( int ip = 0; ip < nls; ip++ ) {
5593                     easting = *ppt++;
5594                     northing = *ppt++;
5595                     GetPointPixSingle( rzRules, (float) northing, (float) easting, &ptp[ip + 1], vp );
5596                 }
5597             }
5598 
5599             //  Get last connected node
5600             unsigned int jnode = *index_run++;
5601             if( jnode ) {
5602                 pnode = (*vc_hash)[jnode];
5603                 if( pnode ) {
5604                     ppt = pnode->pPoint;
5605                     easting = *ppt++;
5606                     northing = *ppt;
5607                     GetPointPixSingle( rzRules, (float) northing, (float) easting, &pra, vp );
5608                 }
5609                 ptp[nls + 1] = pra; // insert ending node
5610             }
5611 
5612             if( ( inode ) && ( jnode ) ){
5613                 draw_lc_poly( m_pdc, color, w, ptp, NULL, nls + 2, sym_len, sym_factor, rules->razRule, vp );
5614             }
5615             else if(nls){
5616                 draw_lc_poly( m_pdc, color, w, &ptp[1], NULL, nls, sym_len, sym_factor, rules->razRule, vp );
5617             }
5618 
5619         }
5620         free( ptp );
5621     }
5622 
5623     else
5624         if( rzRules->obj->pPolyTessGeo ) {
5625             if( !rzRules->obj->pPolyTessGeo->IsOk() ){ // perform deferred tesselation
5626                 rzRules->obj->pPolyTessGeo->BuildDeferredTess();
5627             }
5628 
5629             PolyTriGroup *pptg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
5630             float *ppolygeo = pptg->pgroup_geom;
5631             if(ppolygeo){
5632                 int ctr_offset = 0;
5633                 for( int ic = 0; ic < pptg->nContours; ic++ ) {
5634 
5635                     int npt = pptg->pn_vertex[ic];
5636                     wxPoint *ptp = (wxPoint *) malloc( ( npt + 1 ) * sizeof(wxPoint) );
5637                     wxPoint *pr = ptp;
5638                     for( int ip = 0; ip < npt; ip++ ) {
5639                         float plon = ppolygeo[( 2 * ip ) + ctr_offset];
5640                         float plat = ppolygeo[( 2 * ip ) + ctr_offset + 1];
5641 
5642                         GetPointPixSingle( rzRules, plat, plon, pr, vp );
5643                         pr++;
5644                     }
5645                     float plon = ppolygeo[ctr_offset]; // close the polyline
5646                     float plat = ppolygeo[ctr_offset + 1];
5647                     GetPointPixSingle( rzRules, plat, plon, pr, vp );
5648 
5649                     draw_lc_poly( m_pdc, color, w, ptp, NULL, npt + 1, sym_len, sym_factor, rules->razRule,
5650                             vp );
5651 
5652                     free( ptp );
5653 
5654                     ctr_offset += npt * 2;
5655                 }
5656             }
5657         }
5658     return 1;
5659 }
5660 
5661 int s52plib::RenderLCPlugIn( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
5662 {
5663     wxPoint r;
5664 
5665     int isym_len = rules->razRule->pos.line.bnbox_w.SYHL;
5666     float sym_len = isym_len * canvas_pix_per_mm / 100;
5667     float sym_factor = 1.0; ///1.50;                        // gives nicer effect
5668 
5669     //      Create a color for drawing adjustments outside of HPGL renderer
5670     char *tcolptr = rules->razRule->colRef.LCRF;
5671     S52color *c = getColor( tcolptr + 1 ); // +1 skips "n" in HPGL SPn format
5672     int w = 1; // arbitrary width
5673     wxColour color( c->R, c->G, c->B );
5674     double LOD = 2.0 / vp->view_scale_ppm;              // empirical value, by experiment
5675     LOD = 0; //wxMin(LOD, 10.0);
5676 
5677     //  Get the current display priority
5678     //  Default comes from the LUP, unless overridden
5679     int priority_current = rzRules->LUP->DPRI - '0';
5680     if(rzRules->obj->m_DPRI >= 0)
5681         priority_current = rzRules->obj->m_DPRI;
5682 
5683 
5684     //  Calculate max malloc size required
5685 
5686     int max_points = 0;
5687     if( rzRules->obj->m_ls_list_legacy )
5688     {
5689         VE_Element *pedge;
5690         PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
5691 
5692         while(ls){
5693             int nPoints;
5694                     // fetch the first point
5695             if(ls->type == TYPE_EE){
5696                 pedge = (VE_Element *)ls->private0;
5697                 nPoints = pedge->nCount;
5698              }
5699              else{
5700                 nPoints = 2;
5701              }
5702 
5703              max_points +=nPoints;
5704 
5705             ls = ls->next;
5706         }
5707     }
5708 
5709 
5710 
5711     if( rzRules->obj->m_ls_list_legacy )
5712     {
5713         float *ppt;
5714 
5715         VE_Element *pedge;
5716 
5717 
5718 
5719         //  Allocate some storage for converted points
5720         wxPoint *ptp = (wxPoint *) malloc( ( max_points + 2 ) * sizeof(wxPoint) ); // + 2 allows for end nodes
5721         double *pdp = (double *)malloc( 2 * ( max_points+ 2 ) * sizeof(double) );
5722 
5723         PI_connector_segment *pcs;
5724 
5725         unsigned char *vbo_point = (unsigned char *)rzRules->obj->m_chart_context->vertex_buffer;
5726         PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
5727 
5728         unsigned int index = 0;
5729         unsigned int idouble = 0;
5730         int nls = 0;
5731         wxPoint lp;
5732 
5733         ls = rzRules->obj->m_ls_list_legacy;
5734         while(ls){
5735             if( ls->priority == priority_current  ) {
5736 
5737 
5738                 //transcribe the segment in the proper order into the output buffer
5739                 int nPoints;
5740                 int idir = 1;
5741                 bool bcon = false;
5742                 // fetch the first point
5743                 if( (ls->type == TYPE_EE) || (ls->type == TYPE_EE_REV) ){
5744                     pedge = (VE_Element *)ls->private0;
5745                     ppt = (float *)(vbo_point + pedge->vbo_offset);
5746                     nPoints = pedge->nCount;
5747                     if(ls->type == TYPE_EE_REV)
5748                         idir = -1;
5749 
5750                 }
5751                 else{
5752                     pcs = (PI_connector_segment *)ls->private0;
5753                     ppt = (float *)(vbo_point + pcs->vbo_offset);
5754                     nPoints = 2;
5755                     bcon = true;
5756                 }
5757 
5758 
5759                 int vbo_index = 0;
5760                 int vbo_inc = 2;
5761                 if( (idir == -1) && !bcon){
5762                     vbo_index = (nPoints-1) * 2;
5763                     vbo_inc = -2;
5764                 }
5765                 for(int ip=0 ; ip < nPoints ; ip++){
5766                     wxPoint r;
5767                     GetPointPixSingle( rzRules, ppt[vbo_index + 1], ppt[vbo_index], &r, vp );
5768 
5769                     if( 1/*(r.x != lp.x) || (r.y != lp.y)*/ ){
5770                         ptp[index++] = r;
5771                         pdp[idouble++] = ppt[vbo_index];
5772                         pdp[idouble++] = ppt[vbo_index + 1];
5773 
5774                         nls++;
5775                     }
5776                     else{               // sKipping point
5777                     }
5778 
5779                     lp = r;
5780                     vbo_index += vbo_inc;
5781                 }
5782 
5783             }  // priority
5784 
5785             // inspect the next segment to see if it can be connected, or if the chain breaks
5786             int idir = 1;
5787             bool bcon = false;
5788             if(ls->next){
5789 
5790                 int nPoints_next;
5791                 PI_line_segment_element *lsn = ls->next;
5792                 // fetch the first point
5793                 if( (lsn->type == TYPE_EE) || (lsn->type == TYPE_EE_REV) ){
5794                     pedge = (VE_Element *)lsn->private0;
5795                     ppt = (float *)(vbo_point + pedge->vbo_offset);
5796                     nPoints_next = pedge->nCount;
5797                     if(lsn->type == TYPE_EE_REV)
5798                         idir = -1;
5799 
5800                 }
5801                 else{
5802                     pcs = (PI_connector_segment *)lsn->private0;
5803                     ppt = (float *)(vbo_point + pcs->vbo_offset);
5804                     nPoints_next = 2;
5805                     bcon = true;
5806                 }
5807 
5808                 wxPoint ptest;
5809                 if(bcon)
5810                     GetPointPixSingle( rzRules, ppt[1], ppt[0], &ptest, vp );
5811 
5812                 else{
5813                     if(idir == 1)
5814                         GetPointPixSingle( rzRules, ppt[1], ppt[0], &ptest, vp );
5815 
5816                     else{
5817                     // fetch the last point
5818                         int index_last_next = (nPoints_next-1) * 2;
5819                         GetPointPixSingle( rzRules, ppt[index_last_next +1], ppt[index_last_next], &ptest, vp );
5820                     }
5821                 }
5822 
5823                 // try to match the correct point in this segment with the last point in the previous segment
5824 
5825                 if(lp != ptest)         // not connectable?
5826                 {
5827                     if(nls){
5828                         wxPoint2DDouble *pReduced = 0;
5829                         int nPointReduced = reduceLOD( LOD, nls, pdp, &pReduced, NULL, NULL);
5830 
5831                         wxPoint *ptestp = (wxPoint *) malloc( ( 2 * ( nPointReduced + 2 )) * sizeof(wxPoint) );
5832                         GetPointPixArray( rzRules, pReduced, ptestp, nPointReduced, vp );
5833                         free(pReduced);
5834 
5835                         draw_lc_poly( m_pdc, color, w, ptestp, NULL, nPointReduced, sym_len, sym_factor, rules->razRule, vp );
5836                         free(ptestp);
5837                     }
5838 
5839                     nls = 0;
5840                     index = 0;
5841                     idouble = 0;
5842                     lp = wxPoint(0,0);
5843                 }
5844 
5845 
5846             }
5847             else{
5848                 // no more segments, so render what is available
5849                 if(nls){
5850                     wxPoint2DDouble *pReduced = 0;
5851                     int nPointReduced = reduceLOD( LOD, nls, pdp, &pReduced, NULL, NULL);
5852 
5853                     wxPoint *ptestp = (wxPoint *) malloc( ( 2 * ( max_points+ 2 ) ) * sizeof(wxPoint) );
5854                     GetPointPixArray( rzRules, pReduced, ptestp, nPointReduced, vp );
5855                     free(pReduced);
5856 
5857                     draw_lc_poly( m_pdc, color, w, ptestp, NULL, nPointReduced, sym_len, sym_factor, rules->razRule, vp );
5858                     free( ptestp );
5859 
5860                 }
5861             }
5862 
5863             ls = ls->next;
5864         }
5865 
5866 
5867 
5868 
5869 
5870         free(ptp);
5871         free(pdp);
5872     }
5873     return 1;
5874 }
5875 
5876 //      Render Line Complex Polyline
5877 
5878 void s52plib::draw_lc_poly( wxDC *pdc, wxColor &color, int width, wxPoint *ptp, int *mask, int npt,
5879         float sym_len, float sym_factor, Rule *draw_rule, ViewPort *vp )
5880 {
5881     if(npt < 2)
5882         return;
5883 
5884     wxPoint r;
5885 
5886     //  We calculate the winding direction of the poly
5887     //  in order to know which side to draw symbol on
5888     double dfSum = 0.0;
5889 
5890     for( int iseg = 0; iseg < npt - 1; iseg++ ) {
5891         dfSum += ptp[iseg].x * ptp[iseg+1].y - ptp[iseg].y * ptp[iseg+1].x;
5892     }
5893     dfSum += ptp[npt-1].x * ptp[0].y - ptp[npt-1].y * ptp[0].x;
5894 
5895     bool cw = dfSum < 0.;
5896 
5897     //    Get a true pixel clipping/bounding box from the vp
5898     wxPoint pbb = vp->GetPixFromLL( vp->clat, vp->clon );
5899     int xmin_ = pbb.x - vp->rv_rect.width / 2;
5900     int xmax_ = xmin_ + vp->rv_rect.width;
5901     int ymin_ = pbb.y - vp->rv_rect.height / 2;
5902     int ymax_ = ymin_ + vp->rv_rect.height;
5903 
5904     int x0, y0, x1, y1;
5905 
5906     if( pdc ) {
5907         wxPen *pthispen = wxThePenList->FindOrCreatePen( color, width, wxPENSTYLE_SOLID );
5908         m_pdc->SetPen( *pthispen );
5909 
5910         int start_seg = 0;
5911         int end_seg = npt - 1;
5912         int inc = 1;
5913 
5914         if( cw ){
5915             start_seg = npt - 1;
5916             end_seg = 0;
5917             inc = -1;
5918         }
5919 
5920         float dx, dy, seg_len, theta;
5921 
5922         bool done = false;
5923         ClipResult res;
5924         int iseg = start_seg;
5925         while( !done ){
5926 
5927             // Do not bother with segments that are invisible
5928 
5929             x0 = ptp[iseg].x;
5930             y0 = ptp[iseg].y;
5931             x1 = ptp[iseg + inc].x;
5932             y1 = ptp[iseg + inc].y;
5933 
5934             //  Also, segments marked (by mask) as invisible
5935             if( mask && !mask[iseg])
5936                 goto next_seg_dc;
5937 
5938             res = cohen_sutherland_line_clip_i( &x0, &y0, &x1, &y1, xmin_, xmax_, ymin_, ymax_ );
5939 
5940             if( res == Invisible )
5941                 goto next_seg_dc;
5942 
5943             dx = ptp[iseg + inc].x - ptp[iseg].x;
5944             dy = ptp[iseg + inc].y - ptp[iseg].y;
5945             seg_len = sqrt( dx * dx + dy * dy );
5946             theta = atan2f( dy, dx );
5947 
5948             if( seg_len >= 1.0 ) {
5949                 if( seg_len <= sym_len * sym_factor ) {
5950                     int xst1 = ptp[iseg].x;
5951                     int yst1 = ptp[iseg].y;
5952                     float xst2, yst2;
5953                     if( seg_len >= sym_len ) {
5954                         xst2 = xst1 + ( sym_len * dx / seg_len );
5955                         yst2 = yst1 + ( sym_len * dy / seg_len );
5956                     } else {
5957                         xst2 = ptp[iseg + inc].x;
5958                         yst2 = ptp[iseg + inc].y;
5959                     }
5960 
5961                     pdc->DrawLine( xst1, yst1, (wxCoord) floor( xst2 ), (wxCoord) floor( yst2 ) );
5962                 }
5963 
5964                 else {
5965                     float s = 0;
5966                     float xs = ptp[iseg].x;
5967                     float ys = ptp[iseg].y;
5968 
5969                     while( s + ( sym_len * sym_factor ) < seg_len ) {
5970                         r.x = (int) xs;
5971                         r.y = (int) ys;
5972                         char *str = draw_rule->vector.LVCT;
5973                         char *col = draw_rule->colRef.LCRF;
5974                         wxPoint pivot( draw_rule->pos.line.pivot_x.LICL,
5975                                 draw_rule->pos.line.pivot_y.LIRW );
5976 
5977                         HPGL->SetTargetDC( pdc );
5978                         HPGL->SetVP(vp);
5979                         HPGL->Render( str, col, r, pivot, pivot, 1.0, theta * 180. / PI, false );
5980 
5981                         xs += sym_len * dx / seg_len * sym_factor;
5982                         ys += sym_len * dy / seg_len * sym_factor;
5983                         s += sym_len * sym_factor;
5984                     }
5985 
5986                     pdc->DrawLine( (int) xs, (int) ys, ptp[iseg + inc].x, ptp[iseg + inc].y );
5987                 }
5988             }
5989 next_seg_dc:
5990             iseg += inc;
5991             if(iseg == end_seg)
5992                 done = true;
5993 
5994         } // while
5995     } // if pdc
5996 
5997 #ifdef ocpnUSE_GL
5998     else // opengl
5999     {
6000         //    Set up the color
6001 #ifndef USE_ANDROID_GLES2
6002         glColor4ub( color.Red(), color.Green(), color.Blue(), color.Alpha() );
6003 #endif
6004 
6005         // Adjust line width up a bit, to improve render quality for GL_BLEND/GL_LINE_SMOOTH
6006         float awidth = wxMax(g_GLMinCartographicLineWidth, (float)width * 0.7);
6007         awidth = wxMax(awidth, 1.5);
6008         glLineWidth( awidth );
6009 
6010         int start_seg = 0;
6011         int end_seg = npt - 1;
6012         int inc = 1;
6013 
6014         if( cw ){
6015             start_seg = npt - 1;
6016             end_seg = 0;
6017             inc = -1;
6018         }
6019 
6020         float dx, dy, seg_len, theta;
6021         ClipResult res;
6022 
6023         bool done = false;
6024         int iseg = start_seg;
6025         while( !done ){
6026            // Do not bother with segments that are invisible
6027 
6028             x0 = ptp[iseg].x;
6029             y0 = ptp[iseg].y;
6030             x1 = ptp[iseg + inc].x;
6031             y1 = ptp[iseg + inc].y;
6032 
6033             //  Also, segments marked (by mask) as invisible
6034             if( mask && !mask[iseg])
6035                 goto next_seg;
6036 
6037             res = cohen_sutherland_line_clip_i( &x0, &y0, &x1, &y1, xmin_, xmax_, ymin_, ymax_ );
6038 
6039             if( res == Invisible )
6040                 goto next_seg;
6041 
6042             dx = ptp[iseg + inc].x - ptp[iseg].x;
6043             dy = ptp[iseg + inc].y - ptp[iseg].y;
6044             seg_len = sqrt( dx * dx + dy * dy );
6045 
6046             if( seg_len >= 1.0 ) {
6047                 if( seg_len <= sym_len * sym_factor ) {
6048                     int xst1 = ptp[iseg].x;
6049                     int yst1 = ptp[iseg].y;
6050                     float xst2, yst2;
6051 
6052                     if( seg_len >= sym_len ) {
6053                         xst2 = xst1 + ( sym_len * dx / seg_len );
6054                         yst2 = yst1 + ( sym_len * dy / seg_len );
6055                     } else {
6056                         xst2 = ptp[iseg + inc].x;
6057                         yst2 = ptp[iseg + inc].y;
6058                     }
6059 
6060                     //      Enable anti-aliased lines, at best quality
6061 #ifndef __OCPN__ANDROID__
6062                     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6063                     glEnable (GL_BLEND);
6064 
6065                     if( m_GLLineSmoothing )
6066                     {
6067                         glEnable (GL_LINE_SMOOTH);
6068                         glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
6069                     }
6070 #endif
6071 
6072 #ifdef USE_ANDROID_GLES2
6073 
6074                     glUseProgram(S52color_tri_shader_program);
6075 
6076                     glBindBuffer( GL_ARRAY_BUFFER, 0 );
6077                     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
6078 
6079                     //GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"MVMatrix");
6080                     //glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)vp->vp_transform);
6081 
6082                     float colorv[4];
6083                     colorv[0] = color.Red() / float(256);
6084                     colorv[1] = color.Green() / float(256);
6085                     colorv[2] = color.Blue() / float(256);
6086                     colorv[3] = 1.0; //transparency;
6087 
6088                     GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
6089                     glUniform4fv(colloc, 1, colorv);
6090 
6091                     float pts[4];
6092                     pts[0] = xst1; pts[1] = yst1; pts[2] = xst2; pts[3] = yst2;
6093 
6094                     GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
6095                     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pts);
6096                     glEnableVertexAttribArray(pos);
6097 
6098                     glDrawArrays(GL_LINES, 0, 2);
6099 
6100 #else
6101                     {
6102                         glBegin( GL_LINES );
6103                         glVertex2i( xst1, yst1 );
6104                         glVertex2i( (wxCoord) floor( xst2 ), (wxCoord) floor( yst2 ) );
6105                         glEnd();
6106                     }
6107 #endif
6108 
6109                     glDisable( GL_LINE_SMOOTH );
6110                     glDisable( GL_BLEND );
6111                 } else {
6112                     float s = 0;
6113                     float xs = ptp[iseg].x;
6114                     float ys = ptp[iseg].y;
6115 
6116                     while( s + ( sym_len * sym_factor ) < seg_len ) {
6117                         r.x = (int) xs;
6118                         r.y = (int) ys;
6119                         char *str = draw_rule->vector.LVCT;
6120                         char *col = draw_rule->colRef.LCRF;
6121                         wxPoint pivot( draw_rule->pos.line.pivot_x.LICL,
6122                                 draw_rule->pos.line.pivot_y.LIRW );
6123 
6124                         HPGL->SetTargetOpenGl();
6125                         HPGL->SetVP(vp);
6126                         theta = atan2f( dy, dx );
6127                         HPGL->Render( str, col, r, pivot, pivot, 1.0, theta * 180. / PI, false );
6128 
6129                         xs += sym_len * dx / seg_len * sym_factor;
6130                         ys += sym_len * dy / seg_len * sym_factor;
6131                         s += sym_len * sym_factor;
6132                     }
6133 #ifndef __OCPN__ANDROID__
6134                     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6135                     glEnable (GL_BLEND);
6136 
6137                     if( m_GLLineSmoothing ) {
6138                         glEnable (GL_LINE_SMOOTH);
6139                         glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
6140                     }
6141 
6142 #endif
6143 #ifdef USE_ANDROID_GLES2
6144                     glUseProgram(S52color_tri_shader_program);
6145 
6146                     glBindBuffer( GL_ARRAY_BUFFER, 0 );
6147                     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
6148 
6149                     //GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"MVMatrix");
6150                     //glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)vp->vp_transform);
6151 
6152                     float colorv[4];
6153                     colorv[0] = color.Red() / float(256);
6154                     colorv[1] = color.Green() / float(256);
6155                     colorv[2] = color.Blue() / float(256);
6156                     colorv[3] = 1.0; //transparency;
6157 
6158                     GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
6159                     glUniform4fv(colloc, 1, colorv);
6160 
6161                     float pts[4];
6162                     pts[0] = xs; pts[1] = ys; pts[2] = ptp[iseg + inc].x; pts[3] =  ptp[iseg + inc].y;
6163 
6164                     GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
6165                     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pts);
6166                     glEnableVertexAttribArray(pos);
6167 
6168                     glDrawArrays(GL_LINES, 0, 2);
6169 
6170 #else
6171 
6172                     {
6173                         glBegin( GL_LINES );
6174                         glVertex2i( xs, ys );
6175                         glVertex2i( ptp[iseg + inc].x, ptp[iseg + inc].y );
6176                         glEnd();
6177                     }
6178 #endif
6179                     glDisable( GL_LINE_SMOOTH );
6180                     glDisable( GL_BLEND );
6181                 }
6182             }
6183 next_seg:
6184             iseg += inc;
6185             if(iseg == end_seg)
6186                 done = true;
6187         } // while
6188 
6189     } //opengl
6190 #endif
6191 }
6192 
6193 // Multipoint Sounding
6194 int s52plib::RenderMPS( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
6195 {
6196     if( !m_bShowSoundg )
6197         return 0;
6198 
6199     if( m_bUseSCAMIN ) {
6200         if( vp->chart_scale > rzRules->obj->Scamin )
6201             return 0;
6202     }
6203 
6204 
6205     int npt = rzRules->obj->npt;
6206 
6207     // this should never happen
6208     // But it seems that some PlugIns clear the mps rules without resetting the CS state machine
6209     // So fix it
6210     if( rzRules->obj->bCS_Added  && !rzRules->mps)
6211         rzRules->obj->bCS_Added = false;
6212 
6213     //  Build the cached rules list if necessary
6214     if( !rzRules->obj->bCS_Added ) {
6215 
6216         ObjRazRules point_rzRules;
6217         point_rzRules = *rzRules; // take a copy of attributes, etc
6218 
6219         S57Obj point_obj;
6220         point_obj = *( rzRules->obj );
6221         point_obj.bIsClone = true;
6222         point_rzRules.obj = &point_obj;
6223 
6224         Rules *ru_cs = StringToRules( _T ( "CS(SOUNDG03;" ) );
6225 
6226         wxPoint p;
6227         double *pd = rzRules->obj->geoPtz; // the SM points
6228         double *pdl = rzRules->obj->geoPtMulti; // and corresponding lat/lon
6229 
6230 
6231         mps_container *pmps = (mps_container *)calloc( sizeof(mps_container), 1);
6232         pmps->cs_rules = new ArrayOfRules;
6233         rzRules->mps = pmps;
6234 
6235         for( int ip = 0; ip < npt; ip++ ) {
6236             double east = *pd++;
6237             double nort = *pd++;
6238             double depth = *pd++;
6239 
6240             point_obj.x = east;
6241             point_obj.y = nort;
6242             point_obj.z = depth;
6243 
6244             double lon = *pdl++;
6245             double lat = *pdl++;
6246             point_obj.BBObj.Set( lat, lon, lat, lon );
6247             point_obj.BBObj.Invalidate();
6248 
6249             char *rule_str1 = RenderCS( &point_rzRules, ru_cs );
6250             wxString cs_string( rule_str1, wxConvUTF8 );
6251             free( rule_str1 );
6252 
6253             Rules *rule_chain = StringToRules( cs_string );
6254 
6255             rzRules->mps->cs_rules->Add( rule_chain );
6256 
6257         }
6258 
6259         DestroyRulesChain( ru_cs );
6260         rzRules->obj->bCS_Added = 1; // mark the object
6261     }
6262 
6263 
6264 
6265     double *pdl = rzRules->obj->geoPtMulti; // and corresponding lat/lon
6266     double *pd = rzRules->obj->geoPtz; // the SM points
6267 
6268     //  We need a private unrotated copy of the Viewport
6269     ViewPort vp_local = *vp;
6270     vp_local.SetRotationAngle( 0. );
6271 
6272     //  We may be rendering the soundings symbols scaled up, so
6273     //  adjust the inclusion test bounding box
6274 
6275     double scale_factor = vp->ref_scale/vp->chart_scale;
6276     double box_mult = wxMax((scale_factor - g_overzoom_emphasis_base), 1);
6277     int box_dim = 32 * box_mult;
6278 
6279     // We need a pixel bounding rectangle of the passed ViewPort.
6280     // Very important for partial screen renders, as with dc mode pans or OpenGL FBO operation.
6281 
6282     wxPoint cr0 = vp_local.GetPixFromLL( vp_local.GetBBox().GetMaxLat(), vp_local.GetBBox().GetMinLon());
6283     wxPoint cr1 = vp_local.GetPixFromLL( vp_local.GetBBox().GetMinLat(), vp_local.GetBBox().GetMaxLon());
6284     wxRect clip_rect(cr0, cr1);
6285 
6286     for( int ip = 0; ip < npt; ip++ ) {
6287 
6288         double lon = *pdl++;
6289         double lat = *pdl++;
6290 
6291         double east = *pd++;
6292             double nort = *pd++;
6293             double depth = *pd++;
6294 
6295 
6296         wxPoint r = vp_local.GetPixFromLL( lat, lon );
6297         //      Use estimated symbol size
6298         wxRect rr(r.x-(box_dim/2), r.y-(box_dim/2), box_dim, box_dim);
6299 
6300         //      After all the setup, the render inclusion test is trivial....
6301         if(!clip_rect.Intersects(rr))
6302             continue;
6303 
6304         double angle = 0;
6305             if(depth < 0)
6306                 int yyp = 4;
6307 
6308         Rules *rules =  rzRules->mps->cs_rules->Item(ip);
6309         bool bColorSet = false;
6310         wxColor symColor;
6311         GetGlobalColor(_T("SNDG2"), &symColor);
6312 
6313         while( rules ){
6314 
6315             //  Render a raster or vector symbol, as specified by LUP rules
6316             if( rules->razRule->definition.SYDF == 'V' ){
6317                 // On OpenGL, arrange to render the drying height "underline" symbol as un-rotated.
6318                 double dryAngle = 0;
6319                 if( !m_pdc && !strncmp(rules->razRule->name.SYNM, "SOUNDSA1", 8))
6320                     dryAngle = -vp->rotation * 180./PI;
6321                 RenderHPGL( rzRules, rules->razRule, r, vp, dryAngle );
6322             }
6323             else if( rules->razRule->definition.SYDF == 'R' ){
6324 
6325                 // Parse the first rule to determine the color
6326                 if(!bColorSet){
6327                     char symColorT = rules->razRule->name.SYNM[5];
6328                     if(symColorT== 'G')
6329                         GetGlobalColor(_T("SNDG1"), &symColor);
6330                     bColorSet = true;
6331                 }
6332 
6333                 if(!strncmp(rules->razRule->name.SYNM, "SOUNDGC2", 8))
6334                     RenderRasterSymbol( rzRules, rules->razRule, r, vp, angle );
6335                 else
6336                     RenderSoundingSymbol( rzRules, rules->razRule, r, vp, symColor, angle );
6337             }
6338 
6339             rules = rules->next;
6340         }
6341     }
6342 
6343     return 1;
6344 }
6345 
6346 int s52plib::RenderCARC( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
6347 {
6348 #ifdef USE_ANDROID_GLES2
6349     return RenderCARC_GLSL(rzRules, rules, vp);
6350 #endif
6351 
6352     return RenderCARC_VBO(rzRules, rules, vp);
6353 }
6354 
6355 int s52plib::RenderCARC_GLSL( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
6356 {
6357 #ifdef USE_ANDROID_GLES2
6358 
6359 //    glDisable( GL_SCISSOR_TEST );
6360 
6361     char *str = (char*) rules->INSTstr;
6362     //    extract the parameters from the string
6363     //    And creating a unique string hash as we go
6364     wxString inst( str, wxConvUTF8 );
6365     wxString carc_hash;
6366 
6367     wxStringTokenizer tkz( inst, _T ( ",;" ) );
6368 
6369     //    outline color
6370     wxString outline_color = tkz.GetNextToken();
6371     carc_hash += outline_color;
6372     carc_hash += _T(".");
6373 
6374     //    outline width
6375     wxString slong = tkz.GetNextToken();
6376     long outline_width;
6377     slong.ToLong( &outline_width );
6378     carc_hash += slong;
6379     carc_hash += _T(".");
6380 
6381     //    arc color
6382     wxString arc_color = tkz.GetNextToken();
6383     carc_hash += arc_color;
6384     carc_hash += _T(".");
6385 
6386     //    arc width
6387     slong = tkz.GetNextToken();
6388     long arc_width;
6389     slong.ToLong( &arc_width );
6390     carc_hash += slong;
6391     carc_hash += _T(".");
6392 
6393     //    sectr1
6394     slong = tkz.GetNextToken();
6395     double sectr1;
6396     slong.ToDouble( &sectr1 );
6397     carc_hash += slong;
6398     carc_hash += _T(".");
6399 
6400     //    sectr2
6401     slong = tkz.GetNextToken();
6402     double sectr2;
6403     slong.ToDouble( &sectr2 );
6404     carc_hash += slong;
6405     carc_hash += _T(".");
6406 
6407     //    arc radius
6408     slong = tkz.GetNextToken();
6409     long radius;
6410     slong.ToLong( &radius );
6411     carc_hash += slong;
6412     carc_hash += _T(".");
6413 
6414     //    sector radius
6415     slong = tkz.GetNextToken();
6416     long sector_radius;
6417     slong.ToLong( &sector_radius );
6418     carc_hash += slong;
6419     carc_hash += _T(".");
6420 
6421     slong.Printf( _T("%d"), m_colortable_index );
6422     carc_hash += slong;
6423 
6424     // Center point
6425     wxPoint r;
6426     GetPointPixSingle( rzRules, rzRules->obj->y, rzRules->obj->x, &r, vp );
6427 
6428     //  radius scaled to display
6429     float rad =  radius * canvas_pix_per_mm ;
6430     float arcw = arc_width * canvas_pix_per_mm;
6431     float sec_rad = sector_radius * canvas_pix_per_mm;
6432 
6433     // Adjust size
6434     //  Some plain lights have no SCAMIN attribute.
6435     //  This causes display congestion at small viewing scales, since the objects are rendered at fixed pixel dimensions from the LUP rules.
6436     //  As a correction, the idea is to not allow the rendered symbol to be larger than "X" meters on the chart.
6437     //   and scale it down when rendered if necessary.
6438 
6439     float xscale = 1.0;
6440     if(1/*rzRules->obj->Scamin > 10000000*/){                        // huge (unset) SCAMIN)
6441         float radius_meters_target = 200;
6442 
6443         float radius_meters = ( radius * canvas_pix_per_mm ) / vp->view_scale_ppm;
6444 
6445         xscale = radius_meters_target / radius_meters;
6446         xscale = wxMin(xscale, 1.0);
6447         xscale = wxMax(.4, xscale);
6448 
6449         rad *= xscale;
6450         arcw *= xscale;
6451         arcw =wxMin(arcw, rad/10);
6452         sec_rad *= xscale;
6453     }
6454     //      Enable anti-aliased lines, at best quality
6455     glEnable( GL_BLEND );
6456 
6457     // Rotate the center point about vp center
6458     wxPoint point = r;
6459     double sin_rot = sin( vp->rotation );
6460     double cos_rot = cos( vp->rotation );
6461 
6462     double xp = ( (point.x - vp->pix_width/2) * cos_rot ) - ( (point.y - vp->pix_height/2) * sin_rot );
6463     double yp = ( (point.x - vp->pix_width/2) * sin_rot ) + ( (point.y - vp->pix_height/2) * cos_rot );
6464 
6465     point.x = (int) xp + vp->pix_width/2;
6466     point.y = (int) yp + vp->pix_height/2;
6467 
6468     float coords[8];
6469     coords[0] = -rad;  coords[1] =  rad;
6470     coords[2] =  rad;  coords[3] =  rad;
6471     coords[4] = -rad;  coords[5] = -rad;
6472     coords[6] =  rad;  coords[7] = -rad;
6473 
6474     glUseProgram( S52ring_shader_program );
6475 
6476     // Get pointers to the attributes in the program.
6477     GLint mPosAttrib = glGetAttribLocation( S52ring_shader_program, "aPos" );
6478 
6479     // Disable VBO's (vertex buffer objects) for attributes.
6480     glBindBuffer( GL_ARRAY_BUFFER, 0 );
6481     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
6482 
6483     glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
6484     glEnableVertexAttribArray( mPosAttrib );
6485 
6486     //  Circle radius
6487     GLint radiusloc = glGetUniformLocation(S52ring_shader_program,"circle_radius");
6488     glUniform1f(radiusloc, rad);
6489 
6490     //  Circle center point, physical
6491     GLint centerloc = glGetUniformLocation(S52ring_shader_program,"circle_center");
6492     float ctrv[2];
6493     ctrv[0] = point.x; ctrv[1] = GetOCPNCanvasWindow()->GetSize().y - point.y;
6494     glUniform2fv(centerloc, 1, ctrv);
6495 
6496     //  Circle color
6497     wxColour colorb = getwxColour( arc_color );
6498     float colorv[4];
6499     colorv[0] = colorb.Red() / float(256);
6500     colorv[1] = colorb.Green() / float(256);
6501     colorv[2] = colorb.Blue() / float(256);
6502     colorv[3] = 1.0;
6503 
6504     GLint colloc = glGetUniformLocation(S52ring_shader_program,"circle_color");
6505     glUniform4fv(colloc, 1, colorv);
6506 
6507     //  Border color
6508     float bcolorv[4];
6509     bcolorv[0] = 0;
6510     bcolorv[1] = 0;
6511     bcolorv[2] = 0;
6512     bcolorv[3] = 1.0;
6513 
6514     GLint bcolloc = glGetUniformLocation(S52ring_shader_program,"border_color");
6515     glUniform4fv(bcolloc, 1, bcolorv);
6516 
6517     //  Border Width
6518     GLint borderWidthloc = glGetUniformLocation(S52ring_shader_program,"border_width");
6519     glUniform1f(borderWidthloc, 2);
6520 
6521     //  Ring width
6522     GLint ringWidthloc = glGetUniformLocation(S52ring_shader_program,"ring_width");
6523     glUniform1f(ringWidthloc, arcw);
6524 
6525     //  Visible sectors, rotated to vp orientation
6526     float sr1 = sectr1 + (vp->rotation * 180 / PI);
6527     if(sr1 > 360.) sr1 -= 360.;
6528     float sr2 = sectr2 + (vp->rotation * 180 / PI);
6529     if(sr2 > 360.) sr2 -= 360.;
6530 
6531     float sb, se;
6532     if ( sr2 > sr1 ){
6533         sb = sr1;
6534         se = sr2;
6535     }
6536     else{
6537         sb = sr1;
6538         se = sr2 + 360;
6539     }
6540 
6541     //  Shader can handle angles > 360.
6542     if( (sb < 0) || (se < 0)){
6543         sb += 360.;
6544         se += 360.;
6545     }
6546 
6547     GLint sector1loc = glGetUniformLocation(S52ring_shader_program,"sector_1");
6548     glUniform1f(sector1loc, (sb * PI / 180.));
6549     GLint sector2loc = glGetUniformLocation(S52ring_shader_program,"sector_2");
6550     glUniform1f(sector2loc, (se * PI / 180.));
6551 
6552 
6553     // Rotate and translate
6554     mat4x4 I, Q;
6555     mat4x4_identity(I);
6556 
6557     mat4x4_translate_in_place(I, r.x, r.y, 0);
6558 
6559     GLint matloc = glGetUniformLocation(S52ring_shader_program,"TransformMatrix");
6560     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)I);
6561 
6562     // Perform the actual drawing.
6563     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
6564 
6565     // Restore the per-object transform to Identity Matrix
6566     mat4x4 IM;
6567     mat4x4_identity(IM);
6568     GLint matlocf = glGetUniformLocation(S52ring_shader_program,"TransformMatrix");
6569     glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
6570 
6571     //    Draw the sector legs directly on the target DC
6572     if( sector_radius > 0 ) {
6573         int leg_len = (int) ( sec_rad );
6574 
6575         wxDash dash1[2];
6576         dash1[0] = (int) ( 3.6 * canvas_pix_per_mm / 3 ); //8// Long dash  <---------+
6577         dash1[1] = (int) ( 1.8 * canvas_pix_per_mm / 3); //2// Short gap            |
6578 
6579         wxPen thispen = *wxBLACK_PEN;
6580         thispen.SetDashes( 2, dash1 );
6581         thispen.SetWidth(3);
6582         thispen.SetStyle(wxPENSTYLE_USER_DASH);
6583 
6584         float a = ( sectr1 - 90 ) * PI / 180;
6585         a += vp->rotation;
6586         int x = point.x + (int) ( leg_len * cosf( a ) );
6587         int y = point.y + (int) ( leg_len * sinf( a ) );
6588         DrawDashLine(thispen, point.x, point.y, x, y, vp);
6589 
6590         a = ( sectr2 - 90 ) * PI / 180.;
6591         a += vp->rotation;
6592         x = point.x + (int) ( leg_len * cosf( a ) );
6593         y = point.y + (int) ( leg_len * sinf( a ) );
6594         DrawDashLine(thispen, point.x, point.y, x, y, vp);
6595     }
6596     glDisable( GL_BLEND );
6597 
6598     //  Update the object Bounding box,
6599     //  so that subsequent drawing operations will redraw the item fully
6600 
6601     double latmin, lonmin, latmax, lonmax;
6602 
6603     GetPixPointSingleNoRotate( r.x - rad, r.y + rad, &latmin, &lonmin, vp );
6604     GetPixPointSingleNoRotate( r.x + rad, r.y - rad, &latmax, &lonmax, vp );
6605     LLBBox symbox;
6606     symbox.Set( latmin, lonmin, latmax, lonmax );
6607     rzRules->obj->BBObj.Expand( symbox );
6608 
6609 //    glEnable( GL_SCISSOR_TEST );
6610 
6611 #endif
6612 
6613     return 1;
6614 }
6615 
6616 
6617 int s52plib::RenderCARC_VBO( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
6618 {
6619 #ifndef USE_ANDROID_GLES2
6620     char *str = (char*) rules->INSTstr;
6621     //    extract the parameters from the string
6622     //    And creating a unique string hash as we go
6623     wxString inst( str, wxConvUTF8 );
6624     wxString carc_hash;
6625 
6626     wxStringTokenizer tkz( inst, _T ( ",;" ) );
6627 
6628     //    outline color
6629     wxString outline_color = tkz.GetNextToken();
6630     carc_hash += outline_color;
6631     carc_hash += _T(".");
6632 
6633     //    outline width
6634     wxString slong = tkz.GetNextToken();
6635     long outline_width;
6636     slong.ToLong( &outline_width );
6637     carc_hash += slong;
6638     carc_hash += _T(".");
6639 
6640     //    arc color
6641     wxString arc_color = tkz.GetNextToken();
6642     carc_hash += arc_color;
6643     carc_hash += _T(".");
6644 
6645     //    arc width
6646     slong = tkz.GetNextToken();
6647     long arc_width;
6648     slong.ToLong( &arc_width );
6649     carc_hash += slong;
6650     carc_hash += _T(".");
6651 
6652     //    sectr1
6653     slong = tkz.GetNextToken();
6654     double sectr1;
6655     slong.ToDouble( &sectr1 );
6656     carc_hash += slong;
6657     carc_hash += _T(".");
6658 
6659     //    sectr2
6660     slong = tkz.GetNextToken();
6661     double sectr2;
6662     slong.ToDouble( &sectr2 );
6663     carc_hash += slong;
6664     carc_hash += _T(".");
6665 
6666     //    arc radius
6667     slong = tkz.GetNextToken();
6668     long radius;
6669     slong.ToLong( &radius );
6670     carc_hash += slong;
6671     carc_hash += _T(".");
6672 
6673     //    sector radius
6674     slong = tkz.GetNextToken();
6675     long sector_radius;
6676     slong.ToLong( &sector_radius );
6677     carc_hash += slong;
6678     carc_hash += _T(".");
6679 
6680     slong.Printf( _T("%d"), m_colortable_index );
6681     carc_hash += slong;
6682 
6683     int width;
6684     int height;
6685     int rad;
6686     int bm_width;
6687     int bm_height;
6688     int bm_orgx;
6689     int bm_orgy;
6690 
6691     Rule *prule = rules->razRule;
6692 
6693     float scale_factor = 1.0;
6694 
6695     // The dimensions of the light are presented here as pixels on-screen.
6696     // We must scale the rendered size based on the device pixel density
6697     // Let us declare that the width of the arc should be no less than X mm
6698     float wx = 1.0;
6699 
6700     //float pd_scale = 1.0;
6701     float nominal_arc_width_pix = wxMax(1.0, floor(GetPPMM() * wx));             // { wx } mm nominal, but not less than 1 pixel
6702     //pd_scale = nominal_arc_width_pix / arc_width;
6703 
6704     //scale_factor *= pd_scale;
6705     //qDebug() << GetPPMM() << arc_width << nominal_arc_width_pix << pd_scale;
6706 
6707 
6708     // Adjust size
6709     //  Some plain lights have no SCAMIN attribute.
6710     //  This causes display congestion at small viewing scales, since the objects are rendered at fixed pixel dimensions from the LUP rules.
6711     //  As a correction, the idea is to not allow the rendered symbol to be larger than "X" meters on the chart.
6712     //   and scale it down when rendered if necessary.
6713 
6714     float xscale = 1.0;
6715     if(rzRules->obj->Scamin > 10000000){                        // huge (unset) SCAMIN)
6716         float radius_meters_target = 200;
6717 
6718          float radius_meters = ( radius * canvas_pix_per_mm ) / vp->view_scale_ppm;
6719 
6720         xscale = radius_meters_target / radius_meters;
6721         xscale = wxMin(xscale, 1.0);
6722         xscale = wxMax(.4, xscale);
6723 
6724         radius *= xscale;
6725         sector_radius *= xscale;
6726     }
6727 
6728     ///scale_factor *= xscale;
6729 
6730     carc_hash += _T(".");
6731     wxString xs;
6732     xs.Printf( _T("%5g"), xscale );
6733     carc_hash += xs;
6734 
6735     if(m_pdc){          // DC rendering
6736         if(fabs(prule->parm7 - xscale) > .00001){
6737             ClearRulesCache( prule );
6738         }
6739 
6740     //Instantiate the symbol if necessary
6741     if( ( rules->razRule->pixelPtr == NULL ) || ( rules->razRule->parm1 != m_colortable_index ) ) {
6742         //  Render the sector light to a bitmap
6743 
6744         rad = (int) ( radius * canvas_pix_per_mm );
6745 
6746         width = ( rad * 2 ) + 28;
6747         height = ( rad * 2 ) + 28;
6748         wxBitmap bm( width, height, -1 );
6749         wxMemoryDC mdc;
6750         mdc.SelectObject( bm );
6751         mdc.SetBackground( wxBrush( m_unused_wxColor ) );
6752         mdc.Clear();
6753 
6754         //    Adjust sector math for wxWidgets API
6755         float sb;
6756         float se;
6757 
6758         //      For some reason, the __WXMSW__ build flips the sense of
6759         //      start and end angles on DrawEllipticArc()
6760 #ifndef __WXMSW__
6761         if ( sectr2 > sectr1 )
6762         {
6763             sb = 90 - sectr1;
6764             se = 90 - sectr2;
6765         }
6766         else
6767         {
6768             sb = 360 + ( 90 - sectr1 );
6769             se = 90 - sectr2;
6770         }
6771 #else
6772         if( sectr2 > sectr1 ) {
6773             se = 90 - sectr1;
6774             sb = 90 - sectr2;
6775         } else {
6776             se = 360 + ( 90 - sectr1 );
6777             sb = 90 - sectr2;
6778         }
6779 #endif
6780 
6781         //      Here is a goofy way of computing the dc drawing extents exactly
6782         //      Draw a series of fat line segments approximating the arc using dc.DrawLine()
6783         //      This will properly establish the drawing box in the dc
6784 
6785         int border_fluff = 4; // by how much should the blit bitmap be "fluffed"
6786 
6787         //  wxDC min/max calculations are currently broken in wxQT, so we use the entire circle instead of arcs...
6788 #ifndef __WXQT__
6789         if( fabs( sectr2 - sectr1 ) != 360 ) // not necessary for all-round lights
6790                 {
6791             mdc.ResetBoundingBox();
6792 
6793             wxPen *pblockpen = wxThePenList->FindOrCreatePen( *wxBLACK, 10, wxPENSTYLE_SOLID );
6794             mdc.SetPen( *pblockpen );
6795 
6796             float start_angle, end_angle;
6797             if( se < sb ) {
6798                 start_angle = se;
6799                 end_angle = sb;
6800             } else {
6801                 start_angle = sb;
6802                 end_angle = se;
6803             }
6804 
6805             int x0 = ( width / 2 ) + (int) ( rad * cos( start_angle * PI / 180. ) );
6806             int y0 = ( height / 2 ) - (int) ( rad * sin( start_angle * PI / 180. ) );
6807             for( float a = start_angle + .1; a <= end_angle; a += 2.0 ) {
6808                 int x = ( width / 2 ) + (int) ( rad * cosf( a * PI / 180. ) );
6809                 int y = ( height / 2 ) - (int) ( rad * sinf( a * PI / 180. ) );
6810                 mdc.DrawLine( x0, y0, x, y );
6811                 x0 = x;
6812                 y0 = y;
6813             }
6814 
6815             bm_width = ( mdc.MaxX() - mdc.MinX() ) + ( border_fluff * 2 );
6816             bm_height = ( mdc.MaxY() - mdc.MinY() ) + ( border_fluff * 2 );
6817             bm_orgx = mdc.MinX()-border_fluff - width/2; //wxMax ( 0, mdc.MinX()-border_fluff );
6818             bm_orgy = mdc.MinY()-border_fluff - height/2; //wxMax ( 0, mdc.MinY()-border_fluff );
6819 
6820             mdc.Clear();
6821         }
6822 
6823         else {
6824             bm_width = rad * 2 + ( border_fluff * 2 );
6825             bm_height = rad * 2 + ( border_fluff * 2 );
6826             bm_orgx = -bm_width / 2;
6827             bm_orgy = -bm_height / 2;
6828 
6829         }
6830 
6831 #else
6832         bm_width = rad * 2 + ( border_fluff * 2 );
6833         bm_height = rad * 2 + ( border_fluff * 2 );
6834         bm_orgx = -bm_width / 2;
6835         bm_orgy = -bm_height / 2;
6836 #endif
6837 
6838         wxBitmap *sbm = NULL;
6839 
6840             //    Draw the outer border
6841             wxColour color = getwxColour( outline_color );
6842 
6843                 wxPen *pthispen = wxThePenList->FindOrCreatePen( color, outline_width * scale_factor, wxPENSTYLE_SOLID );
6844             mdc.SetPen( *pthispen );
6845             wxBrush *pthisbrush = wxTheBrushList->FindOrCreateBrush( color, wxBRUSHSTYLE_TRANSPARENT );
6846             mdc.SetBrush( *pthisbrush );
6847 
6848             mdc.DrawEllipticArc( width / 2 - rad, height / 2 - rad, rad * 2, rad * 2, sb, se );
6849 
6850             if( arc_width ) {
6851                 wxColour colorb = getwxColour( arc_color );
6852 
6853                 if( !colorb.IsOk() ) colorb = getwxColour( _T("CHMGD") );
6854 
6855                     pthispen = wxThePenList->FindOrCreatePen( colorb, arc_width * scale_factor, wxPENSTYLE_SOLID );
6856                 mdc.SetPen( *pthispen );
6857 
6858                 mdc.DrawEllipticArc( width / 2 - rad, height / 2 - rad, rad * 2, rad * 2, sb, se );
6859 
6860             }
6861 
6862             mdc.SelectObject( wxNullBitmap );
6863 
6864             //          Get smallest containing bitmap
6865             sbm = new wxBitmap(
6866                 bm.GetSubBitmap( wxRect( width/2 + bm_orgx, height/2 + bm_orgy, bm_width, bm_height ) ) );
6867 
6868             //      Make the mask
6869             wxMask *pmask = new wxMask( *sbm, m_unused_wxColor );
6870 
6871             //      Associate the mask with the bitmap
6872             sbm->SetMask( pmask );
6873 
6874             // delete any old private data
6875             ClearRulesCache( rules->razRule );
6876 
6877         //      Save the bitmap ptr and aux parms in the rule
6878         prule->pixelPtr = sbm;
6879         prule->parm0 = ID_wxBitmap;
6880         prule->parm1 = m_colortable_index;
6881         prule->parm2 = bm_orgx;
6882         prule->parm3 = bm_orgy;
6883         prule->parm5 = bm_width;
6884         prule->parm6 = bm_height;
6885             prule->parm7 = xscale;
6886     } // instantiation
6887     }
6888 
6889 #ifdef ocpnUSE_GL
6890     CARC_Buffer buffer;
6891 
6892 
6893     if( !m_pdc ) // opengl
6894     {
6895         //    Is there not already an generated vbo the CARC_hashmap for this object?
6896         rad = (int) ( radius * canvas_pix_per_mm );
6897         if( m_CARC_hashmap.find( carc_hash ) == m_CARC_hashmap.end() ) {
6898 
6899             if( sectr1 > sectr2 ) sectr2 += 360;
6900 
6901             /* to ensure that the final segment lands exactly on sectr2 */
6902 
6903             //    Draw wide outline arc
6904             wxColour colorb = getwxColour( outline_color );
6905             buffer.color[0][0] = colorb.Red();
6906             buffer.color[0][1] = colorb.Green();
6907             buffer.color[0][2] = colorb.Blue();
6908             buffer.color[0][3] = 150;
6909             buffer.line_width[0] = wxMax(g_GLMinSymbolLineWidth, outline_width * scale_factor);
6910 
6911             int steps = ceil((sectr2 - sectr1) / 12) + 1; // max of 12 degree step
6912             float step = (sectr2 - sectr1) / (steps - 1);
6913 
6914             buffer.steps = steps;
6915             buffer.size = 2*(steps + 4);
6916             buffer.data = new float[buffer.size];
6917 
6918             int s = 0;
6919             for(int i = 0; i < steps; i++) {
6920                 float a = (sectr1 + i * step) * M_PI / 180.0;
6921                 buffer.data[s++] = rad * sinf( a );
6922                 buffer.data[s++] = -rad * cosf( a );
6923             }
6924 
6925             //    Draw narrower color arc, overlaying the drawn outline.
6926             colorb = getwxColour( arc_color );
6927             buffer.color[1][0] = colorb.Red();
6928             buffer.color[1][1] = colorb.Green();
6929             buffer.color[1][2] = colorb.Blue();
6930             buffer.color[1][3] = 150;
6931             buffer.line_width[1] = wxMax(g_GLMinSymbolLineWidth, (arc_width  * scale_factor) + .8);
6932 
6933             //    Draw the sector legs
6934             if( sector_radius > 0 ) {
6935                 int leg_len = (int) ( sector_radius * canvas_pix_per_mm );
6936 
6937                 //wxColour c = GetGlobalColor( _T ( "CHBLK" ) );
6938                 wxColour c; GetGlobalColor( _T ( "CHBLK" ), &c);
6939 
6940                 buffer.color[2][0] = c.Red();
6941                 buffer.color[2][1] = c.Green();
6942                 buffer.color[2][2] = c.Blue();
6943                 buffer.color[2][3] = c.Alpha();
6944                 //buffer.line_width[2] = wxMax(g_GLMinSymbolLineWidth, (float)0.5) * scale_factor;
6945                 buffer.line_width[2] = wxMax(1.0, floor(GetPPMM() * 0.2));             //0.4 mm nominal, but not less than 1 pixel
6946 
6947                 float a = ( sectr1 - 90 ) * PI / 180.;
6948                 buffer.data[s++] = 0;
6949                 buffer.data[s++] = 0;
6950                 buffer.data[s++] = leg_len * cosf( a );
6951                 buffer.data[s++] = leg_len * sinf( a );
6952 
6953                 a = ( sectr2 - 90 ) * PI / 180.;
6954                 buffer.data[s++] = 0;
6955                 buffer.data[s++] = 0;
6956                 buffer.data[s++] = leg_len * cosf( a );
6957                 buffer.data[s++] = leg_len * sinf( a );
6958             } else
6959                 buffer.line_width[2] = 0;
6960 
6961             m_CARC_hashmap[carc_hash] = buffer;
6962 
6963         } else
6964             buffer = m_CARC_hashmap[carc_hash];
6965 
6966         int border_fluff = 4; // by how much should the blit bitmap be "fluffed"
6967         bm_width = rad * 2 + ( border_fluff * 2 );
6968         bm_height = rad * 2 + ( border_fluff * 2 );
6969         bm_orgx = -bm_width / 2;
6970         bm_orgy = -bm_height / 2;
6971 
6972         prule->parm2 = bm_orgx;
6973         prule->parm3 = bm_orgy;
6974         prule->parm5 = bm_width;
6975         prule->parm6 = bm_height;
6976         prule->parm7 = xscale;
6977 
6978     } // instantiation
6979 #endif
6980 
6981 
6982     //  Render arcs at object's x/y
6983     wxPoint r;
6984     GetPointPixSingle( rzRules, rzRules->obj->y, rzRules->obj->x, &r, vp );
6985 
6986     //      Now render the symbol
6987     if( !m_pdc ) // opengl
6988     {
6989 #ifdef ocpnUSE_GL
6990         glPushMatrix();
6991         glTranslatef( r.x, r.y, 0 );
6992 
6993 //        glScalef(scale_factor, scale_factor, 1);
6994         glVertexPointer(2, GL_FLOAT, 2 * sizeof(float), buffer.data);
6995 
6996 #ifndef __OCPN__ANDROID__
6997         glEnable( GL_BLEND );
6998         if( m_GLLineSmoothing )
6999             glEnable( GL_LINE_SMOOTH );
7000 #endif
7001         glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
7002 
7003         glColor3ubv(buffer.color[0]);
7004         glLineWidth(buffer.line_width[0]);
7005         glDrawArrays(GL_LINE_STRIP, 0, buffer.steps);
7006 
7007 
7008         glColor3ubv(buffer.color[1]);
7009         glLineWidth(buffer.line_width[1]);
7010         glDrawArrays(GL_LINE_STRIP, 0, buffer.steps);
7011 
7012         //qDebug() << buffer.line_width[0] << buffer.line_width[1] << buffer.line_width[2];
7013 
7014         if(buffer.line_width[2]) {
7015 #ifndef ocpnUSE_GLES // linestipple is emulated poorly
7016             glLineStipple( 1, 0x3F3F );
7017             glEnable( GL_LINE_STIPPLE );
7018 #endif
7019             glColor3ubv(buffer.color[2]);
7020             glLineWidth(buffer.line_width[2]);
7021             glDrawArrays(GL_LINES, buffer.steps, 4);
7022 #ifndef ocpnUSE_GLES
7023             glDisable( GL_LINE_STIPPLE );
7024 #endif
7025         }
7026 
7027         glDisableClientState(GL_VERTEX_ARRAY);
7028         glDisable( GL_LINE_SMOOTH );
7029         glDisable( GL_BLEND );
7030 
7031         // Debug the symbol bounding box.....
7032 /*
7033         {
7034             int x0 = rules->razRule->parm2;
7035             int y0 = rules->razRule->parm3;
7036 
7037             glLineWidth( 2 );
7038             glColor4f( 0,0,0,0 );
7039 
7040             glBegin( GL_LINE_STRIP );
7041             glVertex2i( x0, y0 );
7042             glVertex2i( x0 + b_width, y0 );
7043             glVertex2i( x0 + b_width, y0 + b_height );
7044             glVertex2i( x0, y0 + b_height);
7045             glVertex2i( x0, y0 );
7046             glEnd();
7047         }
7048         */
7049 
7050 //        glTranslatef( -r.x, -r.y, 0 );
7051         glPopMatrix();
7052 #endif
7053     } else {
7054         int b_width = prule->parm5;
7055         int b_height = prule->parm6;
7056 
7057         //      Get the bitmap into a memory dc
7058         wxMemoryDC mdc;
7059         mdc.SelectObject( (wxBitmap &) ( *( (wxBitmap *) ( rules->razRule->pixelPtr ) ) ) );
7060 
7061         //      Blit it into the target dc, using mask
7062         m_pdc->Blit( r.x + rules->razRule->parm2, r.y + rules->razRule->parm3, b_width, b_height,
7063                 &mdc, 0, 0, wxCOPY, true );
7064 
7065         mdc.SelectObject( wxNullBitmap );
7066 
7067         //    Draw the sector legs directly on the target DC
7068         //    so that anti-aliasing works against the drawn image (cannot be cached...)
7069         if( sector_radius > 0 ) {
7070             int leg_len = (int) ( sector_radius * canvas_pix_per_mm );
7071 
7072             wxDash dash1[2];
7073             dash1[0] = (int) ( 3.6 * canvas_pix_per_mm ); //8// Long dash  <---------+
7074             dash1[1] = (int) ( 1.8 * canvas_pix_per_mm ); //2// Short gap            |
7075 
7076             /*
7077              wxPen *pthispen = new wxPen(*wxBLACK_PEN);
7078              pthispen->SetStyle(wxPENSTYLE_USER_DASH);
7079              pthispen->SetDashes( 2, dash1 );
7080              //      Undocumented "feature":  Pen must be fully specified <<<BEFORE>>> setting into DC
7081              pdc->SetPen ( *pthispen );
7082              */
7083             //wxColour c = GetGlobalColor( _T ( "CHBLK" ) );
7084             wxColour c; GetGlobalColor( _T ( "CHBLK" ), &c);
7085 
7086             float a = ( sectr1 - 90 ) * PI / 180;
7087             int x = r.x + (int) ( leg_len * cosf( a ) );
7088             int y = r.y + (int) ( leg_len * sinf( a ) );
7089             DrawAALine( m_pdc, r.x, r.y, x, y, c, dash1[0], dash1[1] );
7090 
7091             a = ( sectr2 - 90 ) * PI / 180.;
7092             x = r.x + (int) ( leg_len * cosf( a ) );
7093             y = r.y + (int) ( leg_len * sinf( a ) );
7094             DrawAALine( m_pdc, r.x, r.y, x, y, c, dash1[0], dash1[1] );
7095         }
7096 
7097         // Debug the symbol bounding box.....
7098 /*
7099         if(m_pdc){
7100             m_pdc->SetPen(wxPen(*wxGREEN, 1));
7101             m_pdc->SetBrush(wxBrush(*wxGREEN, wxTRANSPARENT));
7102             m_pdc->DrawRectangle( r.x + rules->razRule->parm2, r.y + rules->razRule->parm3, b_width, b_height);
7103         }
7104         */
7105     }
7106 
7107     //  Update the object Bounding box,
7108     //  so that subsequent drawing operations will redraw the item fully
7109 
7110     double latmin, lonmin, latmax, lonmax;
7111 
7112     GetPixPointSingleNoRotate( r.x + prule->parm2,                r.y + prule->parm3 + prule->parm6, &latmin, &lonmin, vp );
7113     GetPixPointSingleNoRotate( r.x + prule->parm2 + prule->parm5, r.y + prule->parm3,                &latmax, &lonmax, vp );
7114     LLBBox symbox;
7115     symbox.Set( latmin, lonmin, latmax, lonmax );
7116     rzRules->obj->BBObj.Expand( symbox );
7117 #endif
7118     return 1;
7119 }
7120 
7121 // Conditional Symbology
7122 char *s52plib::RenderCS( ObjRazRules *rzRules, Rules *rules )
7123 {
7124     void *ret;
7125     void* (*f)( void* );
7126 
7127     static int f05;
7128 
7129     if( rules->razRule == NULL ) {
7130         if( !f05 )
7131         //                  CPLError ( ( CPLErr ) 0, 0,"S52plib:_renderCS(): ERROR no conditional symbology for: %s\n", rules->INSTstr );
7132         f05++;
7133         return 0;
7134     }
7135 
7136     void *g = (void *) rules->razRule;
7137 
7138 #ifdef FIX_FOR_MSVC  //__WXMSW__
7139 //#warning Fix this cast, somehow...
7140 //      dsr             sigh... can't get the cast right
7141     _asm
7142     {
7143         mov eax,[dword ptr g]
7144         mov [dword ptr f],eax
7145     }
7146     ret = f ( ( void * ) rzRules ); // call cond symb
7147 #else
7148 
7149     f = (void * (*)( void * ) ) g;ret
7150     = f( (void *) rzRules );
7151 
7152 #endif
7153 
7154     return (char *) ret;
7155 }
7156 
7157 int s52plib::RenderObjectToDC( wxDC *pdcin, ObjRazRules *rzRules, ViewPort *vp )
7158 {
7159     return DoRenderObject( pdcin, rzRules, vp );
7160 }
7161 
7162 
7163 int s52plib::RenderObjectToGL( const wxGLContext &glcc, ObjRazRules *rzRules, ViewPort *vp )
7164 {
7165     m_glcc = (wxGLContext *) &glcc;
7166     return DoRenderObject( NULL, rzRules, vp );
7167 }
7168 
7169 int s52plib::RenderObjectToDCText( wxDC *pdcin, ObjRazRules *rzRules, ViewPort *vp )
7170 {
7171     return DoRenderObjectTextOnly( pdcin, rzRules, vp );
7172 }
7173 
7174 int s52plib::RenderObjectToGLText( const wxGLContext &glcc, ObjRazRules *rzRules, ViewPort *vp )
7175 {
7176     m_glcc = (wxGLContext *) &glcc;
7177     return DoRenderObjectTextOnly( NULL, rzRules, vp );
7178 }
7179 
7180 
7181 
7182 int s52plib::DoRenderObject( wxDC *pdcin, ObjRazRules *rzRules, ViewPort *vp )
7183 {
7184     //TODO  Debugging
7185 //      if(rzRules->obj->Index != 6118)
7186 //        return 0; //int yyp = 0;
7187 
7188 //        if(!strncmp(rzRules->obj->FeatureName, "berths", 6))
7189 //            int yyp = 0;
7190 
7191     if( !ObjectRenderCheckRules( rzRules, vp, true ) )
7192         return 0;
7193 
7194     m_pdc = pdcin; // use this DC
7195     Rules *rules = rzRules->LUP->ruleList;
7196 
7197     while( rules != NULL ) {
7198         switch( rules->ruleType ){
7199             case RUL_TXT_TX:
7200                 RenderTX( rzRules, rules, vp );
7201                 break; // TX
7202             case RUL_TXT_TE:
7203                 RenderTE( rzRules, rules, vp );
7204                 break; // TE
7205             case RUL_SYM_PT:
7206                 RenderSY( rzRules, rules, vp );
7207                 break; // SY
7208             case RUL_SIM_LN:
7209                 if(m_pdc)
7210                     RenderLS( rzRules, rules, vp );
7211                 else
7212                     RenderGLLS( rzRules, rules, vp );
7213                 break; // LS
7214             case RUL_COM_LN:
7215                 RenderLC( rzRules, rules, vp );
7216                 break; // LC
7217             case RUL_MUL_SG:
7218                 RenderMPS( rzRules, rules, vp );
7219                 break; // MultiPoint Sounding
7220             case RUL_ARC_2C:
7221                 RenderCARC( rzRules, rules, vp );
7222                 break; // Circular Arc, 2 colors
7223 
7224             case RUL_CND_SY: {
7225                 if( !rzRules->obj->bCS_Added ) {
7226                     rzRules->obj->CSrules = NULL;
7227                     GetAndAddCSRules( rzRules, rules );
7228                     if(strncmp(rzRules->obj->FeatureName, "SOUNDG", 6))
7229                         rzRules->obj->bCS_Added = 1; // mark the object
7230                 }
7231 
7232                 Rules *rules_last = rules;
7233                 rules = rzRules->obj->CSrules;
7234 
7235                 while( NULL != rules ) {
7236                         switch( rules->ruleType ){
7237                             case RUL_TXT_TX:
7238                                 RenderTX( rzRules, rules, vp );
7239                                 break;
7240                             case RUL_TXT_TE:
7241                                 RenderTE( rzRules, rules, vp );
7242                                 break;
7243                             case RUL_SYM_PT:
7244                                 RenderSY( rzRules, rules, vp );
7245                                 break;
7246                             case RUL_SIM_LN:
7247                                 if(m_pdc)
7248                                     RenderLS( rzRules, rules, vp );
7249                                 else
7250                                     RenderGLLS( rzRules, rules, vp );
7251                                 break; // LS
7252                             case RUL_COM_LN:
7253                                 RenderLC( rzRules, rules, vp );
7254                                 break;
7255                             case RUL_MUL_SG:
7256                                 RenderMPS( rzRules, rules, vp );
7257                                 break; // MultiPoint Sounding
7258                             case RUL_ARC_2C:
7259                                 RenderCARC( rzRules, rules, vp );
7260                                 break; // Circular Arc, 2 colors
7261                             case RUL_NONE:
7262                             default:
7263                                 break; // no rule type (init)
7264                         }
7265                         rules_last = rules;
7266                         rules = rules->next;
7267                 }
7268 
7269                 rules = rules_last;
7270                 break;
7271             }
7272 
7273             case RUL_NONE:
7274             default:
7275                 break; // no rule type (init)
7276         } // switch
7277 
7278         rules = rules->next;
7279     }
7280 
7281     return 1;
7282 }
7283 
7284 int s52plib::DoRenderObjectTextOnly( wxDC *pdcin, ObjRazRules *rzRules, ViewPort *vp )
7285 {
7286 //    if(strncmp(rzRules->obj->FeatureName, "RDOCAL", 6))
7287 //        return 0;
7288 
7289 //    if(rzRules->obj->Index == 2766)
7290 //        int yyp = 4;
7291 
7292     if( !ObjectRenderCheckRules( rzRules, vp, true ) )
7293         return 0;
7294 
7295     m_pdc = pdcin; // use this DC
7296     Rules *rules = rzRules->LUP->ruleList;
7297 
7298     while( rules != NULL ) {
7299         switch( rules->ruleType ){
7300             case RUL_TXT_TX:
7301                 RenderTX( rzRules, rules, vp );
7302                 break; // TX
7303             case RUL_TXT_TE:
7304                 RenderTE( rzRules, rules, vp );
7305                 break; // TE
7306             case RUL_CND_SY: {
7307                 if( !rzRules->obj->bCS_Added ) {
7308                     rzRules->obj->CSrules = NULL;
7309                     GetAndAddCSRules( rzRules, rules );
7310                     if(strncmp(rzRules->obj->FeatureName, "SOUNDG", 6))
7311                         rzRules->obj->bCS_Added = 1; // mark the object
7312                 }
7313 
7314                 Rules *rules_last = rules;
7315                 rules = rzRules->obj->CSrules;
7316 
7317                 while( NULL != rules ) {
7318                     switch( rules->ruleType ){
7319                         case RUL_TXT_TX:
7320                             RenderTX( rzRules, rules, vp );
7321                             break;
7322                         case RUL_TXT_TE:
7323                             RenderTE( rzRules, rules, vp );
7324                             break;
7325                         default:
7326                             break; // no rule type (init)
7327                     }
7328                     rules_last = rules;
7329                     rules = rules->next;
7330                 }
7331 
7332                 rules = rules_last;
7333                 break;
7334             }
7335 
7336             case RUL_NONE:
7337             default:
7338                  break; // no rule type (init)
7339         } // switch
7340 
7341         rules = rules->next;
7342     }
7343 
7344     return 1;
7345 }
7346 
7347 bool s52plib::PreloadOBJLFromCSV(const wxString &csv_file)
7348 {
7349     wxTextFile file( csv_file );
7350     if( !file.Exists() ) return false;
7351 
7352     file.Open();
7353 
7354     wxString str;
7355     str = file.GetFirstLine();
7356     wxChar quote[] = { '\"', 0 };
7357     wxString description;
7358     wxString token;
7359 
7360     while( !file.Eof() ) {
7361         str = file.GetNextLine();
7362 
7363         wxStringTokenizer tkz( str, _T(",") );
7364         token = tkz.GetNextToken(); // code
7365 
7366         description = tkz.GetNextToken(); // May contain comma
7367         if( !description.EndsWith( quote ) ) description << tkz.GetNextToken();
7368         description.Replace( _T("\""), _T(""), true );
7369 
7370         token = tkz.GetNextToken(); // Acronym
7371 
7372         if( token.Len() ) {
7373             //    Filter out any duplicates, in a case insensitive way
7374             //    i.e. only the first of "DEPARE" and "depare" is added
7375             bool bdup = false;
7376             for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ ) {
7377                 OBJLElement *pOLEt = (OBJLElement *) ( pOBJLArray->Item( iPtr ) );
7378                 if( !token.CmpNoCase( wxString( pOLEt->OBJLName, wxConvUTF8 ) ) ) {
7379                     bdup = true;
7380                     break;
7381                 }
7382             }
7383 
7384             if( !bdup ) {
7385                 wxCharBuffer buffer=token.ToUTF8();
7386                 if(buffer.data()) {
7387                     OBJLElement *pOLE = (OBJLElement *) calloc( sizeof(OBJLElement), 1 );
7388                     memcpy( pOLE->OBJLName, buffer.data(), 6 );
7389                     pOLE->nViz = 0;
7390 
7391                     pOBJLArray->Add( (void *) pOLE );
7392 
7393                     OBJLDescriptions.push_back( description );
7394                 }
7395             }
7396         }
7397     }
7398     return true;
7399 }
7400 
7401 void s52plib::UpdateOBJLArray( S57Obj *obj )
7402 {
7403     //    Search the array for this object class
7404 
7405     bool bNeedNew = true;
7406     OBJLElement *pOLE;
7407 
7408     for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ ) {
7409         pOLE = (OBJLElement *) ( pOBJLArray->Item( iPtr ) );
7410         if( !strncmp( pOLE->OBJLName, obj->FeatureName, 6 ) ) {
7411             obj->iOBJL = iPtr;
7412             bNeedNew = false;
7413             break;
7414         }
7415     }
7416 
7417     //    Not found yet, so add an element
7418     if( bNeedNew ) {
7419         pOLE = (OBJLElement *) calloc( sizeof(OBJLElement), 1 );
7420         memcpy( pOLE->OBJLName, obj->FeatureName, OBJL_NAME_LEN );
7421         pOLE->nViz = 1;
7422 
7423         pOBJLArray->Add( (void *) pOLE );
7424         obj->iOBJL = pOBJLArray->GetCount() - 1;
7425     }
7426 
7427 }
7428 
7429 int s52plib::SetLineFeaturePriority( ObjRazRules *rzRules, int npriority )
7430 {
7431 
7432     int priority_set = npriority; // may be adjusted
7433 
7434     Rules *rules = rzRules->LUP->ruleList;
7435 
7436     //      Do Object Type Filtering
7437     //    If the object s not currently visible (i.e. classed as a not-currently visible category),
7438     //    then do not set the line segment priorities at all
7439     //
7440 
7441     bool b_catfilter = true;
7442 
7443    // DEPCNT is mutable
7444     if( m_nDisplayCategory == STANDARD ) {
7445         if( ( DISPLAYBASE != rzRules->LUP->DISC ) && ( STANDARD != rzRules->LUP->DISC ) ) {
7446             b_catfilter = rzRules->obj->m_bcategory_mutable;
7447         }
7448     } else if( m_nDisplayCategory == DISPLAYBASE ) {
7449         if( DISPLAYBASE != rzRules->LUP->DISC ) {
7450             b_catfilter = rzRules->obj->m_bcategory_mutable;
7451         }
7452     }
7453 
7454     if( IsObjNoshow( rzRules->LUP->OBCL) )
7455         b_catfilter = false;
7456 
7457     if(!b_catfilter)            // No chance this object is visible
7458         return 0;
7459 
7460 
7461     while( rules != NULL ) {
7462         switch( rules->ruleType ){
7463 
7464             case RUL_SIM_LN:
7465             case RUL_COM_LN:
7466                 PrioritizeLineFeature( rzRules, priority_set );
7467                 break; // LC
7468 
7469             case RUL_CND_SY: {
7470                 if( !rzRules->obj->bCS_Added ) {
7471                     rzRules->obj->CSrules = NULL;
7472                     GetAndAddCSRules( rzRules, rules );
7473                     rzRules->obj->bCS_Added = 1; // mark the object
7474                 }
7475                 Rules *rules_last = rules;
7476                 rules = rzRules->obj->CSrules;
7477 
7478                 while( NULL != rules ) {
7479                     switch( rules->ruleType ){
7480                         case RUL_SIM_LN:
7481                         case RUL_COM_LN:
7482                             PrioritizeLineFeature( rzRules, priority_set );
7483                             break;
7484                         case RUL_NONE:
7485                         default:
7486                             break; // no rule type (init)
7487                     }
7488                     rules_last = rules;
7489                     rules = rules->next;
7490                 }
7491 
7492                 rules = rules_last;
7493                 break;
7494             }
7495 
7496             case RUL_NONE:
7497             default:
7498                 break; // no rule type (init)
7499         } // switch
7500 
7501         rules = rules->next;
7502     }
7503 
7504     return 1;
7505 }
7506 
7507 int s52plib::PrioritizeLineFeature( ObjRazRules *rzRules, int npriority )
7508 {
7509     if(rzRules->obj->m_ls_list){
7510 
7511         VE_Element *pedge;
7512         connector_segment *pcs;
7513         line_segment_element *ls = rzRules->obj->m_ls_list;
7514         while( ls ){
7515             switch (ls->ls_type){
7516                 case TYPE_EE:
7517                 case TYPE_EE_REV:
7518 
7519                     pedge = ls->pedge; //(VE_Element *)ls->private0;
7520                     if(pedge)
7521                         pedge->max_priority = npriority;// wxMax(pedge->max_priority, npriority);
7522                     break;
7523 
7524                 default:
7525                     pcs = ls->pcs; //(connector_segment *)ls->private0;
7526                     if(pcs)
7527                         pcs->max_priority_cs = npriority; //wxMax(pcs->max_priority, npriority);
7528                     break;
7529             }
7530 
7531             ls = ls->next;
7532         }
7533     }
7534 
7535     else if(rzRules->obj->m_ls_list_legacy){            // PlugIn (S63)
7536 
7537         PI_connector_segment *pcs;
7538         VE_Element *pedge;
7539 
7540         PI_line_segment_element *ls = rzRules->obj->m_ls_list_legacy;
7541         while( ls ){
7542             switch (ls->type){
7543                 case TYPE_EE:
7544 
7545                     pedge = (VE_Element *)ls->private0;
7546                     if(pedge)
7547                         pedge->max_priority = npriority;// wxMax(pedge->max_priority, npriority);
7548                     break;
7549 
7550                 default:
7551                     pcs = (PI_connector_segment *)ls->private0;
7552                     if(pcs)
7553                         pcs->max_priority = npriority; //wxMax(pcs->max_priority, npriority);
7554                     break;
7555             }
7556 
7557             ls = ls->next;
7558         }
7559     }
7560 #if 0
7561     else if( rzRules->obj->m_n_lsindex && rzRules->obj->m_lsindex_array) {
7562         VE_Hash *edge_hash;
7563 
7564         if( rzRules->obj->m_chart_context->chart ){
7565             edge_hash = &rzRules->obj->m_chart_context->chart->Get_ve_hash();
7566         }
7567         else {
7568             edge_hash = (VE_Hash *)rzRules->obj->m_chart_context->m_pve_hash;
7569         }
7570 
7571         int *index_run = rzRules->obj->m_lsindex_array;
7572 
7573         for( int iseg = 0; iseg < rzRules->obj->m_n_lsindex; iseg++ ) {
7574             //  Get first connected node
7575             int inode = *index_run++;
7576 
7577             VE_Element *pedge = 0;
7578             //  Get the edge
7579             int enode = *index_run++;
7580             if(enode)
7581                 pedge = (*edge_hash)[enode];
7582 
7583             //    Set priority
7584             if(pedge){
7585                 pedge->max_priority = npriority;
7586             }
7587 
7588             //  Get last connected node
7589             inode = *index_run++;
7590 
7591         }
7592     }
7593 #endif
7594 
7595     return 1;
7596 }
7597 
7598 class XPOINT {
7599 public:
7600     float x, y;
7601 };
7602 
7603 class XLINE {
7604 public:
7605     XPOINT o, p;
7606     float m;
7607     float c;
7608 };
7609 
7610 bool TestLinesIntersection( XLINE &a, XLINE &b )
7611 {
7612     XPOINT i;
7613 
7614     if( ( a.p.x == a.o.x ) && ( b.p.x == b.o.x ) ) // both vertical
7615             {
7616         return ( a.p.x == b.p.x );
7617     }
7618 
7619     if( a.p.x == a.o.x ) // a line a is vertical
7620             {
7621         // calculate b gradient
7622         b.m = ( b.p.y - b.o.y ) / ( b.p.x - b.o.x );
7623         // calculate axis intersect values
7624         b.c = b.o.y - ( b.m * b.o.x );
7625         // calculate y point of intercept
7626         i.y = b.o.y + ( ( a.o.x - b.o.x ) * b.m );
7627         if( i.y < wxMin(a.o.y, a.p.y) || i.y > wxMax(a.o.y, a.p.y) ) return false;
7628         return true;
7629     }
7630 
7631     if( b.p.x == b.o.x ) // line b is vertical
7632             {
7633         // calculate b gradient
7634         a.m = ( a.p.y - a.o.y ) / ( a.p.x - a.o.x );
7635         // calculate axis intersect values
7636         a.c = a.o.y - ( a.m * a.o.x );
7637         // calculate y point of intercept
7638         i.y = a.o.y + ( ( b.o.x - a.o.x ) * a.m );
7639         if( i.y < wxMin(b.o.y, b.p.y) || i.y > wxMax(b.o.y, b.p.y) ) return false;
7640         return true;
7641     }
7642 
7643 // calculate gradients
7644     a.m = ( a.p.y - a.o.y ) / ( a.p.x - a.o.x );
7645     b.m = ( b.p.y - b.o.y ) / ( b.p.x - b.o.x );
7646 // parallel lines can't intercept
7647     if( a.m == b.m ) {
7648         return false;
7649     }
7650     // calculate axis intersect values
7651     a.c = a.o.y - ( a.m * a.o.x );
7652     b.c = b.o.y - ( b.m * b.o.x );
7653 // calculate x point of intercept
7654     i.x = ( b.c - a.c ) / ( a.m - b.m );
7655 // is intersection point in segment
7656     if( i.x < wxMin(a.o.x, a.p.x) || i.x > wxMax(a.o.x, a.p.x) ) {
7657         return false;
7658     }
7659     if( i.x < wxMin(b.o.x, b.p.x) || i.x > wxMax(b.o.x, b.p.x) ) {
7660         return false;
7661     }
7662 // points intercept
7663     return true;
7664 }
7665 
7666 //-----------------------------------------------------------------------
7667 //    Check a triangle described by point array, and rectangle described by render_canvas_parms
7668 //    for intersection
7669 //    Return false if no intersection
7670 //-----------------------------------------------------------------------
7671 bool s52plib::inter_tri_rect( wxPoint *ptp, render_canvas_parms *pb_spec )
7672 {
7673     //    First stage
7674     //    Check all three points of triangle to see it any are within the render rectangle
7675 
7676     wxBoundingBox rect( pb_spec->lclip, pb_spec->y, pb_spec->rclip, pb_spec->y + pb_spec->height );
7677 
7678     for( int i = 0; i < 3; i++ ) {
7679         if( rect.PointInBox( ptp[i].x, ptp[i].y ) ) return true;
7680     }
7681 
7682     //    Next stage
7683     //    Check all four points of rectangle to see it any are within the render triangle
7684 
7685     double p[6];
7686     MyPoint *pmp = (MyPoint *) p;
7687 
7688     for( int i = 0; i < 3; i++ ) {
7689         pmp[i].x = ptp[i].x;
7690         pmp[i].y = ptp[i].y;
7691     }
7692 
7693     if( G_PtInPolygon( pmp, 3, pb_spec->lclip, pb_spec->y ) ) return true;
7694 
7695     if( G_PtInPolygon( pmp, 3, pb_spec->lclip, pb_spec->y + pb_spec->height ) ) return true;
7696 
7697     if( G_PtInPolygon( pmp, 3, pb_spec->rclip, pb_spec->y ) ) return true;
7698 
7699     if( G_PtInPolygon( pmp, 3, pb_spec->rclip, pb_spec->y + pb_spec->height ) ) return true;
7700 
7701     //    last step
7702     //    Check triangle lines against rect lines for line intersect
7703 
7704     for( int i = 0; i < 3; i++ ) {
7705         XLINE a;
7706         a.o.x = ptp[i].x;
7707         a.o.y = ptp[i].y;
7708         if( i == 2 ) {
7709             a.p.x = ptp[0].x;
7710             a.p.y = ptp[0].y;
7711         } else {
7712             a.p.x = ptp[i + 1].x;
7713             a.p.y = ptp[i + 1].y;
7714         }
7715 
7716         XLINE b;
7717 
7718         //    top line
7719         b.o.x = pb_spec->lclip;
7720         b.o.y = pb_spec->y;
7721         b.p.x = pb_spec->rclip;
7722         b.p.y = pb_spec->y;
7723 
7724         if( TestLinesIntersection( a, b ) ) return true;
7725 
7726         //    right line
7727         b.o.x = pb_spec->rclip;
7728         b.o.y = pb_spec->y;
7729         b.p.x = pb_spec->rclip;
7730         b.p.y = pb_spec->y + pb_spec->height;
7731 
7732         if( TestLinesIntersection( a, b ) ) return true;
7733 
7734         //    bottom line
7735         b.o.x = pb_spec->rclip;
7736         b.o.y = pb_spec->y + pb_spec->height;
7737         b.p.x = pb_spec->lclip;
7738         b.p.y = pb_spec->y + pb_spec->height;
7739 
7740         if( TestLinesIntersection( a, b ) ) return true;
7741 
7742         //    left line
7743         b.o.x = pb_spec->lclip;
7744         b.o.y = pb_spec->y + pb_spec->height;
7745         b.p.x = pb_spec->lclip;
7746         b.p.y = pb_spec->y;
7747 
7748         if( TestLinesIntersection( a, b ) ) return true;
7749     }
7750 
7751     return false; // no Intersection
7752 
7753 }
7754 
7755 //----------------------------------------------------------------------------------
7756 //
7757 //              Fast Basic Canvas Rendering
7758 //              Render triangle
7759 //
7760 //----------------------------------------------------------------------------------
7761 int s52plib::dda_tri( wxPoint *ptp, S52color *c, render_canvas_parms *pb_spec,
7762         render_canvas_parms *pPatt_spec )
7763 {
7764     unsigned char r = 0;
7765     unsigned char g = 0;
7766     unsigned char b = 0;
7767 
7768     if( !inter_tri_rect( ptp, pb_spec ) ) return 0;
7769 
7770     if( NULL != c ) {
7771         if(pb_spec->b_revrgb) {
7772             r = c->R;
7773             g = c->G;
7774             b = c->B;
7775         }
7776         else {
7777             b = c->R;
7778             g = c->G;
7779             r = c->B;
7780         }
7781     }
7782 
7783     //      Color Debug
7784     /*    int fc = rand();
7785      b = fc & 0xff;
7786      g = fc & 0xff;
7787      r = fc & 0xff;
7788      */
7789 
7790     int color_int = 0;
7791     if( NULL != c ) color_int = ( ( r ) << 16 ) + ( ( g ) << 8 ) + ( b );
7792 
7793     //      Determine ymin and ymax indices
7794 
7795     int ymax = ptp[0].y;
7796     int ymin = ymax;
7797     int xmin, xmax, xmid, ymid;
7798     int imin = 0;
7799     int imax = 0;
7800     int imid;
7801 
7802     for( int ip = 1; ip < 3; ip++ ) {
7803         if( ptp[ip].y > ymax ) {
7804             imax = ip;
7805             ymax = ptp[ip].y;
7806         }
7807         if( ptp[ip].y <= ymin ) {
7808             imin = ip;
7809             ymin = ptp[ip].y;
7810         }
7811     }
7812 
7813     imid = 3 - ( imin + imax ); // do the math...
7814 
7815     xmax = ptp[imax].x;
7816     xmin = ptp[imin].x;
7817     xmid = ptp[imid].x;
7818     ymid = ptp[imid].y;
7819 
7820     //      Create edge arrays using fast integer DDA
7821     int m, x, dy, count;
7822     bool cw;
7823 
7824     if( ( abs( xmax - xmin ) > 32768 ) || ( abs( xmid - xmin ) > 32768 )
7825             || ( abs( xmax - xmid ) > 32768 ) || ( abs( ymax - ymin ) > 32768 )
7826             || ( abs( ymid - ymin ) > 32768 ) || ( abs( ymax - ymid ) > 32768 ) || ( xmin > 32768 )
7827             || ( xmid > 32768 ) ) {
7828 
7829         dy = ( ymax - ymin );
7830         if( dy ) {
7831             m = ( xmax - xmin ) << 8;
7832             m /= dy;
7833 
7834             x = xmin << 8;
7835 
7836             for( count = ymin; count <= ymax; count++ ) {
7837                 if( ( count >= 0 ) && ( count < 1500 ) ) ledge[count] = x >> 8;
7838                 x += m;
7839             }
7840         }
7841 
7842         dy = ( ymid - ymin );
7843         if( dy ) {
7844             m = ( xmid - xmin ) << 8;
7845             m /= dy;
7846 
7847             x = xmin << 8;
7848 
7849             for( count = ymin; count <= ymid; count++ ) {
7850                 if( ( count >= 0 ) && ( count < 1500 ) ) redge[count] = x >> 8;
7851                 x += m;
7852             }
7853         }
7854 
7855         dy = ( ymax - ymid );
7856         if( dy ) {
7857             m = ( xmax - xmid ) << 8;
7858             m /= dy;
7859 
7860             x = xmid << 8;
7861 
7862             for( count = ymid; count <= ymax; count++ ) {
7863                 if( ( count >= 0 ) && ( count < 1500 ) ) redge[count] = x >> 8;
7864                 x += m;
7865             }
7866         }
7867 
7868         double ddfSum = 0;
7869         //      Check the triangle edge winding direction
7870         ddfSum += ( xmin / 1 ) * ( ymax / 1 ) - ( ymin / 1 ) * ( xmax / 1 );
7871         ddfSum += ( xmax / 1 ) * ( ymid / 1 ) - ( ymax / 1 ) * ( xmid / 1 );
7872         ddfSum += ( xmid / 1 ) * ( ymin / 1 ) - ( ymid / 1 ) * ( xmin / 1 );
7873         cw = ddfSum < 0;
7874 
7875     } else {
7876 
7877         dy = ( ymax - ymin );
7878         if( dy ) {
7879             m = ( xmax - xmin ) << 16;
7880             m /= dy;
7881 
7882             x = xmin << 16;
7883 
7884             for( count = ymin; count <= ymax; count++ ) {
7885                 if( ( count >= 0 ) && ( count < 1500 ) ) ledge[count] = x >> 16;
7886                 x += m;
7887             }
7888         }
7889 
7890         dy = ( ymid - ymin );
7891         if( dy ) {
7892             m = ( xmid - xmin ) << 16;
7893             m /= dy;
7894 
7895             x = xmin << 16;
7896 
7897             for( count = ymin; count <= ymid; count++ ) {
7898                 if( ( count >= 0 ) && ( count < 1500 ) ) redge[count] = x >> 16;
7899                 x += m;
7900             }
7901         }
7902 
7903         dy = ( ymax - ymid );
7904         if( dy ) {
7905             m = ( xmax - xmid ) << 16;
7906             m /= dy;
7907 
7908             x = xmid << 16;
7909 
7910             for( count = ymid; count <= ymax; count++ ) {
7911                 if( ( count >= 0 ) && ( count < 1500 ) ) redge[count] = x >> 16;
7912                 x += m;
7913             }
7914         }
7915 
7916         //      Check the triangle edge winding direction
7917         long dfSum = 0;
7918         dfSum += xmin * ymax - ymin * xmax;
7919         dfSum += xmax * ymid - ymax * xmid;
7920         dfSum += xmid * ymin - ymid * xmin;
7921 
7922         cw = dfSum < 0;
7923 
7924     } // else
7925 
7926     //      if cw is true, redge is actually on the right
7927 
7928     int y1 = ymax;
7929     int y2 = ymin;
7930 
7931     int ybt = pb_spec->y;
7932     int yt = pb_spec->y + pb_spec->height;
7933 
7934     if( y1 > yt ) y1 = yt;
7935     if( y1 < ybt ) y1 = ybt;
7936 
7937     if( y2 > yt ) y2 = yt;
7938     if( y2 < ybt ) y2 = ybt;
7939 
7940     int lclip = pb_spec->lclip;
7941     int rclip = pb_spec->rclip;
7942     if (y1 == y2 )
7943         return 0;
7944 
7945     //              Clip the triangle
7946     if( cw ) {
7947         for( int iy = y2; iy <= y1; iy++ ) {
7948             if( ledge[iy] < lclip ) {
7949                 if( redge[iy] < lclip ) ledge[iy] = -1;
7950                 else
7951                     ledge[iy] = lclip;
7952             }
7953 
7954             if( redge[iy] > rclip ) {
7955                 if( ledge[iy] > rclip ) ledge[iy] = -1;
7956                 else
7957                     redge[iy] = rclip;
7958             }
7959         }
7960     } else {
7961         for( int iy = y2; iy <= y1; iy++ ) {
7962             if( redge[iy] < lclip ) {
7963                 if( ledge[iy] < lclip ) ledge[iy] = -1;
7964                 else
7965                     redge[iy] = lclip;
7966             }
7967 
7968             if( ledge[iy] > rclip ) {
7969                 if( redge[iy] > rclip ) ledge[iy] = -1;
7970                 else
7971                     ledge[iy] = rclip;
7972             }
7973         }
7974     }
7975 
7976     //              Fill the triangle
7977 
7978     int ya = y2;
7979     int yb = y1;
7980 
7981     unsigned char *pix_buff = pb_spec->pix_buff;
7982 
7983     int patt_size_x = 0, patt_size_y = 0, patt_pitch = 0;
7984     unsigned char *patt_s0 = NULL;
7985     if( pPatt_spec ) {
7986         patt_size_y = pPatt_spec->height;
7987         patt_size_x = pPatt_spec->width;
7988         patt_pitch = pPatt_spec->pb_pitch;
7989         patt_s0 = pPatt_spec->pix_buff;
7990 
7991         if(patt_size_y == 0) /* integer division by this value below */
7992             return false;
7993     }
7994 
7995     if( pb_spec->depth == 24 ) {
7996         for( int iyp = ya; iyp < yb; iyp++ ) {
7997             if( ( iyp >= ybt ) && ( iyp < yt ) ) {
7998                 int yoff = ( iyp - pb_spec->y ) * pb_spec->pb_pitch;
7999 
8000                 unsigned char *py = pix_buff + yoff;
8001 
8002                 int ix, ixm;
8003                 if( cw ) {
8004                     ix = ledge[iyp];
8005                     ixm = redge[iyp];
8006                 } else {
8007                     ixm = ledge[iyp];
8008                     ix = redge[iyp];
8009                 }
8010 
8011                 if( ledge[iyp] != -1 ) {
8012 
8013                     //    This would be considered a failure of the dda algorithm
8014                     //    Happens on very high zoom, with very large triangles.
8015                     //    The integers of the dda algorithm don't have enough bits...
8016                     //    Anyway, just ignore this triangle if it happens
8017                     if( ix > ixm ) continue;
8018 
8019                     int xoff = ( ix - pb_spec->x ) * 3;
8020 
8021                     unsigned char *px = py + xoff;
8022 
8023                     if( pPatt_spec  ) // Pattern
8024                     {
8025                         int y_stagger = ( iyp - pPatt_spec->y ) / patt_size_y;
8026                         int x_stagger_off = 0;
8027                         if( ( y_stagger & 1 ) && pPatt_spec->b_stagger ) x_stagger_off =
8028                                 pPatt_spec->width / 2;
8029 
8030                         int patt_y = abs( ( iyp - pPatt_spec->y ) ) % patt_size_y;
8031 
8032                         unsigned char *pp0 = patt_s0 + ( patt_y * patt_pitch );
8033 
8034                         while( ix <= ixm ) {
8035                             int patt_x = abs( ( ( ix - pPatt_spec->x ) + x_stagger_off ) % patt_size_x );
8036 
8037                             unsigned char *pp = pp0 + ( patt_x * 4 );
8038                             unsigned char alpha = pp[3];
8039                             double da = (double) alpha / 256.;
8040 
8041                             unsigned char r = (unsigned char) ( *px*(1.0-da) + pp[0] * da );
8042                             unsigned char g = (unsigned char) ( *(px+1)*(1.0-da) + pp[1] * da );
8043                             unsigned char b = (unsigned char) ( *(px+2)*(1.0-da) + pp[2] * da );
8044 
8045                             *px++ = r;
8046                             *px++ = g;
8047                             *px++ = b;
8048                             ix++;
8049                         }
8050                     }
8051 
8052                     else // No Pattern
8053                     {
8054 #if defined( __WXGTK__) && defined(__INTEL__)
8055 #define memset3(dest, value, count) \
8056 __asm__ __volatile__ ( \
8057 "cmp $0,%2\n\t" \
8058 "jg 2f\n\t" \
8059 "je 3f\n\t" \
8060 "jmp 4f\n\t" \
8061 "2:\n\t" \
8062 "movl  %0,(%1)\n\t" \
8063 "add $3,%1\n\t" \
8064 "dec %2\n\t" \
8065 "jnz 2b\n\t" \
8066 "3:\n\t" \
8067 "movb %b0,(%1)\n\t" \
8068 "inc %1\n\t" \
8069 "movb %h0,(%1)\n\t" \
8070 "inc %1\n\t" \
8071 "shr $16,%0\n\t" \
8072 "movb %b0,(%1)\n\t" \
8073 "4:\n\t" \
8074 : : "a"(value), "D"(dest), "r"(count) :  );
8075 
8076                         int count = ixm-ix;
8077                         memset3 ( px, color_int, count )
8078 #else
8079 
8080                         while( ix <= ixm ) {
8081                             *px++ = b;
8082                             *px++ = g;
8083                             *px++ = r;
8084 
8085                             ix++;
8086                         }
8087 #endif
8088                     }
8089                 }
8090             }
8091         }
8092     }
8093 
8094     if( pb_spec->depth == 32 ) {
8095 
8096         assert( ya <= yb );
8097 
8098         for( int iyp = ya; iyp < yb; iyp++ ) {
8099             if( ( iyp >= ybt ) && ( iyp < yt ) ) {
8100                 int yoff = ( iyp - pb_spec->y ) * pb_spec->pb_pitch;
8101 
8102                 unsigned char *py = pix_buff + yoff;
8103 
8104                 int ix, ixm;
8105                 if( cw ) {
8106                     ix = ledge[iyp];
8107                     ixm = redge[iyp];
8108                 } else {
8109                     ixm = ledge[iyp];
8110                     ix = redge[iyp];
8111                 }
8112 
8113                 if( ledge[iyp] != -1 ) {
8114                     //    This would be considered a failure of the dda algorithm
8115                     //    Happens on very high zoom, with very large triangles.
8116                     //    The integers of the dda algorithm don't have enough bits...
8117                     //    Anyway, just ignore this triangle if it happens
8118                     if( ix > ixm ) continue;
8119 
8120                     int xoff = ( ix - pb_spec->x ) * pb_spec->depth / 8;
8121 
8122                     unsigned char *px = py + xoff;
8123 
8124                     if( pPatt_spec ) // Pattern
8125                     {
8126                         int y_stagger = ( iyp - pPatt_spec->y ) / patt_size_y;
8127 
8128                         int x_stagger_off = 0;
8129                         if( ( y_stagger & 1 ) && pPatt_spec->b_stagger ) x_stagger_off =
8130                                 pPatt_spec->width / 2;
8131 
8132                         int patt_y = abs( ( iyp - pPatt_spec->y ) ) % patt_size_y;
8133 
8134                         unsigned char *pp0 = patt_s0 + ( patt_y * patt_pitch );
8135 
8136                         while( ix <= ixm ) {
8137                             int patt_x = abs(
8138                                     ( ( ix - pPatt_spec->x ) + x_stagger_off ) % patt_size_x );
8139                             /*
8140                              if(pPatt_spec->depth == 24)
8141                              {
8142                              unsigned char *pp = pp0 + (patt_x * 3);
8143 
8144                              //  Todo    This line assumes unused_color is always 0,0,0
8145                              if( pp[0] && pp[1] && pp[2] ) {
8146                              *px++ = *pp++;
8147                              *px++ = *pp++;
8148                              *px++ = *pp++;
8149                              px++;
8150                              } else {
8151                              px += 4;
8152                              //                                                      pp += 4;
8153                              }
8154                              }
8155                              else
8156                              */
8157                             {
8158                                 unsigned char *pp = pp0 + ( patt_x * 4 );
8159                                 unsigned char alpha = pp[3];
8160                                 if( alpha > 128 ) {
8161                                     double da = (double) alpha / 256.;
8162 
8163                                     unsigned char r = (unsigned char) ( pp[0] * da );
8164                                     unsigned char g = (unsigned char) ( pp[1] * da );
8165                                     unsigned char b = (unsigned char) ( pp[2] * da );
8166 
8167                                     *px++ = r;
8168                                     *px++ = g;
8169                                     *px++ = b;
8170                                     px++;
8171                                 } else
8172                                     px += 4;
8173                             }
8174                             ix++;
8175                         }
8176                     }
8177 
8178                     else // No Pattern
8179                     {
8180                         int *pxi = (int *) px;
8181                         while( ix <= ixm ) {
8182                             *pxi++ = color_int;
8183                             ix++;
8184                         }
8185                     }
8186                 }
8187             }
8188         }
8189     }
8190 
8191     return true;
8192 }
8193 
8194 //----------------------------------------------------------------------------------
8195 //
8196 //              Render Trapezoid
8197 //
8198 //----------------------------------------------------------------------------------
8199 inline int s52plib::dda_trap( wxPoint *segs, int lseg, int rseg, int ytop, int ybot, S52color *c,
8200         render_canvas_parms *pb_spec, render_canvas_parms *pPatt_spec )
8201 {
8202     unsigned char r = 0, g = 0, b = 0;
8203 
8204     if( NULL != c ) {
8205         if(pb_spec->b_revrgb) {
8206             r = c->R;
8207             g = c->G;
8208             b = c->B;
8209         }
8210         else {
8211             b = c->R;
8212             g = c->G;
8213             r = c->B;
8214         }
8215     }
8216 
8217     //      Color Debug
8218     /*    int fc = rand();
8219      b = fc & 0xff;
8220      g = fc & 0xff;
8221      r = fc & 0xff;
8222      */
8223 
8224 //      int debug = 0;
8225     int ret_val = 0;
8226 
8227     int color_int = 0;
8228     if( NULL != c ) color_int = ( ( r ) << 16 ) + ( ( g ) << 8 ) + ( b );
8229 
8230     //      Create edge arrays using fast integer DDA
8231 
8232     int lclip = pb_spec->lclip;
8233     int rclip = pb_spec->rclip;
8234 
8235     int m, x, dy, count;
8236 
8237     //    Left edge
8238     int xmax = segs[lseg].x;
8239     int xmin = segs[lseg + 1].x;
8240     int ymax = segs[lseg].y;
8241     int ymin = segs[lseg + 1].y;
8242 
8243     if( ymax < ymin ) {
8244         int a = ymax;
8245         ymax = ymin;
8246         ymin = a;
8247 
8248         a = xmax;
8249         xmax = xmin;
8250         xmin = a;
8251     }
8252 
8253     int y_dda_limit = wxMin ( ybot, ymax );
8254     y_dda_limit = wxMin ( y_dda_limit, 1499 ); // don't overrun edge array
8255 
8256     //    Some peephole optimization:
8257     //    if xmax and xmin are both < 0, arrange to simply fill the ledge array with 0
8258     if( ( xmax < 0 ) && ( xmin < 0 ) ) {
8259         xmax = -2;
8260         xmin = -2;
8261     }
8262     //    if xmax and xmin are both > rclip, arrange to simply fill the ledge array with rclip + 1
8263     //    This may induce special clip case below, and cause trap not to be rendered
8264     else
8265         if( ( xmax > rclip ) && ( xmin > rclip ) ) {
8266             xmax = rclip + 1;
8267             xmin = rclip + 1;
8268         }
8269 
8270     dy = ( ymax - ymin );
8271     if( dy ) {
8272         m = ( xmax - xmin ) << 16;
8273         m /= dy;
8274 
8275         x = xmin << 16;
8276 
8277         //TODO implement this logic in dda_tri also
8278         count = ymin;
8279         while( count < 0 ) {
8280             x += m;
8281             count++;
8282         }
8283 
8284         while( count < y_dda_limit ) {
8285             ledge[count] = x >> 16;
8286             x += m;
8287             count++;
8288         }
8289     }
8290 
8291     if( ( ytop < ymin ) || ( ybot > ymax ) ) {
8292 //            printf ( "### ledge out of range\n" );
8293         ret_val = 1;
8294 //            r=255;
8295 //            g=0;
8296 //            b=0;
8297     }
8298 
8299     //    Right edge
8300     xmax = segs[rseg].x;
8301     xmin = segs[rseg + 1].x;
8302     ymax = segs[rseg].y;
8303     ymin = segs[rseg + 1].y;
8304 
8305 //Note this never gets hit???
8306     if( ymax < ymin ) {
8307         int a = ymax;
8308         ymax = ymin;
8309         ymin = a;
8310 
8311         a = xmax;
8312         xmax = xmin;
8313         xmin = a;
8314     }
8315 
8316     //    Some peephole optimization:
8317     //    if xmax and xmin are both < 0, arrange to simply fill the redge array with -1
8318     //    This may induce special clip case below, and cause trap not to be rendered
8319     if( ( xmax < 0 ) && ( xmin < 0 ) ) {
8320         xmax = -1;
8321         xmin = -1;
8322     }
8323 
8324     //    if xmax and xmin are both > rclip, arrange to simply fill the redge array with rclip + 1
8325     //    This may induce special clip case below, and cause trap not to be rendered
8326     else
8327         if( ( xmax > rclip ) && ( xmin > rclip ) ) {
8328             xmax = rclip + 1;
8329             xmin = rclip + 1;
8330         }
8331 
8332     y_dda_limit = wxMin ( ybot, ymax );
8333     y_dda_limit = wxMin ( y_dda_limit, 1499 ); // don't overrun edge array
8334 
8335     dy = ( ymax - ymin );
8336     if( dy ) {
8337         m = ( xmax - xmin ) << 16;
8338         m /= dy;
8339 
8340         x = xmin << 16;
8341 
8342         count = ymin;
8343         while( count < 0 ) {
8344             x += m;
8345             count++;
8346         }
8347 
8348         while( count < y_dda_limit ) {
8349             redge[count] = x >> 16;
8350             x += m;
8351             count++;
8352         }
8353     }
8354 
8355     if( ( ytop < ymin ) || ( ybot > ymax ) ) {
8356 //            printf ( "### redge out of range\n" );
8357         ret_val = 1;
8358 //            r=255;
8359 //            g=0;
8360 //            b=0;
8361     }
8362 
8363     //    Clip trapezoid to height spec
8364     int y1 = ybot;
8365     int y2 = ytop;
8366 
8367     int ybt = pb_spec->y;
8368     int yt = pb_spec->y + pb_spec->height;
8369 
8370     if( y1 > yt ) y1 = yt;
8371     if( y1 < ybt ) y1 = ybt;
8372 
8373     if( y2 > yt ) y2 = yt;
8374     if( y2 < ybt ) y2 = ybt;
8375 
8376     //   Clip the trapezoid to width
8377     for( int iy = y2; iy <= y1; iy++ ) {
8378         if( ledge[iy] < lclip ) {
8379             if( redge[iy] < lclip ) ledge[iy] = -1;
8380             else
8381                 ledge[iy] = lclip;
8382         }
8383 
8384         if( redge[iy] > rclip ) {
8385             if( ledge[iy] > rclip ) ledge[iy] = -1;
8386             else
8387                 redge[iy] = rclip;
8388         }
8389     }
8390 
8391     //    Fill the trapezoid
8392 
8393     int ya = y2;
8394     int yb = y1;
8395 
8396     unsigned char *pix_buff = pb_spec->pix_buff;
8397 
8398     int patt_size_x = 0;
8399     int patt_size_y = 0;
8400     int patt_pitch = 0;
8401     unsigned char *patt_s0 = NULL;
8402     if( pPatt_spec ) {
8403         patt_size_y = pPatt_spec->height;
8404         patt_size_x = pPatt_spec->width;
8405         patt_pitch = pPatt_spec->pb_pitch;
8406         patt_s0 = pPatt_spec->pix_buff;
8407     }
8408 
8409     if( pb_spec->depth == 24 ) {
8410         for( int iyp = ya; iyp < yb; iyp++ ) {
8411             if( ( iyp >= ybt ) && ( iyp < yt ) ) {
8412                 int yoff = ( iyp - pb_spec->y ) * pb_spec->pb_pitch;
8413 
8414                 unsigned char *py = pix_buff + yoff;
8415 
8416                 int ix, ixm;
8417                 ix = ledge[iyp];
8418                 ixm = redge[iyp];
8419 
8420 //                        if(debug) printf("iyp %d, ix %d, ixm %d\n", iyp, ix, ixm);
8421 //                           int ix = ledge[iyp];
8422 //                            if(ix != -1)                    // special clip case
8423                 if( ledge[iyp] != -1 ) {
8424                     int xoff = ( ix - pb_spec->x ) * 3;
8425 
8426                     unsigned char *px = py + xoff;
8427 
8428                     if( pPatt_spec ) // Pattern
8429                     {
8430                         int y_stagger = ( iyp - pPatt_spec->y ) / patt_size_y;
8431                         int x_stagger_off = 0;
8432                         if( ( y_stagger & 1 ) && pPatt_spec->b_stagger ) x_stagger_off =
8433                                 pPatt_spec->width / 2;
8434 
8435                         int patt_y = abs( ( iyp - pPatt_spec->y ) ) % patt_size_y;
8436                         unsigned char *pp0 = patt_s0 + ( patt_y * patt_pitch );
8437 
8438                         while( ix <= ixm ) {
8439                             int patt_x = abs(
8440                                     ( ( ix - pPatt_spec->x ) + x_stagger_off ) % patt_size_x );
8441                             /*
8442                              if(pPatt_spec->depth == 24)
8443                              {
8444                              unsigned char *pp = pp0 + (patt_x * 3);
8445 
8446                              //  Todo    This line assumes unused_color is always 0,0,0
8447                              if( pp[0] && pp[1] && pp[2] ) {
8448                              *px++ = *pp++;
8449                              *px++ = *pp++;
8450                              *px++ = *pp++;
8451                              } else {
8452                              px += 3;
8453                              pp += 3;
8454                              }
8455                              }
8456                              else
8457                              */
8458                             {
8459                                 unsigned char *pp = pp0 + ( patt_x * 4 );
8460                                 unsigned char alpha = pp[3];
8461                                 if( alpha > 128 ) {
8462                                     double da = (double) alpha / 256.;
8463 
8464                                     unsigned char r = (unsigned char) ( pp[0] * da );
8465                                     unsigned char g = (unsigned char) ( pp[1] * da );
8466                                     unsigned char b = (unsigned char) ( pp[2] * da );
8467 
8468                                     *px++ = r;
8469                                     *px++ = g;
8470                                     *px++ = b;
8471                                 } else
8472                                     px += 3;
8473                             }
8474 
8475                             ix++;
8476                         }
8477                     }
8478 
8479                     else // No Pattern
8480                     {
8481 #if defined(__WXGTK__WITH_OPTIMIZE_0) && defined(__INTEL__)
8482 #define memset3d(dest, value, count) \
8483                                     __asm__ __volatile__ ( \
8484                                     "cmp $0,%2\n\t" \
8485                                     "jg ld0\n\t" \
8486                                     "je ld1\n\t" \
8487                                     "jmp ld2\n\t" \
8488                                     "ld0:\n\t" \
8489                                     "movl  %0,(%1)\n\t" \
8490                                     "add $3,%1\n\t" \
8491                                     "dec %2\n\t" \
8492                                     "jnz ld0\n\t" \
8493                                     "ld1:\n\t" \
8494                                     "movb %b0,(%1)\n\t" \
8495                                     "inc %1\n\t" \
8496                                     "movb %h0,(%1)\n\t" \
8497                                     "inc %1\n\t" \
8498                                     "shr $16,%0\n\t" \
8499                                     "movb %b0,(%1)\n\t" \
8500                                     "ld2:\n\t" \
8501                                           : : "a"(value), "D"(dest), "r"(count) :  );
8502                         int count = ixm-ix;
8503                         memset3d ( px, color_int, count )
8504 #else
8505 
8506                         while( ix <= ixm ) {
8507                             *px++ = b;
8508                             *px++ = g;
8509                             *px++ = r;
8510 
8511                             ix++;
8512                         }
8513 #endif
8514                     }
8515                 }
8516             }
8517         }
8518     }
8519 
8520     if( pb_spec->depth == 32 ) {
8521 
8522         assert( ya <= yb );
8523 
8524         for( int iyp = ya; iyp < yb; iyp++ ) {
8525             if( ( iyp >= ybt ) && ( iyp < yt ) ) {
8526                 int yoff = ( iyp - pb_spec->y ) * pb_spec->pb_pitch;
8527 
8528                 unsigned char *py = pix_buff + yoff;
8529 
8530                 int ix, ixm;
8531                 ix = ledge[iyp];
8532                 ixm = redge[iyp];
8533 
8534                 if( ledge[iyp] != -1 ) {
8535                     int xoff = ( ix - pb_spec->x ) * pb_spec->depth / 8;
8536 
8537                     unsigned char *px = py + xoff;
8538 
8539                     if( pPatt_spec ) // Pattern
8540                     {
8541                         int y_stagger = ( iyp - pPatt_spec->y ) / patt_size_y;
8542                         int x_stagger_off = 0;
8543                         if( ( y_stagger & 1 ) && pPatt_spec->b_stagger ) x_stagger_off =
8544                                 pPatt_spec->width / 2;
8545 
8546                         int patt_y = abs( ( iyp - pPatt_spec->y ) ) % patt_size_y;
8547                         unsigned char *pp0 = patt_s0 + ( patt_y * patt_pitch );
8548 
8549                         while( ix <= ixm ) {
8550                             int patt_x = abs(
8551                                     ( ( ix - pPatt_spec->x ) + x_stagger_off ) % patt_size_x );
8552                             /*
8553                              if(pPatt_spec->depth == 24)
8554                              {
8555                              unsigned char *pp = pp0 + (patt_x * 3);
8556 
8557                              //  Todo    This line assumes unused_color is always 0,0,0
8558                              if( pp[0] && pp[1] && pp[2] ) {
8559                              *px++ = *pp++;
8560                              *px++ = *pp++;
8561                              *px++ = *pp++;
8562                              px++;
8563                              } else {
8564                              px += 4;
8565                              //                                                      pp += 3;
8566                              }
8567                              }
8568                              else
8569                              */
8570                             {
8571                                 unsigned char *pp = pp0 + ( patt_x * 4 );
8572                                 unsigned char alpha = pp[3];
8573                                 if( alpha > 128 ) {
8574                                     double da = (double) alpha / 256.;
8575 
8576                                     unsigned char r = (unsigned char) ( pp[0] * da );
8577                                     unsigned char g = (unsigned char) ( pp[1] * da );
8578                                     unsigned char b = (unsigned char) ( pp[2] * da );
8579 
8580                                     *px++ = r;
8581                                     *px++ = g;
8582                                     *px++ = b;
8583                                     px++;
8584                                 } else
8585                                     px += 4;
8586                             }
8587                             ix++;
8588                         }
8589                     }
8590 
8591                     else // No Pattern
8592                     {
8593                         int *pxi = (int *) px;
8594                         while( ix <= ixm ) {
8595                             *pxi++ = color_int;
8596                             ix++;
8597                         }
8598                     }
8599 
8600                 }
8601             }
8602         }
8603     }
8604 
8605     return ret_val;
8606 }
8607 
8608 void s52plib::RenderToBufferFilledPolygon( ObjRazRules *rzRules, S57Obj *obj, S52color *c,
8609                                            render_canvas_parms *pb_spec, render_canvas_parms *pPatt_spec, ViewPort *vp )
8610 {
8611 //    LLBBox BBView = vp->GetBBox();
8612         LLBBox BBView = vp->GetBBox();
8613     // please untangle this logic with the logic below
8614     if(BBView.GetMaxLon()+180 < vp->clon)
8615         BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360,
8616                    BBView.GetMaxLat(), BBView.GetMaxLon() + 360);
8617     else if(BBView.GetMinLon()-180 > vp->clon)
8618         BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360,
8619                    BBView.GetMaxLat(), BBView.GetMaxLon() - 360);
8620 
8621 
8622     S52color cp;
8623     if( NULL != c ) {
8624         cp.R = c->R;
8625         cp.G = c->G;
8626         cp.B = c->B;
8627     }
8628 
8629     if( obj->pPolyTessGeo ) {
8630         if( !rzRules->obj->pPolyTessGeo->IsOk() ){ // perform deferred tesselation
8631             rzRules->obj->pPolyTessGeo->BuildDeferredTess();
8632         }
8633 
8634         wxPoint *pp3 = (wxPoint *) malloc( 3 * sizeof(wxPoint) );
8635         wxPoint *ptp = (wxPoint *) malloc(
8636                 ( obj->pPolyTessGeo->GetnVertexMax() + 1 ) * sizeof(wxPoint) );
8637 
8638         PolyTriGroup *ppg = obj->pPolyTessGeo->Get_PolyTriGroup_head();
8639 
8640         TriPrim *p_tp = ppg->tri_prim_head;
8641         while( p_tp ) {
8642             LLBBox box;
8643             if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
8644                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
8645                 box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
8646             }
8647             else
8648                 box = p_tp->tri_box;
8649 
8650             if(!BBView.IntersectOut(box)) {
8651                 //      Get and convert the points
8652                 wxPoint *pr = ptp;
8653 
8654                 if(ppg->data_type == DATA_TYPE_DOUBLE){
8655                     double *pvert_list = p_tp->p_vertex;
8656 
8657                     for( int iv = 0; iv < p_tp->nVert; iv++ ) {
8658                         double lon = *pvert_list++;
8659                         double lat = *pvert_list++;
8660                         GetPointPixSingle( rzRules, lat, lon, pr, vp );
8661 
8662                         pr++;
8663                     }
8664                 }
8665                 else {
8666                     float *pvert_list = (float *)p_tp->p_vertex;
8667 
8668                     for( int iv = 0; iv < p_tp->nVert; iv++ ) {
8669                         double lon = *pvert_list++;
8670                         double lat = *pvert_list++;
8671                         GetPointPixSingle( rzRules, lat, lon, pr, vp );
8672 
8673                         pr++;
8674                     }
8675                 }
8676 
8677                 switch( p_tp->type ){
8678                     case PTG_TRIANGLE_FAN: {
8679                         for( int it = 0; it < p_tp->nVert - 2; it++ ) {
8680                             pp3[0].x = ptp[0].x;
8681                             pp3[0].y = ptp[0].y;
8682 
8683                             pp3[1].x = ptp[it + 1].x;
8684                             pp3[1].y = ptp[it + 1].y;
8685 
8686                             pp3[2].x = ptp[it + 2].x;
8687                             pp3[2].y = ptp[it + 2].y;
8688 
8689                             dda_tri( pp3, &cp, pb_spec, pPatt_spec );
8690                         }
8691                         break;
8692                     }
8693                     case PTG_TRIANGLE_STRIP: {
8694                         for( int it = 0; it < p_tp->nVert - 2; it++ ) {
8695                             pp3[0].x = ptp[it].x;
8696                             pp3[0].y = ptp[it].y;
8697 
8698                             pp3[1].x = ptp[it + 1].x;
8699                             pp3[1].y = ptp[it + 1].y;
8700 
8701                             pp3[2].x = ptp[it + 2].x;
8702                             pp3[2].y = ptp[it + 2].y;
8703 
8704                             dda_tri( pp3, &cp, pb_spec, pPatt_spec );
8705                         }
8706                         break;
8707                     }
8708                     case PTG_TRIANGLES: {
8709 
8710                         for( int it = 0; it < p_tp->nVert; it += 3 ) {
8711                             pp3[0].x = ptp[it].x;
8712                             pp3[0].y = ptp[it].y;
8713 
8714                             pp3[1].x = ptp[it + 1].x;
8715                             pp3[1].y = ptp[it + 1].y;
8716 
8717                             pp3[2].x = ptp[it + 2].x;
8718                             pp3[2].y = ptp[it + 2].y;
8719 
8720                             dda_tri( pp3, &cp, pb_spec, pPatt_spec );
8721                         }
8722                         break;
8723 
8724                     }
8725                 }
8726             } // if bbox
8727 
8728             // pick up the next in chain
8729             if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
8730                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
8731                 p_tp = (TriPrim *)p_ltp->p_next;
8732             }
8733             else
8734                 p_tp = p_tp->p_next;
8735 
8736         } // while
8737 
8738         free( ptp );
8739         free( pp3 );
8740     } // if pPolyTessGeo
8741 }
8742 
8743 int s52plib::RenderToGLAC( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
8744 {
8745 #ifdef ocpnUSE_GL
8746     GLenum reset_err = glGetError();
8747 
8748     S52color *c;
8749     char *str = (char*) rules->INSTstr;
8750 
8751     c = getColor( str );
8752 
8753 #ifndef ocpnUSE_GLES // linestipple is emulated poorly
8754     glColor3ub( c->R, c->G, c->B );
8755 #endif
8756 
8757     LLBBox BBView = vp->GetBBox();
8758     // please untangle this logic with the logic below
8759     if(BBView.GetMaxLon()+180 < vp->clon)
8760         BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360,
8761                    BBView.GetMaxLat(), BBView.GetMaxLon() + 360);
8762     else if(BBView.GetMinLon()-180 > vp->clon)
8763         BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360,
8764                    BBView.GetMaxLat(), BBView.GetMaxLon() - 360);
8765 
8766     //  Allow a little slop in calculating whether a triangle
8767     //  is within the requested Viewport
8768     double margin = BBView.GetLonRange() * .05;
8769     BBView.EnLarge( margin );
8770 
8771     bool b_useVBO = m_useVBO && !rzRules->obj->auxParm1 && vp->m_projection_type == PROJECTION_MERCATOR;
8772 
8773     if( rzRules->obj->pPolyTessGeo ) {
8774 
8775         bool b_temp_vbo = false;
8776         bool b_transform = false;
8777 
8778         // Set up the OpenGL transform matrix for this object
8779         // We transform from SENC SM vertex data to screen.
8780 
8781 #ifndef USE_ANDROID_GLES2
8782 
8783         glColor3ub( c->R, c->G, c->B );
8784 
8785         //  First, the VP transform
8786         if(b_useVBO || vp->m_projection_type == PROJECTION_MERCATOR) {
8787             b_transform = true;
8788             glPushMatrix();
8789 
8790             glTranslatef( vp->pix_width / 2, vp->pix_height/2, 0 );
8791             glScalef( vp->view_scale_ppm, -vp->view_scale_ppm, 0 );
8792             glTranslatef( -rzRules->sm_transform_parms->easting_vp_center, -rzRules->sm_transform_parms->northing_vp_center, 0 );
8793             //  Next, the per-object transform
8794 
8795             float x_origin = rzRules->obj->x_origin;
8796 
8797             if(rzRules->obj->m_chart_context->chart) {          // not a PlugIn Chart
8798                 if( ( (int)rzRules->obj->auxParm3 == (int)PI_CHART_TYPE_CM93 )
8799                     || ( (int)rzRules->obj->auxParm3 == (int)PI_CHART_TYPE_CM93COMP ) )
8800                 {
8801                     //      We may need to translate object coordinates by 360 degrees to conform.
8802                     if( BBView.GetMaxLon() >= 180. ) {
8803                         if(rzRules->obj->BBObj.GetMinLon() < BBView.GetMaxLon() - 360.)
8804                             x_origin += (float)(mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI);
8805                     }
8806                     else
8807                     if( (BBView.GetMinLon() <= -180. && rzRules->obj->BBObj.GetMaxLon() > BBView.GetMinLon() + 360.)
8808                     || (rzRules->obj->BBObj.GetMaxLon() > 180 && BBView.GetMinLon() + 360 < rzRules->obj->BBObj.GetMaxLon() )
8809                     )
8810                     x_origin -= (float)(mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI);
8811                 }
8812             }
8813 
8814             glTranslatef( x_origin, rzRules->obj->y_origin, 0);
8815             glScalef( rzRules->obj->x_rate, rzRules->obj->y_rate, 0 );
8816         }
8817 #endif
8818 
8819         // perform deferred tesselation
8820         if( !rzRules->obj->pPolyTessGeo->IsOk() ){
8821             rzRules->obj->pPolyTessGeo->BuildDeferredTess();
8822         }
8823 
8824         //  Get the vertex data
8825         PolyTriGroup *ppg_vbo = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
8826 
8827             //  Has the input vertex buffer been converted to "single_alloc float" model?
8828             //  and is it allowed?
8829         if(!ppg_vbo->bsingle_alloc && (rzRules->obj->auxParm1 >= 0) ){
8830 
8831                 int data_size = sizeof(float);
8832 
8833                 //  First calculate the required total byte size
8834                     int total_byte_size = 0;
8835                     TriPrim *p_tp = ppg_vbo->tri_prim_head;
8836                     while( p_tp ) {
8837                         total_byte_size += p_tp->nVert * 2 * data_size;
8838                         p_tp = p_tp->p_next; // pick up the next in chain
8839                     }
8840 
8841                     float *vbuf = (float *)malloc(total_byte_size);
8842                     p_tp = ppg_vbo->tri_prim_head;
8843 
8844                     if( ppg_vbo->data_type == DATA_TYPE_DOUBLE){  //DOUBLE to FLOAT
8845                             float *p_run = vbuf;
8846                             while( p_tp ) {
8847                                 float *pfbuf = p_run;
8848                                 for( int i=0 ; i < p_tp->nVert * 2 ; ++i){
8849                                     float x = (float)(p_tp->p_vertex[i]);
8850                                     *p_run++ = x;
8851                                 }
8852 
8853                                 free(p_tp->p_vertex);
8854                                 p_tp->p_vertex = (double *)pfbuf;
8855 
8856                                 p_tp = p_tp->p_next; // pick up the next in chain
8857                             }
8858                     }
8859                     else {          // FLOAT to FLOAT
8860                             float *p_run = vbuf;
8861                             while( p_tp ) {
8862                                 memcpy( p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float) );
8863 
8864                                 free(p_tp->p_vertex);
8865                                 p_tp->p_vertex = (double *)p_run;
8866 
8867                                 p_run += p_tp->nVert * 2 * sizeof(float);
8868 
8869                                 p_tp = p_tp->p_next; // pick up the next in chain
8870                             }
8871                     }
8872 
8873 
8874                     ppg_vbo->bsingle_alloc = true;
8875                     ppg_vbo->single_buffer = (unsigned char *)vbuf;
8876                     ppg_vbo->single_buffer_size = total_byte_size;
8877                     ppg_vbo->data_type = DATA_TYPE_FLOAT;
8878 
8879         }
8880 
8881 
8882         if( b_useVBO ){
8883         //  Has a VBO been built for this object?
8884             if( 1 ) {
8885                 glGetError(); // clear it
8886 
8887                  if(rzRules->obj->auxParm0 <= 0) {
8888 #ifdef xUSE_ANDROID_GLES2
8889                   if(ppg_vbo->data_type != DATA_TYPE_SHORT){
8890                     // We convert the vertex data from FLOAT to GL_SHORT to make the VBO smaller, but still keeping enough precision
8891                     //  This requires a scale factor to reduce the range from existing  data to +/- 32K
8892 
8893                     size_t np =  ppg_vbo->single_buffer_size / (2 * sizeof(float));
8894                     np --;
8895 
8896                     PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
8897                     TriPrim *p_tp = ppg->tri_prim_head;
8898 
8899                     size_t npp = 0;
8900                     while( p_tp ) {
8901                         npp += p_tp->nVert;
8902                         p_tp = (TriPrim *)p_tp->p_next;
8903                     }
8904 
8905                     //  Get the data range
8906                     float * pRun = (float *)ppg_vbo->single_buffer;
8907                     float north_max = -1e8;
8908                     float north_min = 1e8;
8909                     float east_max = -1e8;
8910                     float east_min = 1e8;
8911 
8912                     for( size_t i = 0 ; i < np ; i++){
8913                         float east = *pRun++;
8914                         float north = *pRun++;
8915                         north_max = wxMax(north, north_max);
8916                         north_min = wxMin(north, north_min);
8917                         east_max = wxMax(east, east_max);
8918                         east_min = wxMin(east, east_min);
8919                     }
8920 
8921                     float cfactx = wxMax(fabs(east_max), fabs(east_min));
8922                     float cfacty = wxMax(fabs(north_max), fabs(north_min));
8923                     float cfact = wxMax(cfactx, cfacty);
8924 
8925                     float sfact = cfact / 32700.0;
8926 
8927                     sfact = wxMax(sfact, 1.0);
8928 
8929                     //  Copy/convert the data
8930                     unsigned char *new_buf = (unsigned char *)malloc(np * 2 * sizeof(short));
8931                     pRun = (float *)ppg_vbo->single_buffer;
8932                     short *pd = (short *)new_buf;
8933                     for( size_t i = 0 ; i < np ; i++){
8934                         float east = *pRun++;
8935                         float north = *pRun++;
8936  //                       short a = (east / sfact);
8937  //                       short b = (north / sfact);
8938                         *pd++ = (east / sfact);
8939                         *pd++ = (north / sfact);
8940 
8941                     }
8942 
8943                     // replace the buffer
8944                     free(ppg_vbo->single_buffer);
8945                     ppg_vbo->single_buffer = new_buf;
8946                     ppg_vbo->single_buffer_size /= 2;
8947 
8948                     // Record the scale/offset factors
8949                     ppg_vbo->sfactor = sfact;
8950                     ppg_vbo->soffset = 0.;
8951 
8952                     ppg_vbo->data_type = DATA_TYPE_SHORT;
8953 
8954                   }
8955 
8956 
8957 
8958 #endif
8959                     b_temp_vbo = (rzRules->obj->auxParm0 == -5);   // Must we use a temporary VBO?  Probably slower than simple glDrawArrays
8960 
8961                     GLuint vboId = 0;
8962                     // generate a new VBO and get the associated ID
8963                     glGenBuffers(1, &vboId);
8964 
8965                     rzRules->obj->auxParm0 = vboId;
8966 
8967                     // bind VBO in order to use
8968                     glBindBuffer(GL_ARRAY_BUFFER, vboId);
8969                     GLenum err = glGetError();
8970                     if(err){
8971                         wxString msg;
8972                         msg.Printf(_T("VBO Error A: %d"), err);
8973                         wxLogMessage(msg);
8974                         return 0;
8975                     }
8976 
8977                     // upload data to VBO
8978 #ifndef USE_ANDROID_GLES2
8979                     glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
8980 #endif
8981                     glBufferData(GL_ARRAY_BUFFER,
8982                                     ppg_vbo->single_buffer_size, ppg_vbo->single_buffer, GL_STATIC_DRAW);
8983                     err = glGetError();
8984                     if(err){
8985                         wxString msg;
8986                         msg.Printf(_T("VBO Error B: %d"), err);
8987                         wxLogMessage(msg);
8988                         return 0;
8989                     }
8990 
8991                 }
8992                 else {
8993                     glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0);
8994                     GLenum err = glGetError();
8995                     if(err){
8996                         wxString msg;
8997                         msg.Printf(_T("VBO Error C: %d"), err);
8998                         wxLogMessage(msg);
8999                         return 0;
9000                     }
9001 
9002 #ifndef USE_ANDROID_GLES2
9003                     glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
9004 #endif
9005                 }
9006              }
9007         }
9008 
9009 
9010 
9011         PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
9012 
9013         TriPrim *p_tp = ppg->tri_prim_head;
9014         GLintptr vbo_offset = 0;
9015 
9016 #ifndef USE_ANDROID_GLES2
9017         glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
9018 #endif
9019         //      Set up the stride sizes for the array
9020         int array_data_size = sizeof(float);
9021         GLint array_gl_type = GL_FLOAT;
9022 
9023         if(ppg->data_type == DATA_TYPE_DOUBLE){
9024             array_data_size = sizeof(double);
9025             array_gl_type = GL_DOUBLE;
9026         }
9027 
9028         if(ppg->data_type == DATA_TYPE_SHORT){
9029             array_data_size = sizeof(short);
9030             array_gl_type = GL_SHORT;
9031         }
9032 
9033 #ifdef USE_ANDROID_GLES2
9034         glUseProgram(S52color_tri_shader_program);
9035 
9036                 // Disable VBO's (vertex buffer objects) for attributes.
9037         if(!b_useVBO)
9038             glBindBuffer( GL_ARRAY_BUFFER, 0 );
9039         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
9040 
9041         GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
9042         glEnableVertexAttribArray(pos);
9043 
9044         float angle = 0;
9045 
9046         // Build Transform matrix
9047         mat4x4 I, Q;
9048         mat4x4_identity(I);
9049 
9050 
9051         // Scale
9052         I[0][0] *= rzRules->obj->x_rate;
9053         I[1][1] *= rzRules->obj->y_rate;
9054 
9055         // Translate
9056         float x_origin = rzRules->obj->x_origin;
9057 
9058         if(rzRules->obj->m_chart_context->chart) {          // not a PlugIn Chart
9059                 if( ( (int)rzRules->obj->m_chart_context->chart->GetChartType() == (int)PI_CHART_TYPE_CM93 )
9060                     || ( (int)rzRules->obj->m_chart_context->chart->GetChartType() == (int)PI_CHART_TYPE_CM93COMP ) )
9061                 {
9062                     //      We may need to translate object coordinates by 360 degrees to conform.
9063                     if( BBView.GetMaxLon() >= 180. ) {
9064                         if(rzRules->obj->BBObj.GetMinLon() < BBView.GetMaxLon() - 360.)
9065                             x_origin += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
9066                     }
9067                     else
9068                         if( (BBView.GetMinLon() <= -180. && rzRules->obj->BBObj.GetMaxLon() > BBView.GetMinLon() + 360.)
9069                             || (rzRules->obj->BBObj.GetMaxLon() > 180 && BBView.GetMinLon() + 360 < rzRules->obj->BBObj.GetMaxLon() )
9070                         )
9071                             x_origin -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
9072                 }
9073         }
9074 
9075         I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - x_origin) * vp->view_scale_ppm ;
9076         I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center - rzRules->obj->y_origin) * -vp->view_scale_ppm ;
9077 
9078         // Scale
9079         I[0][0] *= vp->view_scale_ppm * ppg->sfactor;
9080         I[1][1] *= -vp->view_scale_ppm * ppg->sfactor;
9081 
9082         //Rotate
9083         mat4x4_rotate_Z(Q, I, angle);
9084 
9085         // Translate
9086         Q[3][0] += vp->pix_width / 2;
9087         Q[3][1] += vp->pix_height / 2;
9088 
9089         GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"TransformMatrix");
9090         glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
9091 
9092 
9093         float colorv[4];
9094         colorv[0] = c->R / float(256);
9095         colorv[1] = c->G / float(256);
9096         colorv[2] = c->B / float(256);
9097         colorv[3] = 1.0;
9098 
9099         GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
9100         glUniform4fv(colloc, 1, colorv);
9101 
9102 
9103 #endif
9104         while( p_tp ) {
9105             LLBBox box;
9106             if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
9107                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
9108                 box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
9109             }
9110             else
9111                 box = p_tp->tri_box;
9112 
9113             if(!BBView.IntersectOut(box)) {
9114 #ifdef USE_ANDROID_GLES2
9115 
9116                 if(b_useVBO) {
9117                     glVertexAttribPointer(pos, 2, array_gl_type, GL_FALSE, 0, (GLvoid *)(vbo_offset));
9118                     glDrawArrays(p_tp->type, 0, p_tp->nVert);
9119                 }
9120                 else {
9121                     float *bufOffset = (float *)(&ppg->single_buffer[vbo_offset]);
9122                     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, bufOffset);
9123                     glDrawArrays(p_tp->type, 0, p_tp->nVert);
9124                 }
9125 
9126 
9127 #else
9128                 if(b_useVBO) {
9129                     glVertexPointer(2, array_gl_type, 2 * array_data_size, (GLvoid *)(vbo_offset));
9130                     if(vbo_offset + p_tp->nVert * 2 * array_data_size <= ppg_vbo->single_buffer_size)
9131                         glDrawArrays(p_tp->type, 0, p_tp->nVert);
9132                 }
9133                 else {
9134                     if(vp->m_projection_type == PROJECTION_MERCATOR) {
9135                         glVertexPointer(2, array_gl_type, 2 * array_data_size, p_tp->p_vertex);
9136                         glDrawArrays(p_tp->type, 0, p_tp->nVert);
9137                     } else {
9138                         // temporary slow hack
9139                         glDisableClientState(GL_VERTEX_ARRAY);
9140 
9141                         glBegin(p_tp->type);
9142                         float *pvert_list = (float *)p_tp->p_vertex;
9143                         for(int i=0; i<p_tp->nVert; i++) {
9144                             float lon = *pvert_list++;
9145                             float lat = *pvert_list++;
9146                             wxPoint r;
9147                             GetPointPixSingle(rzRules, lat, lon, &r, vp);
9148 
9149                             if(r.x != INVALID_COORD)
9150                                 glVertex2i(r.x, r.y);
9151                             else if(p_tp->type != GL_TRIANGLE_FAN) {
9152                                 glEnd();
9153                                 glBegin(p_tp->type);
9154                                 if(p_tp->type == GL_TRIANGLES)
9155                                     while(i%3 < 2) i++;
9156                             }
9157                         }
9158                         glEnd();
9159 
9160                     }
9161                 }
9162 #endif
9163             }
9164 
9165             vbo_offset += p_tp->nVert * 2 * array_data_size;
9166 
9167             // pick up the next in chain
9168             if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
9169                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
9170                 p_tp = (TriPrim *)p_ltp->p_next;
9171             }
9172             else
9173                 p_tp = p_tp->p_next;
9174 
9175         } // while
9176 
9177         if(b_useVBO)
9178             glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
9179 
9180 
9181 #ifndef USE_ANDROID_GLES2
9182         glDisableClientState(GL_VERTEX_ARRAY);            // deactivate vertex array
9183 
9184         if(b_transform)
9185             glPopMatrix();
9186 #else
9187         mat4x4 IM;
9188         mat4x4_identity(IM);
9189         GLint matlocf = glGetUniformLocation(S52color_tri_shader_program,"TransformMatrix");
9190         glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
9191 #endif
9192 
9193 
9194         if( b_useVBO && b_temp_vbo){
9195             glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STATIC_DRAW);
9196             glDeleteBuffers(1, (unsigned int *)&rzRules->obj->auxParm0);
9197             rzRules->obj->auxParm0 = 0;
9198         }
9199     } // if pPolyTessGeo
9200 
9201 
9202 #endif          //#ifdef ocpnUSE_GL
9203 
9204     return 1;
9205 }
9206 
9207 void s52plib::SetGLClipRect(const ViewPort &vp, const wxRect &rect)
9208 {
9209 #ifndef USE_ANDROID_GLES2
9210     bool b_clear = false;
9211     bool s_b_useStencil = m_useStencil;
9212 
9213     #if 0
9214     /* for some reason this causes an occasional bug in depth mode, I cannot
9215      *       seem to solve it yet, so for now: */
9216     if(s_b_useStencil && s_b_useScissorTest) {
9217         wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
9218         if(rect != vp_rect) {
9219             glEnable(GL_SCISSOR_TEST);
9220             glScissor(rect.x, cc1->m_canvas_height-rect.height-rect.y, rect.width, rect.height);
9221 }
9222 
9223 if(b_clear) {
9224     glBegin(GL_QUADS);
9225     glVertex2i( rect.x, rect.y );
9226     glVertex2i( rect.x + rect.width, rect.y );
9227     glVertex2i( rect.x + rect.width, rect.y + rect.height );
9228     glVertex2i( rect.x, rect.y + rect.height );
9229     glEnd();
9230 }
9231 
9232 /* the code in s52plib depends on the depth buffer being
9233  *           initialized to this value, this code should go there instead and
9234  *           only a flag set here. */
9235 if(!s_b_useStencil) {
9236     glClearDepth( 0.25 );
9237     glDepthMask( GL_TRUE );    // to allow writes to the depth buffer
9238     glClear( GL_DEPTH_BUFFER_BIT );
9239     glDepthMask( GL_FALSE );
9240     glClearDepth( 1 ); // set back to default of 1
9241     glDepthFunc( GL_GREATER );                          // Set the test value
9242 }
9243 return;
9244 }
9245 #endif
9246 // slower way if there is no scissor support
9247     if(!b_clear)
9248         glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );   // disable color buffer
9249 
9250     if( s_b_useStencil ) {
9251         //    Create a stencil buffer for clipping to the region
9252         glEnable( GL_STENCIL_TEST );
9253         glStencilMask( 0x1 );                 // write only into bit 0 of the stencil buffer
9254         glClear( GL_STENCIL_BUFFER_BIT );
9255 
9256         //    We are going to write "1" into the stencil buffer wherever the region is valid
9257         glStencilFunc( GL_ALWAYS, 1, 1 );
9258         glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
9259     } else              //  Use depth buffer for clipping
9260     {
9261         glEnable( GL_DEPTH_TEST ); // to enable writing to the depth buffer
9262         glDepthFunc( GL_ALWAYS );  // to ensure everything you draw passes
9263         glDepthMask( GL_TRUE );    // to allow writes to the depth buffer
9264 
9265         glClear( GL_DEPTH_BUFFER_BIT ); // for a fresh start
9266 
9267         //    Decompose the region into rectangles, and draw as quads
9268         //    With z = 1
9269         // dep buffer clear = 1
9270         // 1 makes 0 in dep buffer, works
9271         // 0 make .5 in depth buffer
9272         // -1 makes 1 in dep buffer
9273 
9274         //    Depth buffer runs from 0 at z = 1 to 1 at z = -1
9275         //    Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
9276         //    Subsequent drawing at z=0 (depth = 0.5) will pass if using glDepthFunc(GL_GREATER);
9277         glTranslatef( 0, 0, .5 );
9278     }
9279 
9280     glBegin(GL_QUADS);
9281     glVertex2i( rect.x, rect.y );
9282     glVertex2i( rect.x + rect.width, rect.y );
9283     glVertex2i( rect.x + rect.width, rect.y + rect.height );
9284     glVertex2i( rect.x, rect.y + rect.height );
9285     glEnd();
9286 
9287     if( s_b_useStencil ) {
9288         //    Now set the stencil ops to subsequently render only where the stencil bit is "1"
9289         glStencilFunc( GL_EQUAL, 1, 1 );
9290         glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
9291     } else {
9292         glDepthFunc( GL_GREATER );                          // Set the test value
9293         glDepthMask( GL_FALSE );                            // disable depth buffer
9294         glTranslatef( 0, 0, -.5 ); // reset translation
9295     }
9296 
9297     if(!b_clear)
9298         glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );  // re-enable color buffer
9299 #endif
9300 }
9301 
9302 void RotateToViewPort(const ViewPort &vp)
9303 {
9304 #ifndef USE_ANDROID_GLES2
9305     bool g_bskew_comp = true;
9306 
9307     float angle = vp.rotation;
9308     if(g_bskew_comp)
9309         angle -= vp.skew;
9310 
9311     if( fabs( angle ) > 0.0001 )
9312     {
9313         //    Rotations occur around 0,0, so translate to rotate around screen center
9314         float xt = vp.pix_width / 2.0, yt = vp.pix_height / 2.0;
9315 
9316         glTranslatef( xt, yt, 0 );
9317         glRotatef( angle * 180. / PI, 0, 0, 1 );
9318         glTranslatef( -xt, -yt, 0 );
9319     }
9320 #endif
9321 
9322 }
9323 
9324 
9325 
9326 int s52plib::RenderToGLAP( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
9327 {
9328 #ifdef USE_ANDROID_GLES2
9329     return RenderToGLAP_GLSL( rzRules, rules, vp);
9330 #endif
9331 
9332 #ifdef ocpnUSE_GL
9333     if( rules->razRule == NULL )
9334         return 0;
9335 
9336     int obj_xmin = 10000;
9337     int obj_xmax = -10000;
9338     int obj_ymin = 10000;
9339     int obj_ymax = -10000;
9340 
9341     double z_clip_geom = 1.0;
9342     double z_tex_geom = 0.;
9343 
9344     LLBBox BBView = vp->GetBBox();
9345 
9346     wxPoint *ptp;
9347     if( rzRules->obj->pPolyTessGeo ) {
9348         if( !rzRules->obj->pPolyTessGeo->IsOk() ){ // perform deferred tesselation
9349             rzRules->obj->pPolyTessGeo->BuildDeferredTess();
9350         }
9351 
9352         ptp = (wxPoint *) malloc(
9353                 ( rzRules->obj->pPolyTessGeo->GetnVertexMax() + 1 ) * sizeof(wxPoint) );
9354     } else
9355         return 0;
9356 
9357     if( m_useStencilAP ) {
9358         glPushAttrib( GL_STENCIL_BUFFER_BIT );          // See comment below
9359         //    Use masked bit "1" of the stencil buffer to create a stencil for the area of interest
9360 
9361         glEnable( GL_STENCIL_TEST );
9362         glStencilMask( 0x2 ); // write only into bit 1 of the stencil buffer
9363         glColorMask( false, false, false, false ); // Disable writing to the color buffer
9364         glClear( GL_STENCIL_BUFFER_BIT );
9365 
9366         //    We are going to write "2" into the stencil buffer wherever the object is valid
9367         glStencilFunc( GL_ALWAYS, 2, 2 );
9368         glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
9369 
9370     } else {
9371         glEnable( GL_DEPTH_TEST ); // to use the depth test
9372         glDepthFunc( GL_GREATER ); // Respect global render mask in depth buffer
9373         glDepthMask( GL_TRUE ); // to allow writes to the depth buffer
9374         glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); // disable color buffer
9375 
9376         glColor3f( 1, 1, 0 );
9377 
9378         //  If we are using stencil for overall clipping, then we are only
9379         //  using depth buffer for AreaPattern rendering
9380         //  So, each AP render can start with a clear depth buffer
9381 
9382         if(m_useStencil){
9383              glClearDepth(.26);
9384              glClear( GL_DEPTH_BUFFER_BIT ); // for a fresh start
9385         }
9386             //    Overall chart clip buffer was set at z=0.5
9387         //    Draw this clip geometry at z = .25, so still respecting the previously established clip region
9388         //    Subsequent drawing to this area at z=.25  will pass only this area if using glDepthFunc(GL_EQUAL);
9389 
9390         z_clip_geom = .25;
9391         z_tex_geom = .25;
9392     }
9393 
9394         PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
9395 
9396         TriPrim *p_tp = ppg->tri_prim_head;
9397         while( p_tp ) {
9398             LLBBox box;
9399             if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
9400                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
9401                 box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
9402             }
9403             else
9404                 box = p_tp->tri_box;
9405 
9406             if(!BBView.IntersectOut(box)) {
9407                 //      Get and convert the points
9408 
9409                 wxPoint *pr = ptp;
9410                 if( ppg->data_type == DATA_TYPE_FLOAT ){
9411                     float *pvert_list = (float *)p_tp->p_vertex;
9412 
9413                     for( int iv = 0; iv < p_tp->nVert; iv++ ) {
9414                         float lon = *pvert_list++;
9415                         float lat = *pvert_list++;
9416                         GetPointPixSingle(rzRules, lat, lon, pr, vp );
9417 
9418                         obj_xmin = wxMin(obj_xmin, pr->x);
9419                         obj_xmax = wxMax(obj_xmax, pr->x);
9420                         obj_ymin = wxMin(obj_ymin, pr->y);
9421                         obj_ymax = wxMax(obj_ymax, pr->y);
9422 
9423                         pr++;
9424                     }
9425                 }
9426                 else {
9427                     double *pvert_list = p_tp->p_vertex;
9428 
9429                     for( int iv = 0; iv < p_tp->nVert; iv++ ) {
9430                         double lon = *pvert_list++;
9431                         double lat = *pvert_list++;
9432                         GetPointPixSingle(rzRules, lat, lon, pr, vp );
9433 
9434                         obj_xmin = wxMin(obj_xmin, pr->x);
9435                         obj_xmax = wxMax(obj_xmax, pr->x);
9436                         obj_ymin = wxMin(obj_ymin, pr->y);
9437                         obj_ymax = wxMax(obj_ymax, pr->y);
9438 
9439                         pr++;
9440                     }
9441                 }
9442 
9443 
9444                 switch( p_tp->type ){
9445                     case PTG_TRIANGLE_FAN: {
9446                         glBegin( GL_TRIANGLE_FAN );
9447                         for( int it = 0; it < p_tp->nVert; it++ )
9448                             glVertex3f( ptp[it].x, ptp[it].y, z_clip_geom );
9449                         glEnd();
9450                         break;
9451                     }
9452 
9453                     case PTG_TRIANGLE_STRIP: {
9454                         glBegin( GL_TRIANGLE_STRIP );
9455                         for( int it = 0; it < p_tp->nVert; it++ )
9456                             glVertex3f( ptp[it].x, ptp[it].y, z_clip_geom );
9457                         glEnd();
9458                         break;
9459                     }
9460                     case PTG_TRIANGLES: {
9461                         glBegin( GL_TRIANGLES );
9462                         for( int it = 0; it < p_tp->nVert; it += 3 ) {
9463                             int xmin = wxMin(ptp[it].x, wxMin(ptp[it+1].x, ptp[it+2].x));
9464                             int xmax = wxMax(ptp[it].x, wxMax(ptp[it+1].x, ptp[it+2].x));
9465                             int ymin = wxMin(ptp[it].y, wxMin(ptp[it+1].y, ptp[it+2].y));
9466                             int ymax = wxMax(ptp[it].y, wxMax(ptp[it+1].y, ptp[it+2].y));
9467 
9468                             wxRect rect( xmin, ymin, xmax - xmin, ymax - ymin );
9469                             //if( rect.Intersects( m_render_rect ) )
9470                             {
9471                                 glVertex3f( ptp[it].x, ptp[it].y, z_clip_geom );
9472                                 glVertex3f( ptp[it + 1].x, ptp[it + 1].y, z_clip_geom );
9473                                 glVertex3f( ptp[it + 2].x, ptp[it + 2].y, z_clip_geom );
9474                             }
9475                         }
9476                         glEnd();
9477                         break;
9478                     }
9479                 }
9480             } // if bbox
9481 
9482             // pick up the next in chain
9483             if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
9484                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
9485                 p_tp = (TriPrim *)p_ltp->p_next;
9486             }
9487             else
9488                 p_tp = p_tp->p_next;
9489 
9490         } // while
9491 
9492 //        obj_xmin = 0;
9493 //        obj_xmax = 2000;
9494 
9495         glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); // re-enable color buffer
9496 
9497         if( m_useStencilAP ) {
9498             //    Now set the stencil ops to subsequently render only where the stencil bit is "2"
9499             glStencilFunc( GL_EQUAL, 2, 2 );
9500             glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
9501         } else {
9502             glDepthFunc( GL_EQUAL ); // Set the test value
9503             glDepthMask( GL_FALSE ); // disable depth buffer
9504         }
9505         //    Get the pattern definition
9506         if( ( rules->razRule->pixelPtr == NULL ) || ( rules->razRule->parm1 != m_colortable_index )
9507                 || ( rules->razRule->parm0 != ID_GL_PATT_SPEC ) ) {
9508             render_canvas_parms *patt_spec = CreatePatternBufferSpec( rzRules, rules, vp, false,
9509                     true );
9510 
9511             ClearRulesCache( rules->razRule ); //  Clear out any existing cached symbology
9512 
9513             rules->razRule->pixelPtr = patt_spec;
9514             rules->razRule->parm1 = m_colortable_index;
9515             rules->razRule->parm0 = ID_GL_PATT_SPEC;
9516         }
9517 
9518         //  Render the Area using the pattern spec stored in the rules
9519         render_canvas_parms *ppatt_spec = (render_canvas_parms *) rules->razRule->pixelPtr;
9520 
9521         //    Has the pattern been uploaded as a texture?
9522         if( !ppatt_spec->OGL_tex_name ) {
9523             GLuint tex_name;
9524             glGenTextures( 1, &tex_name );
9525             ppatt_spec->OGL_tex_name = tex_name;
9526 
9527             glBindTexture( GL_TEXTURE_2D, tex_name );
9528 
9529             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
9530             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
9531 
9532             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
9533             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
9534 
9535             glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, ppatt_spec->w_pot, ppatt_spec->h_pot, 0,
9536                           GL_RGBA, GL_UNSIGNED_BYTE, ppatt_spec->pix_buff );
9537         }
9538 
9539         glEnable( GL_TEXTURE_2D );
9540         glBindTexture( GL_TEXTURE_2D, ppatt_spec->OGL_tex_name );
9541 
9542         glEnable( GL_BLEND );
9543         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
9544 
9545         int h = ppatt_spec->height;
9546         int w = ppatt_spec->width;
9547         int xr = obj_xmin;
9548         int yr = obj_ymin;
9549 
9550         float ww = (float) ppatt_spec->width / (float) ppatt_spec->w_pot;
9551         float hh = (float) ppatt_spec->height / (float) ppatt_spec->h_pot;
9552         float x_stagger_off = 0;
9553         if( ppatt_spec->b_stagger ) x_stagger_off = (float) ppatt_spec->width / 2;
9554         int yc = 0;
9555 
9556 
9557         if(w>0 && h>0) {
9558             while( yr < vp->pix_height ) {
9559                 if( ( (yr + h) >= 0 ) && ( yr <= obj_ymax ) )  {
9560                     xr = obj_xmin;   //reset
9561                     while( xr < vp->pix_width ) {
9562 
9563                         int xp = xr;
9564                         if( yc & 1 ) xp += x_stagger_off;
9565 
9566                     //    Render a quad.
9567                         if( ( (xr + w) >= 0 ) && ( xr <= obj_xmax ) ){
9568                             glBegin( GL_QUADS );
9569                             glTexCoord2f( 0, 0 );
9570                             glVertex3f( xp, yr, z_tex_geom );
9571                             glTexCoord2f( ww, 0 );
9572                             glVertex3f( xp + w, yr, z_tex_geom );
9573                             glTexCoord2f( ww, hh );
9574                             glVertex3f( xp + w, yr + h, z_tex_geom );
9575                             glTexCoord2f( 0, hh );
9576                             glVertex3f( xp, yr + h, z_tex_geom );
9577                             glEnd();
9578                         }
9579                         xr += ppatt_spec->width;
9580                     }
9581                 }
9582                 yr += ppatt_spec->height;
9583                 yc++;
9584             }
9585         }
9586 
9587         glDisable( GL_TEXTURE_2D );
9588         glDisable( GL_BLEND );
9589 
9590         //    Restore the previous state
9591 
9592          if( m_useStencilAP ){
9593              //  Theoretically, it should be sufficient to simply reset the StencilFunc()...
9594              //  But I found one platform where this does not work, and we need to save and restore
9595              //  the entire STENCIL state.  I suspect bad GL drivers here, but we do what must needs...
9596              //glStencilFunc( GL_EQUAL, 1, 1 );
9597 
9598              glPopAttrib();
9599          }
9600          else {
9601              // restore clipping region
9602              glPopMatrix();
9603              SetGLClipRect( *vp, m_last_clip_rect);
9604 
9605              glPushMatrix();
9606              RotateToViewPort(*vp);
9607              glDisable( GL_DEPTH_TEST );
9608 
9609          }
9610 
9611 
9612     free( ptp );
9613 #endif                  //#ifdef ocpnUSE_GL
9614 
9615     return 1;
9616 }
9617 
9618 int s52plib::RenderToGLAP_GLSL( ObjRazRules *rzRules, Rules *rules, ViewPort *vp )
9619 {
9620 
9621 #ifdef USE_ANDROID_GLES2
9622 
9623     //    Get the pattern definition
9624     if( ( rules->razRule->pixelPtr == NULL ) || ( rules->razRule->parm1 != m_colortable_index )
9625         || ( rules->razRule->parm0 != ID_GL_PATT_SPEC ) ) {
9626 
9627         render_canvas_parms *patt_spec = CreatePatternBufferSpec( rzRules, rules, vp, false, true );
9628 
9629         ClearRulesCache( rules->razRule ); //  Clear out any existing cached symbology
9630 
9631         rules->razRule->pixelPtr = patt_spec;
9632         rules->razRule->parm1 = m_colortable_index;
9633         rules->razRule->parm0 = ID_GL_PATT_SPEC;
9634     }
9635 
9636     //  Render the Area using the pattern spec stored in the rules
9637     render_canvas_parms *ppatt_spec = (render_canvas_parms *) rules->razRule->pixelPtr;
9638 
9639     //    Has the pattern been uploaded as a texture?
9640     if( !ppatt_spec->OGL_tex_name ) {
9641         GLuint tex_name;
9642         glGenTextures( 1, &tex_name );
9643         ppatt_spec->OGL_tex_name = tex_name;
9644 
9645         glBindTexture( GL_TEXTURE_2D, tex_name );
9646 
9647         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
9648         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
9649 
9650         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
9651         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
9652 
9653         glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, ppatt_spec->w_pot, ppatt_spec->h_pot, 0,
9654                       GL_RGBA, GL_UNSIGNED_BYTE, ppatt_spec->pix_buff );
9655     }
9656 
9657     glEnable( GL_TEXTURE_2D );
9658     glBindTexture( GL_TEXTURE_2D, ppatt_spec->OGL_tex_name );
9659 
9660     glEnable( GL_BLEND );
9661     //glBlendFunc(GL_SRC_COLOR, GL_ZERO);
9662 
9663     int textureWidth = ppatt_spec->width;    //ppatt_spec->w_pot;
9664     int textureHeight = ppatt_spec->height;  //ppatt_spec->h_pot;
9665 
9666     wxPoint pr;
9667     GetPointPixSingle(rzRules, rzRules->obj->m_lat, rzRules->obj->m_lat, &pr, vp );
9668     float xOff = pr.x;
9669     float yOff = pr.y;
9670 
9671 
9672     LLBBox BBView = vp->GetBBox();
9673     // please untangle this logic with the logic below
9674     if(BBView.GetMaxLon()+180 < vp->clon)
9675         BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() + 360,
9676                    BBView.GetMaxLat(), BBView.GetMaxLon() + 360);
9677         else if(BBView.GetMinLon()-180 > vp->clon)
9678             BBView.Set(BBView.GetMinLat(), BBView.GetMinLon() - 360,
9679                        BBView.GetMaxLat(), BBView.GetMaxLon() - 360);
9680 
9681             //  Allow a little slop in calculating whether a triangle
9682             //  is within the requested Viewport
9683             double margin = BBView.GetLonRange() * .05;
9684         BBView.EnLarge( margin );
9685 
9686     bool b_useVBO = m_useVBO  && !rzRules->obj->auxParm1 && vp->m_projection_type == PROJECTION_MERCATOR;
9687 
9688     if( rzRules->obj->pPolyTessGeo ) {
9689 
9690         bool b_temp_vbo = false;
9691         bool b_transform = false;
9692 
9693 
9694         // perform deferred tesselation
9695         if( !rzRules->obj->pPolyTessGeo->IsOk() )
9696             rzRules->obj->pPolyTessGeo->BuildDeferredTess();
9697 
9698         //  Get the vertex data
9699         PolyTriGroup *ppg_vbo = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
9700 
9701             //  Has the input vertex buffer been converted to "single_alloc float" model?
9702             //  and is it allowed?
9703         if(!ppg_vbo->bsingle_alloc && (rzRules->obj->auxParm1 >= 0) ){
9704 
9705                 int data_size = sizeof(float);
9706 
9707                 //  First calculate the required total byte size
9708                 int total_byte_size = 0;
9709                 TriPrim *p_tp = ppg_vbo->tri_prim_head;
9710                 while( p_tp ) {
9711                     total_byte_size += p_tp->nVert * 2 * data_size;
9712                     p_tp = p_tp->p_next; // pick up the next in chain
9713                 }
9714 
9715                 float *vbuf = (float *)malloc(total_byte_size);
9716                 p_tp = ppg_vbo->tri_prim_head;
9717 
9718                 if( ppg_vbo->data_type == DATA_TYPE_DOUBLE){  //DOUBLE to FLOAT
9719                             float *p_run = vbuf;
9720                             while( p_tp ) {
9721                                 float *pfbuf = p_run;
9722                                 for( int i=0 ; i < p_tp->nVert * 2 ; ++i){
9723                                     float x = (float)(p_tp->p_vertex[i]);
9724                                     *p_run++ = x;
9725                                 }
9726 
9727                                 free(p_tp->p_vertex);
9728                                 p_tp->p_vertex = (double *)pfbuf;
9729 
9730                                 p_tp = p_tp->p_next; // pick up the next in chain
9731                             }
9732                 }
9733                 else {          // FLOAT to FLOAT
9734                             float *p_run = vbuf;
9735                             while( p_tp ) {
9736                                 memcpy( p_run, p_tp->p_vertex, p_tp->nVert * 2 * sizeof(float) );
9737 
9738                                 free(p_tp->p_vertex);
9739                                 p_tp->p_vertex = (double *)p_run;
9740 
9741                                 p_run += p_tp->nVert * 2 * sizeof(float);
9742 
9743                                 p_tp = p_tp->p_next; // pick up the next in chain
9744                             }
9745                 }
9746 
9747 
9748                 ppg_vbo->bsingle_alloc = true;
9749                 ppg_vbo->single_buffer = (unsigned char *)vbuf;
9750                 ppg_vbo->single_buffer_size = total_byte_size;
9751                 ppg_vbo->data_type = DATA_TYPE_FLOAT;
9752 
9753         }
9754 
9755 
9756         if( b_useVBO ){
9757                //  Has a VBO been built for this object?
9758                 if( 1 ) {
9759 
9760                     if(rzRules->obj->auxParm0 <= 0) {
9761 #if 0
9762                         if(ppg_vbo->data_type != DATA_TYPE_SHORT){
9763                             // We convert the vertex data from FLOAT to GL_SHORT to make the VBO smaller, but still keeping enough precision
9764                             //  This requires a scale factor to reduce the range from existing  data to +/- 32K
9765 
9766                             size_t np =  ppg_vbo->single_buffer_size / (2 * sizeof(float));
9767                             np --;
9768 
9769                             PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
9770                             TriPrim *p_tp = ppg->tri_prim_head;
9771 
9772                             size_t npp = 0;
9773                             while( p_tp ) {
9774                                 npp += p_tp->nVert;
9775                                 p_tp = (TriPrim *)p_tp->p_next;
9776                             }
9777 
9778                             //  Get the data range
9779                             float * pRun = (float *)ppg_vbo->single_buffer;
9780                             float north_max = -1e8;
9781                             float north_min = 1e8;
9782                             float east_max = -1e8;
9783                             float east_min = 1e8;
9784 
9785                             for( size_t i = 0 ; i < np ; i++){
9786                                 float east = *pRun++;
9787                                 float north = *pRun++;
9788                                 north_max = wxMax(north, north_max);
9789                                 north_min = wxMin(north, north_min);
9790                                 east_max = wxMax(east, east_max);
9791                                 east_min = wxMin(east, east_min);
9792                             }
9793 
9794                             float cfactx = wxMax(fabs(east_max), fabs(east_min));
9795                             float cfacty = wxMax(fabs(north_max), fabs(north_min));
9796                             float cfact = wxMax(cfactx, cfacty);
9797 
9798                             float sfact = cfact / 32700.0;
9799 
9800                             sfact = wxMax(sfact, 1.0);
9801 
9802                             //  Copy/convert the data
9803                             unsigned char *new_buf = (unsigned char *)malloc(np * 2 * sizeof(short));
9804                             pRun = (float *)ppg_vbo->single_buffer;
9805                             short *pd = (short *)new_buf;
9806                             for( size_t i = 0 ; i < np ; i++){
9807                                 float east = *pRun++;
9808                                 float north = *pRun++;
9809                                 //                       short a = (east / sfact);
9810                                 //                       short b = (north / sfact);
9811                                 *pd++ = (east / sfact);
9812                                 *pd++ = (north / sfact);
9813 
9814                             }
9815 
9816                             // replace the buffer
9817                             free(ppg_vbo->single_buffer);
9818                             ppg_vbo->single_buffer = new_buf;
9819                             ppg_vbo->single_buffer_size /= 2;
9820 
9821                             // Record the scale/offset factors
9822                             ppg_vbo->sfactor = sfact;
9823                             ppg_vbo->soffset = 0.;
9824 
9825                             ppg_vbo->data_type = DATA_TYPE_SHORT;
9826 
9827                         }
9828 #endif
9829 
9830 
9831                         GLuint vboId;
9832                         // generate a new VBO and get the associated ID
9833                         glGenBuffers(1, &vboId);
9834 
9835                         rzRules->obj->auxParm0 = vboId;
9836 
9837                         // bind VBO in order to use
9838                         glBindBuffer(GL_ARRAY_BUFFER, vboId);
9839 
9840                         // upload data to VBO
9841 #ifndef USE_ANDROID_GLES2
9842                         glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
9843 #endif
9844                         glBufferData(GL_ARRAY_BUFFER, ppg_vbo->single_buffer_size, ppg_vbo->single_buffer, GL_STATIC_DRAW);
9845 
9846                     }
9847                     else {
9848                         glBindBuffer(GL_ARRAY_BUFFER, rzRules->obj->auxParm0);
9849 #ifndef USE_ANDROID_GLES2
9850                         glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
9851 #endif
9852                     }
9853                 }
9854         }
9855 
9856 
9857 
9858         PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
9859 
9860             TriPrim *p_tp = ppg->tri_prim_head;
9861             GLintptr vbo_offset = 0;
9862 
9863 #ifndef USE_ANDROID_GLES2
9864             glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex coords array
9865 #endif
9866             //      Set up the stride sizes for the array
9867             int array_data_size = sizeof(float);
9868             GLint array_gl_type = GL_FLOAT;
9869 
9870             if(ppg->data_type == DATA_TYPE_DOUBLE){
9871                 array_data_size = sizeof(double);
9872                 array_gl_type = GL_DOUBLE;
9873             }
9874 
9875             if(ppg->data_type == DATA_TYPE_SHORT){
9876                 array_data_size = sizeof(short);
9877                 array_gl_type = GL_SHORT;
9878             }
9879 
9880             GLint program = S52AP_shader_program;
9881             glUseProgram(program);
9882 
9883             // Disable VBO's (vertex buffer objects) for attributes.
9884             if(!b_useVBO)
9885                 glBindBuffer( GL_ARRAY_BUFFER, 0 );
9886             glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
9887 
9888             GLint pos = glGetAttribLocation(program, "position");
9889             glEnableVertexAttribArray(pos);
9890 
9891             // Select the active texture unit.
9892             glActiveTexture( GL_TEXTURE0 );
9893 
9894             // Bind our texture to the texturing target.
9895             glBindTexture( GL_TEXTURE_2D, ppatt_spec->OGL_tex_name );
9896 
9897             // Set up the texture sampler to texture unit 0
9898             GLint texUni = glGetUniformLocation( program, "uTex" );
9899             glUniform1i( texUni, 0 );
9900 
9901             GLint texWidth  = glGetUniformLocation( program, "texWidth" );
9902             GLint texHeight  = glGetUniformLocation( program, "texHeight" );
9903             glUniform1f(texWidth, textureWidth);
9904             glUniform1f(texHeight, textureHeight);
9905 
9906             GLint texPOTWidth  = glGetUniformLocation( program, "texPOTWidth" );
9907             GLint texPOTHeight  = glGetUniformLocation( program, "texPOTHeight" );
9908             glUniform1f(texPOTWidth, ppatt_spec->w_pot);
9909             glUniform1f(texPOTHeight, ppatt_spec->h_pot);
9910 
9911             GLint xo  = glGetUniformLocation( program, "xOff" );
9912             GLint yo  = glGetUniformLocation( program, "yOff" );
9913 
9914             glUniform1f(xo, fmod(xOff, ppatt_spec->w_pot));
9915             glUniform1f(yo, fmod(yOff, ppatt_spec->h_pot));
9916 
9917             GLint yom  = glGetUniformLocation( program, "yOffM" );
9918             glUniform1f(yom, yOff);
9919 
9920 
9921             if(ppatt_spec->b_stagger){
9922                 GLint staggerFact  = glGetUniformLocation( program, "staggerFactor" );
9923                 glUniform1f(staggerFact, 0.5);
9924             }
9925 
9926             float angle = 0;
9927 
9928             // Build Transform matrix
9929             mat4x4 I, Q;
9930             mat4x4_identity(I);
9931 
9932 
9933        // Scale
9934             I[0][0] *= rzRules->obj->x_rate;
9935             I[1][1] *= rzRules->obj->y_rate;
9936 
9937         // Translate
9938             I[3][0] = -(rzRules->sm_transform_parms->easting_vp_center - rzRules->obj->x_origin) * vp->view_scale_ppm ;
9939             I[3][1] = -(rzRules->sm_transform_parms->northing_vp_center - rzRules->obj->y_origin) * -vp->view_scale_ppm ;
9940 
9941         // Scale
9942             I[0][0] *= vp->view_scale_ppm * ppg->sfactor;
9943             I[1][1] *= -vp->view_scale_ppm * ppg->sfactor;
9944 
9945         //Rotate
9946             mat4x4_rotate_Z(Q, I, angle);
9947 
9948         // Translate
9949             Q[3][0] += vp->pix_width / 2;
9950             Q[3][1] += vp->pix_height / 2;
9951 
9952             GLint matloc = glGetUniformLocation(program,"TransformMatrix");
9953             glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
9954 
9955 
9956             if(!b_useVBO){
9957                 float *bufBase = (float *)(&ppg->single_buffer[vbo_offset]);
9958                 glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufBase);
9959             }
9960 
9961             while( p_tp ) {
9962                 LLBBox box;
9963                 if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
9964                 LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
9965                 box.Set(p_ltp->miny, p_ltp->minx, p_ltp->maxy, p_ltp->maxx);
9966                 }
9967                 else
9968                     box = p_tp->tri_box;
9969 
9970                 if(!BBView.IntersectOut(box)) {
9971 
9972 
9973 
9974                     if(b_useVBO) {
9975                         glVertexAttribPointer(pos, 2, array_gl_type, GL_FALSE, 0/*2*sizeof(array_gl_type)*/, (GLvoid *)(vbo_offset));
9976                         glDrawArrays(p_tp->type, 0, p_tp->nVert);
9977                     }
9978                     else {
9979                         glDrawArrays(p_tp->type, vbo_offset/(2 * array_data_size), p_tp->nVert);
9980                     }
9981 
9982 
9983 
9984                 }
9985 
9986                 vbo_offset += p_tp->nVert * 2 * array_data_size;
9987 
9988                 // pick up the next in chain
9989                 if(!rzRules->obj->m_chart_context->chart) {          // This is a PlugIn Chart
9990                     LegacyTriPrim *p_ltp = (LegacyTriPrim *)p_tp;
9991                     p_tp = (TriPrim *)p_ltp->p_next;
9992                 }
9993                 else
9994                     p_tp = p_tp->p_next;
9995 
9996             } // while
9997 
9998             if(b_useVBO)
9999                 glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
10000 
10001 #ifndef USE_ANDROID_GLES2
10002             glDisableClientState(GL_VERTEX_ARRAY);            // deactivate vertex array
10003 #endif
10004             //  Restore the transform matrix to identity
10005             mat4x4 IM;
10006             mat4x4_identity(IM);
10007             GLint matlocf = glGetUniformLocation(S52color_tri_shader_program,"TransformMatrix");
10008             glUniformMatrix4fv( matlocf, 1, GL_FALSE, (const GLfloat*)IM);
10009 
10010 
10011         } // if pPolyTessGeo
10012 
10013 
10014 #endif
10015         glDisable( GL_TEXTURE_2D );
10016 
10017         return 1;
10018 }
10019 
10020 
10021 void s52plib::RenderPolytessGL(ObjRazRules *rzRules, ViewPort *vp, double z_clip_geom, wxPoint *ptp)
10022 {
10023 #ifdef ocpnUSE_GL
10024 #ifndef USE_ANDROID_GLES2
10025 
10026     LLBBox BBView = vp->GetBBox();
10027 
10028     //  Allow a little slop in calculating whether a triangle
10029     //  is within the requested Viewport
10030     double margin = BBView.GetLonRange() * .05;
10031 
10032     int obj_xmin = 10000;
10033     int obj_xmax = -10000;
10034     int obj_ymin = 10000;
10035     int obj_ymax = -10000;
10036 
10037     PolyTriGroup *ppg = rzRules->obj->pPolyTessGeo->Get_PolyTriGroup_head();
10038 
10039     TriPrim *p_tp = ppg->tri_prim_head;
10040     while( p_tp ) {
10041         if(!BBView.IntersectOut( p_tp->tri_box)) {
10042             //      Get and convert the points
10043 
10044             wxPoint *pr = ptp;
10045             if( ppg->data_type == DATA_TYPE_FLOAT ){
10046                 float *pvert_list = (float *)p_tp->p_vertex;
10047 
10048                 for( int iv = 0; iv < p_tp->nVert; iv++ ) {
10049                     float lon = *pvert_list++;
10050                     float lat = *pvert_list++;
10051                     GetPointPixSingle(rzRules, lat, lon, pr, vp );
10052 
10053                     obj_xmin = wxMin(obj_xmin, pr->x);
10054                     obj_xmax = wxMax(obj_xmax, pr->x);
10055                     obj_ymin = wxMin(obj_ymin, pr->y);
10056                     obj_ymax = wxMax(obj_ymax, pr->y);
10057 
10058                     pr++;
10059                 }
10060             }
10061             else {
10062                 double *pvert_list = p_tp->p_vertex;
10063 
10064                 for( int iv = 0; iv < p_tp->nVert; iv++ ) {
10065                     double lon = *pvert_list++;
10066                     double lat = *pvert_list++;
10067                     GetPointPixSingle(rzRules, lat, lon, pr, vp );
10068 
10069                     obj_xmin = wxMin(obj_xmin, pr->x);
10070                     obj_xmax = wxMax(obj_xmax, pr->x);
10071                     obj_ymin = wxMin(obj_ymin, pr->y);
10072                     obj_ymax = wxMax(obj_ymax, pr->y);
10073 
10074                     pr++;
10075                 }
10076             }
10077 
10078 
10079 
10080             switch( p_tp->type ){
10081                 case PTG_TRIANGLE_FAN: {
10082                     glBegin( GL_TRIANGLE_FAN );
10083                     for( int it = 0; it < p_tp->nVert; it++ )
10084                         glVertex3f( ptp[it].x, ptp[it].y, z_clip_geom );
10085                     glEnd();
10086                     break;
10087                 }
10088 
10089                 case PTG_TRIANGLE_STRIP: {
10090                     glBegin( GL_TRIANGLE_STRIP );
10091                     for( int it = 0; it < p_tp->nVert; it++ )
10092                         glVertex3f( ptp[it].x, ptp[it].y, z_clip_geom );
10093                     glEnd();
10094                     break;
10095                 }
10096                 case PTG_TRIANGLES: {
10097                     glBegin( GL_TRIANGLES );
10098                     for( int it = 0; it < p_tp->nVert; it += 3 ) {
10099                         int xmin = wxMin(ptp[it].x, wxMin(ptp[it+1].x, ptp[it+2].x));
10100                         int xmax = wxMax(ptp[it].x, wxMax(ptp[it+1].x, ptp[it+2].x));
10101                         int ymin = wxMin(ptp[it].y, wxMin(ptp[it+1].y, ptp[it+2].y));
10102                         int ymax = wxMax(ptp[it].y, wxMax(ptp[it+1].y, ptp[it+2].y));
10103 
10104                         wxRect rect( xmin, ymin, xmax - xmin, ymax - ymin );
10105 //                        if( rect.Intersects( m_render_rect ) )
10106                         {
10107                             glVertex3f( ptp[it].x, ptp[it].y, z_clip_geom );
10108                             glVertex3f( ptp[it + 1].x, ptp[it + 1].y, z_clip_geom );
10109                             glVertex3f( ptp[it + 2].x, ptp[it + 2].y, z_clip_geom );
10110                         }
10111                     }
10112                     glEnd();
10113                     break;
10114                 }
10115             }
10116         } // if bbox
10117         p_tp = p_tp->p_next; // pick up the next in chain
10118     } // while
10119 
10120 #endif
10121 #endif
10122 }
10123 
10124 #ifdef ocpnUSE_GL
10125 
10126 int s52plib::RenderAreaToGL( const wxGLContext &glcc, ObjRazRules *rzRules, ViewPort *vp )
10127 {
10128     if( !ObjectRenderCheckRules( rzRules, vp, true ) )
10129         return 0;
10130 
10131     Rules *rules = rzRules->LUP->ruleList;
10132 
10133     while( rules != NULL ) {
10134         switch( rules->ruleType ){
10135             case RUL_ARE_CO:
10136                 RenderToGLAC( rzRules, rules, vp );
10137                 break; // AC
10138 
10139             case RUL_ARE_PA:
10140                 RenderToGLAP( rzRules, rules, vp );
10141                 break; // AP
10142 
10143             case RUL_CND_SY: {
10144                 if( !rzRules->obj->bCS_Added ) {
10145                     rzRules->obj->CSrules = NULL;
10146                     GetAndAddCSRules( rzRules, rules );
10147                     rzRules->obj->bCS_Added = 1; // mark the object
10148                 }
10149                 Rules *rules_last = rules;
10150                 rules = rzRules->obj->CSrules;
10151 
10152                 while( NULL != rules ) {
10153                         switch( rules->ruleType ){
10154                             case RUL_ARE_CO:
10155                                 RenderToGLAC( rzRules, rules, vp );
10156                                 break;
10157                             case RUL_ARE_PA:
10158                                 RenderToGLAP( rzRules, rules, vp );
10159                                 break;
10160                             case RUL_NONE:
10161                             default:
10162                                 break; // no rule type (init)
10163                         }
10164                         rules_last = rules;
10165                         rules = rules->next;
10166                 }
10167 
10168                 rules = rules_last;
10169                 break;
10170             }
10171 
10172             case RUL_NONE:
10173             default:
10174                 break; // no rule type (init)
10175         } // switch
10176 
10177         rules = rules->next;
10178     }
10179 
10180     return 1;
10181 
10182 }
10183 #endif
10184 
10185 render_canvas_parms* s52plib::CreatePatternBufferSpec( ObjRazRules *rzRules, Rules *rules,
10186                                                        ViewPort *vp, bool b_revrgb, bool b_pot )
10187 {
10188     wxImage Image;
10189 
10190     Rule *prule = rules->razRule;
10191 
10192     bool bstagger_pattern = ( prule->fillType.PATP == 'S' );
10193 
10194     wxColour local_unused_wxColor = m_unused_wxColor;
10195 
10196     //      Create a wxImage of the pattern drawn on an "unused_color" field
10197     if( prule->definition.SYDF == 'R' ) {
10198         Image = useLegacyRaster ?
10199         RuleXBMToImage( prule ) : ChartSymbols::GetImage( prule->name.PANM );
10200     }
10201 
10202     else          // Vector
10203     {
10204         float fsf = 100 / canvas_pix_per_mm;
10205 
10206         // Base bounding box
10207         wxBoundingBox box( prule->pos.patt.bnbox_x.PBXC, prule->pos.patt.bnbox_y.PBXR,
10208                            prule->pos.patt.bnbox_x.PBXC + prule->pos.patt.bnbox_w.PAHL,
10209                            prule->pos.patt.bnbox_y.PBXR + prule->pos.patt.bnbox_h.PAVL );
10210 
10211         // Expand to include pivot
10212         box.Expand( prule->pos.patt.pivot_x.PACL, prule->pos.patt.pivot_y.PARW );
10213 
10214         //    Pattern bounding boxes may be offset from origin, to preset the spacing
10215         //    So, the bitmap must be delta based.
10216         double dwidth = box.GetWidth();
10217         double dheight = box.GetHeight();
10218 
10219         //  Add in the pattern spacing parameters
10220         dwidth += prule->pos.patt.minDist.PAMI;
10221         dheight += prule->pos.patt.minDist.PAMI;
10222 
10223         //  Prescale
10224         dwidth /= fsf;
10225         dheight /= fsf;
10226 
10227         int width = (int) dwidth + 1;
10228         int height = (int) dheight + 1;
10229 
10230 
10231         float render_scale = 1.0;
10232 #ifdef sUSE_ANDROID_GLES2
10233         int width_pot = width;
10234         int height_pot = height;
10235         if( b_pot ) {
10236             int xp = width;
10237             if(((xp != 0) && !(xp & (xp - 1))))     // detect POT
10238                 width_pot = xp;
10239             else{
10240                 int a = 0;
10241                 while( xp ) {
10242                     xp = xp >> 1;
10243                     a++;
10244                 }
10245                 width_pot = 1 << a;
10246             }
10247 
10248             xp = height;
10249             if(((xp != 0) && !(xp & (xp - 1))))
10250                 height_pot = xp;
10251             else{
10252                 int a = 0;
10253                 while( xp ) {
10254                     xp = xp >> 1;
10255                     a++;
10256                 }
10257                 height_pot = 1 << a;
10258             }
10259         }
10260 
10261         // adjust scaler
10262         render_scale = (float) height_pot / (float) height;
10263         qDebug() << "first" << width << width_pot << height << height_pot << render_scale;
10264 
10265         width = width_pot;
10266         height = height_pot;
10267 #endif
10268 
10269 
10270         //      Instantiate the vector pattern to a wxBitmap
10271         wxMemoryDC mdc;
10272 
10273         //  TODO
10274         // This ought to work for wxOSX, but DOES NOT.
10275         // We we do not want anti-aliased lines drawn in the pattern spec, since we used the solid primary color
10276         // as a mask for manual blitting from the dc to memory buffer.
10277 
10278         // Best we can do is set the background color very dark, and hope for the best
10279         #ifdef __WXOSX__
10280         #if wxUSE_GRAPHICS_CONTEXT
10281         wxGraphicsContext* pgc = mdc.GetGraphicsContext();
10282         if(pgc)
10283             pgc->SetAntialiasMode(wxANTIALIAS_NONE);
10284         #endif
10285             local_unused_wxColor.Set(2,2,2);
10286             #endif
10287 
10288             wxBitmap *pbm = NULL;
10289 
10290             if( ( 0 != width ) && ( 0 != height ) ) {
10291                 pbm = new wxBitmap( width, height );
10292 
10293                 mdc.SelectObject( *pbm );
10294                 mdc.SetBackground( wxBrush( local_unused_wxColor ) );
10295                 mdc.Clear();
10296 
10297                 int pivot_x = prule->pos.patt.pivot_x.PACL;
10298                 int pivot_y = prule->pos.patt.pivot_y.PARW;
10299 
10300                 char *str = prule->vector.LVCT;
10301                 char *col = prule->colRef.LCRF;
10302                 wxPoint pivot( pivot_x, pivot_y );
10303 
10304                 int origin_x = prule->pos.patt.bnbox_x.PBXC;
10305                 int origin_y = prule->pos.patt.bnbox_y.PBXR;
10306                 wxPoint origin(origin_x, origin_y);
10307 
10308                 wxPoint r0( (int) ( ( pivot_x - box.GetMinX() ) / fsf ) + 1,
10309                             (int) ( ( pivot_y - box.GetMinY() ) / fsf ) + 1 );
10310 
10311                 HPGL->SetTargetDC( &mdc );
10312                 HPGL->SetVP(vp);
10313                 HPGL->Render( str, col, r0, pivot, origin, 1.0 /*render_scale*/, 0, false);
10314 
10315 //                 mdc.SetPen( wxPen( wxColor(0, 0, 250), 1, wxPENSTYLE_SOLID ) );
10316 //                 mdc.SetBrush(*wxTRANSPARENT_BRUSH);
10317 //                 mdc.DrawRectangle(0,0, width-1, height-1);
10318 
10319             } else {
10320                 pbm = new wxBitmap( 2, 2 );       // substitute small, blank pattern
10321                 mdc.SelectObject( *pbm );
10322                 mdc.SetBackground( wxBrush( local_unused_wxColor ) );
10323                 mdc.Clear();
10324             }
10325 
10326             mdc.SelectObject( wxNullBitmap );
10327 
10328             //    Build a wxImage from the wxBitmap
10329             Image = pbm->ConvertToImage();
10330 
10331             delete pbm;
10332     }
10333 
10334     //  Convert the wxImage to a populated render_canvas_parms struct
10335 
10336     int sizey = Image.GetHeight();
10337     int sizex = Image.GetWidth();
10338 
10339     render_canvas_parms *patt_spec = new render_canvas_parms;
10340     patt_spec->OGL_tex_name = 0;
10341 
10342     if( b_pot ) {
10343         int xp = sizex;
10344         if(((xp != 0) && !(xp & (xp - 1))))     // detect POT
10345             patt_spec->w_pot = sizex;
10346         else{
10347         int a = 0;
10348         while( xp ) {
10349             xp = xp >> 1;
10350             a++;
10351         }
10352         patt_spec->w_pot = 1 << a;
10353         }
10354 
10355         xp = sizey;
10356         if(((xp != 0) && !(xp & (xp - 1))))
10357             patt_spec->h_pot = sizey;
10358         else{
10359             int a = 0;
10360         while( xp ) {
10361             xp = xp >> 1;
10362             a++;
10363         }
10364         patt_spec->h_pot = 1 << a;
10365         }
10366 
10367     } else {
10368         patt_spec->w_pot = sizex;
10369         patt_spec->h_pot = sizey;
10370     }
10371 
10372     patt_spec->depth = 32;             // set the depth, always 32 bit
10373 
10374     patt_spec->pb_pitch = ( ( patt_spec->w_pot * patt_spec->depth / 8 ) );
10375     patt_spec->lclip = 0;
10376     patt_spec->rclip = patt_spec->w_pot - 1;
10377     patt_spec->pix_buff = (unsigned char *) malloc( patt_spec->h_pot * patt_spec->pb_pitch );
10378 
10379     // Preset background
10380     memset( patt_spec->pix_buff, 0, patt_spec->h_pot * patt_spec->pb_pitch );
10381     patt_spec->width = sizex;
10382     patt_spec->height = sizey;
10383     patt_spec->x = 0;
10384     patt_spec->y = 0;
10385     patt_spec->b_stagger = bstagger_pattern;
10386 
10387     unsigned char *pd0 = patt_spec->pix_buff;
10388     unsigned char *pd;
10389     unsigned char *ps0 = Image.GetData();
10390     unsigned char *imgAlpha = NULL;
10391     bool b_use_alpha = false;
10392     if( Image.HasAlpha() ) {
10393         imgAlpha = Image.GetAlpha();
10394         b_use_alpha = true;
10395     }
10396 
10397     #if defined(__WXMAC__) || defined(__WXQT__)
10398 
10399     if( prule->definition.SYDF == 'V' ) {
10400         b_use_alpha = true;
10401         imgAlpha = NULL;
10402     }
10403     #endif
10404 
10405     unsigned char primary_r = 0;
10406     unsigned char primary_g = 0;
10407     unsigned char primary_b = 0;
10408     double reference_value = 0.5;
10409 
10410     bool b_filter = false;
10411 #if defined(__WXMAC__) || defined(__WXGTK3__)
10412     S52color *primary_color = 0;
10413     if( prule->definition.SYDF == 'V' ){
10414         b_filter = true;
10415         char *col = prule->colRef.LCRF;
10416         primary_color = getColor( col+1);
10417         if(primary_color){
10418             primary_r = primary_color->R;
10419             primary_g = primary_color->G;
10420             primary_b = primary_color->B;
10421             wxImage::RGBValue rgb(primary_r,primary_g,primary_b);
10422             wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
10423             reference_value = hsv.value;
10424         }
10425     }
10426 #endif
10427 
10428     unsigned char *ps;
10429 
10430     {
10431         unsigned char mr = local_unused_wxColor.Red();
10432         unsigned char mg = local_unused_wxColor.Green();
10433         unsigned char mb = local_unused_wxColor.Blue();
10434 
10435         if( pd0 && ps0 ){
10436             for( int iy = 0; iy < sizey; iy++ ) {
10437 #ifdef __OCPN__ANDROID__
10438                 pd = pd0 + ( (sizey - iy - 1) * patt_spec->pb_pitch );
10439 #else
10440                 pd = pd0 + ( iy * patt_spec->pb_pitch );
10441 #endif
10442                 ps = ps0 + ( iy * sizex * 3 );
10443                 for( int ix = 0; ix < sizex; ix++ ) {
10444                     if( ix < sizex ) {
10445                         unsigned char r = *ps++;
10446                         unsigned char g = *ps++;
10447                         unsigned char b = *ps++;
10448 
10449                         if(b_filter){
10450                             wxImage::RGBValue rgb(r,g,b);
10451                             wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
10452                             double ratio = hsv.value/reference_value;
10453 
10454                             if(ratio > 0.5){
10455                                 *pd++ = primary_r;
10456                                 *pd++ = primary_g;
10457                                 *pd++ = primary_b;
10458                                 *pd++ = 255;
10459                             }
10460                             else{
10461                                 *pd++ = 0;
10462                                 *pd++ = 0;
10463                                 *pd++ = 0;
10464                                 *pd++ = 0;
10465                             }
10466                         }
10467                         else{
10468                             #ifdef ocpnUSE_ocpnBitmap
10469                             if( b_revrgb ) {
10470                                 *pd++ = b;
10471                                 *pd++ = g;
10472                                 *pd++ = r;
10473                             } else {
10474                                 *pd++ = r;
10475                                 *pd++ = g;
10476                                 *pd++ = b;
10477                             }
10478 
10479                             #else
10480                             *pd++ = r;
10481                             *pd++ = g;
10482                             *pd++ = b;
10483                             #endif
10484                             if( b_use_alpha && imgAlpha ) {
10485                                 *pd++ = *imgAlpha++;
10486                             } else {
10487                                 *pd++ = ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
10488                             }
10489                         }
10490                     }
10491                 }
10492             }
10493         }
10494     }
10495 
10496     return patt_spec;
10497 
10498 }
10499 
10500 
10501 int s52plib::RenderToBufferAP( ObjRazRules *rzRules, Rules *rules, ViewPort *vp,
10502         render_canvas_parms *pb_spec )
10503 {
10504     if(vp->m_projection_type != PROJECTION_MERCATOR)
10505         return 1;
10506 
10507     wxImage Image;
10508 
10509     if( rules->razRule == NULL )
10510         return 0;
10511 
10512     if( ( rules->razRule->pixelPtr == NULL ) || ( rules->razRule->parm1 != m_colortable_index )
10513             || ( rules->razRule->parm0 != ID_RGB_PATT_SPEC ) ) {
10514         render_canvas_parms *patt_spec = CreatePatternBufferSpec( rzRules, rules, vp, true );
10515 
10516         ClearRulesCache( rules->razRule ); //  Clear out any existing cached symbology
10517 
10518         rules->razRule->pixelPtr = patt_spec;
10519         rules->razRule->parm1 = m_colortable_index;
10520         rules->razRule->parm0 = ID_RGB_PATT_SPEC;
10521 
10522     } // Instantiation done
10523 
10524     //  Render the Area using the pattern spec stored in the rules
10525     render_canvas_parms *ppatt_spec = (render_canvas_parms *) rules->razRule->pixelPtr;
10526 
10527     //  Set the pattern reference point
10528 
10529     wxPoint r;
10530     GetPointPixSingle( rzRules, rzRules->obj->y, rzRules->obj->x, &r, vp );
10531 
10532     ppatt_spec->x = r.x - 2000000; // bias way down to avoid zero-crossing logic in dda
10533     ppatt_spec->y = r.y - 2000000;
10534 
10535     RenderToBufferFilledPolygon( rzRules, rzRules->obj, NULL, pb_spec, ppatt_spec, vp );
10536 
10537     return 1;
10538 }
10539 
10540 int s52plib::RenderToBufferAC( ObjRazRules *rzRules, Rules *rules, ViewPort *vp,
10541         render_canvas_parms *pb_spec )
10542 {
10543     if(vp->m_projection_type != PROJECTION_MERCATOR)
10544         return 1;
10545 
10546     S52color *c;
10547     char *str = (char*) rules->INSTstr;
10548 
10549     c = getColor( str );
10550 
10551     RenderToBufferFilledPolygon( rzRules, rzRules->obj, c, pb_spec, NULL, vp );
10552 
10553     //    At very small scales, the object could be visible on both the left and right sides of the screen.
10554     //    Identify this case......
10555     if( vp->chart_scale > 5e7 ) {
10556         //    Does the object hang out over the left side of the VP?
10557         if( ( rzRules->obj->BBObj.GetMaxLon() > vp->GetBBox().GetMinLon() )
10558                 && ( rzRules->obj->BBObj.GetMinLon() < vp->GetBBox().GetMinLon() ) ) {
10559             //    If we add 360 to the objects lons, does it intersect the the right side of the VP?
10560             if( ( ( rzRules->obj->BBObj.GetMaxLon() + 360. ) > vp->GetBBox().GetMaxLon() )
10561                     && ( ( rzRules->obj->BBObj.GetMinLon() + 360. ) < vp->GetBBox().GetMaxLon() ) ) {
10562                 //  If so, this area oject should be drawn again, this time for the left side
10563                 //    Do this by temporarily adjusting the objects rendering offset
10564                 rzRules->obj->x_origin -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
10565                 RenderToBufferFilledPolygon( rzRules, rzRules->obj, c, pb_spec,
10566                         NULL, vp );
10567                 rzRules->obj->x_origin += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
10568 
10569             }
10570         }
10571     }
10572 
10573     return 1;
10574 }
10575 
10576 int s52plib::RenderAreaToDC( wxDC *pdcin, ObjRazRules *rzRules, ViewPort *vp,
10577         render_canvas_parms *pb_spec )
10578 {
10579 
10580     if( !ObjectRenderCheckRules( rzRules, vp, true ) )
10581         return 0;
10582 
10583     m_pdc = pdcin; // use this DC
10584     Rules *rules = rzRules->LUP->ruleList;
10585 
10586     while( rules != NULL ) {
10587         switch( rules->ruleType ){
10588             case RUL_ARE_CO:
10589                 RenderToBufferAC( rzRules, rules, vp, pb_spec );
10590                 break; // AC
10591             case RUL_ARE_PA:
10592                 RenderToBufferAP( rzRules, rules, vp, pb_spec );
10593                 break; // AP
10594 
10595             case RUL_CND_SY: {
10596                 if( !rzRules->obj->bCS_Added ) {
10597                     rzRules->obj->CSrules = NULL;
10598                     GetAndAddCSRules( rzRules, rules );
10599                     rzRules->obj->bCS_Added = 1; // mark the object
10600                 }
10601                 Rules *rules_last = rules;
10602                 rules = rzRules->obj->CSrules;
10603 
10604                 //    The CS procedure may have changed the Display Category of the Object, need to check again for visibility
10605                 if( ObjectRenderCheckCat( rzRules, vp ) ) {
10606                     while( NULL != rules ) {
10607                         //Hve seen drgare fault here, need to code area query to debug
10608                         //possible that RENDERtoBUFFERAP/AC is blowing obj->CSRules
10609                         //    When it faults here, look at new debug field obj->CSLUP
10610                         switch( rules->ruleType ){
10611                             case RUL_ARE_CO:
10612                                 RenderToBufferAC( rzRules, rules, vp, pb_spec );
10613                                 break;
10614                             case RUL_ARE_PA:
10615                                 RenderToBufferAP( rzRules, rules, vp, pb_spec );
10616                                 break;
10617                             case RUL_NONE:
10618                             default:
10619                                 break; // no rule type (init)
10620                         }
10621                         rules_last = rules;
10622                         rules = rules->next;
10623                     }
10624                 }
10625 
10626                 rules = rules_last;
10627                 break;
10628             }
10629 
10630             case RUL_NONE:
10631             default:
10632                 break; // no rule type (init)
10633         } // switch
10634 
10635         rules = rules->next;
10636     }
10637 
10638     return 1;
10639 
10640 }
10641 
10642 void s52plib::GetAndAddCSRules( ObjRazRules *rzRules, Rules *rules )
10643 {
10644 
10645     LUPrec *NewLUP;
10646     LUPrec *LUP;
10647     LUPrec *LUPCandidate;
10648 
10649     char *rule_str1 = RenderCS( rzRules, rules );
10650     wxString cs_string( rule_str1, wxConvUTF8 );
10651     free( rule_str1 ); //delete rule_str1;
10652 
10653 //  Try to find a match for this object/attribute set in dynamic CS LUP Table
10654 
10655 //  Do this by checking each LUP in the CS LUPARRAY and checking....
10656 //  a) is Object Name the same? and
10657 //  b) was LUP created earlier by exactly the same INSTruction string?
10658 //  c) does LUP have same Display Category and Priority?
10659 
10660     wxArrayOfLUPrec *la = condSymbolLUPArray;
10661     int index = 0;
10662     int index_max = la->GetCount();
10663     LUP = NULL;
10664 
10665     while( ( index < index_max ) ) {
10666         LUPCandidate = la->Item( index );
10667         if( !strcmp( rzRules->LUP->OBCL, LUPCandidate->OBCL ) ) {
10668             if( LUPCandidate->INST->IsSameAs( cs_string ) ) {
10669                 if( LUPCandidate->DISC == rzRules->LUP->DISC ) {
10670                     LUP = LUPCandidate;
10671                     break;
10672                 }
10673             }
10674         }
10675         index++;
10676     }
10677 
10678 //  If not found, need to create a dynamic LUP and add to CS LUP Table
10679 
10680     if( NULL == LUP ) // Not found
10681             {
10682 
10683         NewLUP = (LUPrec*) calloc( 1, sizeof(LUPrec) );
10684         pAlloc->Add( NewLUP );
10685 
10686         NewLUP->DISC = rzRules->LUP->DISC; // as a default
10687 
10688         //sscanf(pBuf+11, "%d", &LUP->RCID);
10689 
10690         memcpy( NewLUP->OBCL, rzRules->LUP->OBCL, 6 ); // the object class name
10691 
10692 //      Add the complete CS string to the LUP
10693         wxString *pINST = new wxString( cs_string );
10694         NewLUP->INST = pINST;
10695 
10696         _LUP2rules( NewLUP, rzRules->obj );
10697 
10698 // Add LUP to array
10699         wxArrayOfLUPrec *pLUPARRAYtyped = condSymbolLUPArray;
10700 
10701         pLUPARRAYtyped->Add( NewLUP );
10702 
10703         LUP = NewLUP;
10704 
10705     } // if (LUP = NULL)
10706 
10707     Rules *top = LUP->ruleList;
10708 
10709     rzRules->obj->CSrules = top; // patch in a new rule set
10710 
10711 }
10712 
10713 bool s52plib::ObjectRenderCheck( ObjRazRules *rzRules, ViewPort *vp )
10714 {
10715     if( !ObjectRenderCheckPos( rzRules, vp ) ) return false;
10716 
10717     if( !ObjectRenderCheckCat( rzRules, vp ) ) return false;
10718 
10719     return true;
10720 }
10721 
10722 bool s52plib::ObjectRenderCheckCS( ObjRazRules *rzRules, ViewPort *vp )
10723 {
10724 //  We need to do this test since some CS procedures change the display category
10725 //  So we need to tentatively process all objects with CS LUPs
10726     Rules *rules = rzRules->LUP->ruleList;
10727     while( rules != NULL ) {
10728         if( RUL_CND_SY == rules->ruleType ) return true;
10729 
10730         rules = rules->next;
10731     }
10732 
10733     return false;
10734 }
10735 
10736 bool s52plib::ObjectRenderCheckPos( ObjRazRules *rzRules, ViewPort *vp )
10737 {
10738     if( rzRules->obj == NULL )
10739         return false;
10740 
10741     // Of course, the object must be at least partly visible in the viewport
10742     const LLBBox &vpBox = vp->GetBBox(), &testBox = rzRules->obj->BBObj;
10743 
10744     if(vpBox.GetMaxLat() < testBox.GetMinLat() || vpBox.GetMinLat() > testBox.GetMaxLat())
10745         return false;
10746 
10747     if(vpBox.GetMaxLon() >= testBox.GetMinLon() && vpBox.GetMinLon() <= testBox.GetMaxLon())
10748         return true;
10749 
10750     if(vpBox.GetMaxLon() >= testBox.GetMinLon()+360 && vpBox.GetMinLon() <= testBox.GetMaxLon()+360)
10751         return true;
10752 
10753     if(vpBox.GetMaxLon() >= testBox.GetMinLon()-360 && vpBox.GetMinLon() <= testBox.GetMaxLon()-360)
10754         return true;
10755 
10756     return false;
10757 }
10758 
10759 bool s52plib::ObjectRenderCheckCat( ObjRazRules *rzRules, ViewPort *vp )
10760 {
10761     g_scaminScale = 1.0;
10762 
10763     if( rzRules->obj == NULL ) return false;
10764 
10765     bool b_catfilter = true;
10766     bool b_visible = false;
10767 
10768     //      Do Object Type Filtering
10769     DisCat obj_cat = rzRules->obj->m_DisplayCat;
10770 
10771     //  Meta object filter.
10772     // Applied when showing display category OTHER, and
10773     // only for objects whose decoded S52 display category (by LUP) is also OTHER
10774     if( m_nDisplayCategory == OTHER ){
10775         if(OTHER == obj_cat){
10776             if( !strncmp( rzRules->LUP->OBCL, "M_", 2 ) )
10777                 if( !m_bShowMeta &&  strncmp( rzRules->LUP->OBCL, "M_QUAL", 6 ))
10778                     return false;
10779         }
10780     }
10781     else{
10782     // We want to filter out M_NSYS objects everywhere except "OTHER" category
10783         if( !strncmp( rzRules->LUP->OBCL, "M_", 2 ) )
10784             if( !m_bShowMeta )
10785                 return false;
10786     }
10787 
10788 #ifdef __OCPN__ANDROID__
10789     // We want to filter out M_NSYS objects on Android, as they are of limited use on a phone/tablet
10790     if( !strncmp( rzRules->LUP->OBCL, "M_", 2 ) )
10791         if( !m_bShowMeta ) return false;
10792 #endif
10793 
10794     if( m_nDisplayCategory == MARINERS_STANDARD ) {
10795         if( -1 == rzRules->obj->iOBJL ) UpdateOBJLArray( rzRules->obj );
10796 
10797         if( DISPLAYBASE == obj_cat ){        // always display individual objects that were moved to DISPLAYBASE by CS Procedures
10798             b_visible = true;
10799             b_catfilter = false;
10800         }
10801         else if( !( (OBJLElement *) ( pOBJLArray->Item( rzRules->obj->iOBJL ) ) )->nViz ){
10802                 b_catfilter = false;
10803         }
10804     }
10805 
10806     else
10807         if( m_nDisplayCategory == OTHER ) {
10808             if( ( DISPLAYBASE != obj_cat ) && ( STANDARD != obj_cat ) && ( OTHER != obj_cat ) ) {
10809                 b_catfilter = false;
10810             }
10811         }
10812 
10813         else
10814             if( m_nDisplayCategory == STANDARD ) {
10815                 if( ( DISPLAYBASE != obj_cat ) && ( STANDARD != obj_cat ) ) {
10816                     b_catfilter = false;
10817                 }
10818             } else
10819                 if( m_nDisplayCategory == DISPLAYBASE ) {
10820                     if( DISPLAYBASE != obj_cat ) {
10821                         b_catfilter = false;
10822                     }
10823                 }
10824 
10825 //  Soundings override
10826     if( !strncmp( rzRules->LUP->OBCL, "SOUNDG", 6 ) )
10827         b_catfilter = m_bShowSoundg;
10828 
10829     if( b_catfilter ) {
10830         b_visible = true;
10831 
10832 //      SCAMIN Filtering
10833         //      Implementation note:
10834         //      According to S52 specs, SCAMIN must not apply to GROUP1 objects, Meta Objects
10835         //      or DisplayCategoryBase objects.
10836         //      Occasionally, an ENC will encode a spurious SCAMIN value for one of these objects.
10837         //      see, for example, US5VA18M, in OpenCPN SENC as Feature 350(DEPARE), LNAM = 022608187ED20ACC.
10838         //      We shall explicitly ignore SCAMIN filtering for these types of objects.
10839 
10840         if( m_bUseSCAMIN ) {
10841 
10842 
10843             if( ( DISPLAYBASE == rzRules->LUP->DISC ) || ( PRIO_GROUP1 == rzRules->LUP->DPRI ) )
10844                 b_visible = true;
10845             else{
10846 //                if( vp->chart_scale > rzRules->obj->Scamin ) b_visible = false;
10847 
10848 
10849                 double zoom_mod = (double)g_chart_zoom_modifier_vector;
10850 
10851                 double modf = zoom_mod/5.;  // -1->1
10852                 double mod = pow(8., modf);
10853                 mod = wxMax(mod, .2);
10854                 mod = wxMin(mod, 8.0);
10855 
10856                 if(mod > 1){
10857                     if( vp->chart_scale  > rzRules->obj->Scamin * mod )
10858                         b_visible = false;                              // definitely invisible
10859                     else{
10860                         //  Theoretically invisible, however...
10861                         //  In the "zoom modified" scale region,
10862                         //  we render the symbol at reduced size, scaling down to no less than half normal size.
10863 
10864                         if(vp->chart_scale  > rzRules->obj->Scamin){
10865                             double xs = vp->chart_scale - rzRules->obj->Scamin;
10866                             double xl = (rzRules->obj->Scamin * mod) - rzRules->obj->Scamin;
10867                             g_scaminScale = 1.0 - (0.5 * xs / xl);
10868 
10869                         }
10870                     }
10871                 }
10872                 else{
10873                     if(vp->chart_scale  > rzRules->obj->Scamin)
10874                         b_visible = false;
10875                 }
10876             }
10877 
10878             //      On the other hand, $TEXTS features need not really be displayed at all scales, always
10879             //      To do so makes a very cluttered display
10880             if( ( !strncmp( rzRules->LUP->OBCL, "$TEXTS", 6 ) )
10881                     && ( vp->chart_scale > rzRules->obj->Scamin ) ) b_visible = false;
10882         }
10883 
10884         return b_visible;
10885     }
10886 
10887     return b_visible;
10888 }
10889 
10890 bool s52plib::ObjectRenderCheckRules( ObjRazRules *rzRules, ViewPort *vp, bool check_noshow )
10891 {
10892     if( !ObjectRenderCheckPos( rzRules, vp ) )
10893         return false;
10894 
10895     // The Feature M_QUAL, in MARINERS_STANDARD catagory, is a special case,
10896     // since it is also controlled by a global hotkey in display category ALL and MARINERS_STANDARD
10897     if(m_nDisplayCategory == MARINERS_STANDARD){
10898         if(strncmp(rzRules->obj->FeatureName, "M_QUAL", 6)){            // Anything other than M_QUAL
10899             if( check_noshow && IsObjNoshow( rzRules->LUP->OBCL) )
10900                 return 0;
10901         }
10902         else{
10903             if(!m_qualityOfDataOn)
10904                 return 0;
10905         }
10906     }
10907     else{
10908         if( check_noshow && IsObjNoshow( rzRules->LUP->OBCL) )
10909             return false;
10910     }
10911 
10912     if( ObjectRenderCheckCat( rzRules, vp ) )
10913         return true;
10914 
10915     //  If this object cannot be moved to a higher category by CS procedures,
10916     //  then we are done here
10917     if(!rzRules->obj->m_bcategory_mutable)
10918         return false;
10919 
10920     // already added, nothing below can change its display category
10921     if(rzRules->obj->bCS_Added )
10922         return false;
10923 
10924     //  Otherwise, make sure the CS, if present, has been evaluated,
10925     //  and then check the category again
10926     //  no rules
10927     if( !ObjectRenderCheckCS( rzRules, vp ) )
10928         return false;
10929 
10930     rzRules->obj->CSrules = NULL;
10931     Rules *rules = rzRules->LUP->ruleList;
10932     while( rules != NULL ) {
10933         if( RUL_CND_SY ==  rules->ruleType ){
10934             GetAndAddCSRules( rzRules, rules );
10935             rzRules->obj->bCS_Added = 1; // mark the object
10936             break;
10937         }
10938         rules = rules->next;
10939     }
10940 
10941     // still not displayable
10942     if( !ObjectRenderCheckCat( rzRules, vp ) )
10943         return false;
10944 
10945     return true;
10946 }
10947 
10948 
10949 void s52plib::SetDisplayCategory(enum _DisCat cat)
10950 {
10951     enum _DisCat old = m_nDisplayCategory;
10952     m_nDisplayCategory = cat;
10953 
10954     if(old != cat){
10955         ClearNoshow();
10956     }
10957     GenerateStateHash();
10958 }
10959 
10960 
10961 bool s52plib::IsObjNoshow( const char *objcl )
10962 {
10963     for(unsigned int i=0 ; i < m_noshow_array.GetCount() ; i++){
10964         if(!strncmp(m_noshow_array[i].obj, objcl, 6) )
10965             return true;
10966     }
10967     return false;
10968 }
10969 
10970 void s52plib::AddObjNoshow( const char *objcl )
10971 {
10972     if( !IsObjNoshow( objcl ) ){
10973         noshow_element element;
10974         memcpy(element.obj, objcl, 6);
10975         m_noshow_array.Add( element );
10976     }
10977 }
10978 
10979 void s52plib::RemoveObjNoshow( const char *objcl )
10980 {
10981     for(unsigned int i=0 ; i < m_noshow_array.GetCount() ; i++){
10982         if(!strncmp(m_noshow_array[i].obj, objcl, 6) ){
10983             m_noshow_array.RemoveAt(i);
10984             return;
10985         }
10986     }
10987 }
10988 
10989 void s52plib::ClearNoshow(void)
10990 {
10991     m_noshow_array.Clear();
10992 }
10993 
10994 void s52plib::PLIB_LoadS57Config()
10995 {
10996     //    Get a pointer to the opencpn configuration object
10997     wxFileConfig *pconfig = GetOCPNConfigObject();
10998 
10999     int read_int;
11000     double dval;
11001 
11002     pconfig->SetPath( _T ( "/Settings" ) );
11003     //pconfig->Read( _T ( "DebugS57" ), &g_PIbDebugS57, 0 );         // Show LUP and Feature info in object query
11004 
11005     pconfig->SetPath( _T ( "/Settings/GlobalState" ) );
11006 
11007     pconfig->Read( _T ( "bShowS57Text" ), &read_int, 0 );
11008     SetShowS57Text( !( read_int == 0 ) );
11009 
11010     pconfig->Read( _T ( "bShowS57ImportantTextOnly" ), &read_int, 0 );
11011     SetShowS57ImportantTextOnly( !( read_int == 0 ) );
11012 
11013     pconfig->Read( _T ( "bShowLightDescription" ), &read_int, 0 );
11014     SetShowLdisText( !( read_int == 0 ) );
11015 
11016     pconfig->Read( _T ( "bExtendLightSectors" ), &read_int, 0 );
11017     SetExtendLightSectors( !( read_int == 0 ) );
11018 
11019     pconfig->Read( _T ( "nDisplayCategory" ), &read_int, (enum _DisCat) STANDARD );
11020     SetDisplayCategory((enum _DisCat) read_int );
11021 
11022     pconfig->Read( _T ( "nSymbolStyle" ), &read_int, (enum _LUPname) PAPER_CHART );
11023     m_nSymbolStyle = (LUPname) read_int;
11024 
11025     pconfig->Read( _T ( "nBoundaryStyle" ), &read_int, PLAIN_BOUNDARIES );
11026     m_nBoundaryStyle = (LUPname) read_int;
11027 
11028     pconfig->Read( _T ( "bShowSoundg" ), &read_int, 1 );
11029     m_bShowSoundg = !( read_int == 0 );
11030 
11031     pconfig->Read( _T ( "bShowMeta" ), &read_int, 0 );
11032     m_bShowMeta = !( read_int == 0 );
11033 
11034     pconfig->Read( _T ( "bUseSCAMIN" ), &read_int, 1 );
11035     m_bUseSCAMIN = !( read_int == 0 );
11036 
11037     pconfig->Read( _T ( "bShowAtonText" ), &read_int, 1 );
11038     m_bShowAtonText = !( read_int == 0 );
11039 
11040     pconfig->Read( _T ( "bDeClutterText" ), &read_int, 0 );
11041     m_bDeClutterText = !( read_int == 0 );
11042 
11043     pconfig->Read( _T ( "bShowNationalText" ), &read_int, 0 );
11044     m_bShowNationalTexts = !( read_int == 0 );
11045 
11046     if( pconfig->Read( _T ( "S52_MAR_SAFETY_CONTOUR" ), &dval, 5.0 ) ) {
11047         S52_setMarinerParam( S52_MAR_SAFETY_CONTOUR, dval );
11048         S52_setMarinerParam( S52_MAR_SAFETY_DEPTH, dval ); // Set safety_contour and safety_depth the same
11049     }
11050 
11051     if( pconfig->Read( _T ( "S52_MAR_SHALLOW_CONTOUR" ), &dval, 3.0 ) ) S52_setMarinerParam(
11052         S52_MAR_SHALLOW_CONTOUR, dval );
11053 
11054     if( pconfig->Read( _T ( "S52_MAR_DEEP_CONTOUR" ), &dval, 10.0 ) ) S52_setMarinerParam(
11055         S52_MAR_DEEP_CONTOUR, dval );
11056 
11057     if( pconfig->Read( _T ( "S52_MAR_TWO_SHADES" ), &dval, 0.0 ) ) S52_setMarinerParam(
11058         S52_MAR_TWO_SHADES, dval );
11059 
11060     UpdateMarinerParams();
11061 
11062     pconfig->SetPath( _T ( "/Settings/GlobalState" ) );
11063     pconfig->Read( _T ( "S52_DEPTH_UNIT_SHOW" ), &read_int, 1 );   // default is metres
11064     read_int = wxMax(read_int, 0);                      // qualify value
11065     read_int = wxMin(read_int, 2);
11066     m_nDepthUnitDisplay = read_int;
11067 
11068     //    S57 Object Class Visibility
11069 
11070     OBJLElement *pOLE;
11071 
11072     pconfig->SetPath( _T ( "/Settings/ObjectFilter" ) );
11073 
11074     int iOBJMax = pconfig->GetNumberOfEntries();
11075     if( iOBJMax ) {
11076 
11077         wxString str;
11078         long val;
11079         long dummy;
11080 
11081         wxString sObj;
11082 
11083         bool bCont = pconfig->GetFirstEntry( str, dummy );
11084         while( bCont ) {
11085             pconfig->Read( str, &val );              // Get an Object Viz
11086 
11087             bool bNeedNew = true;
11088 
11089             if( str.StartsWith( _T ( "viz" ), &sObj ) ) {
11090                 for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ ) {
11091                     pOLE = (OBJLElement *) ( pOBJLArray->Item( iPtr ) );
11092                     if( !strncmp( pOLE->OBJLName, sObj.mb_str(), 6 ) ) {
11093                         pOLE->nViz = val;
11094                         bNeedNew = false;
11095                         break;
11096                     }
11097                 }
11098 
11099                 if( bNeedNew ) {
11100                     pOLE = (OBJLElement *) calloc( sizeof(OBJLElement), 1 );
11101                     memcpy( pOLE->OBJLName, sObj.mb_str(), OBJL_NAME_LEN );
11102                     pOLE->nViz = 1;
11103 
11104                     pOBJLArray->Add( (void *) pOLE );
11105                 }
11106             }
11107             bCont = pconfig->GetNextEntry( str, dummy );
11108         }
11109     }
11110 }
11111 
11112 //    Do all those things necessary to prepare for a new rendering
11113 void s52plib::PrepareForRender( void )
11114 {
11115     PrepareForRender(NULL);
11116 }
11117 
11118 void s52plib::PrepareForRender(ViewPort *vp)
11119 {
11120     m_benableGLLS = true;               // default is to always use RenderToGLLS (VBO support)
11121 
11122 #ifdef USE_ANDROID_GLES2
11123 void PrepareS52ShaderUniforms(ViewPort *vp);
11124     if(vp)
11125         PrepareS52ShaderUniforms( vp );
11126 #endif
11127 
11128 #ifdef BUILDING_PLUGIN
11129     // Has the core S52PLIB configuration changed?
11130     //  If it has, reload from global preferences file, and other dynamic status information.
11131     //  This additional step is only necessary for Plugin chart rendering, as core directly sets
11132     //  options and updates State Hash as needed.
11133 
11134     int core_config = PI_GetPLIBStateHash();
11135     if(core_config != m_myConfig){
11136 
11137         g_ChartScaleFactorExp = GetOCPNChartScaleFactor_Plugin();
11138 
11139         //  If a modern (> OCPN 4.4) version of the core is active,
11140         //  we may rely upon having been updated on S52PLIB state by means of PlugIn messaging scheme.
11141         if( ((m_coreVersionMajor == 4) && (m_coreVersionMinor >= 5)) || m_coreVersionMajor > 4 ){
11142 
11143             // Retain compatibility with O4.8.x
11144             if( (m_coreVersionMajor == 4) && (m_coreVersionMinor < 9)){
11145              // First, we capture some temporary values that were set by messaging, but would be overwritten by config read
11146              bool bTextOn = m_bShowS57Text;
11147              bool bSoundingsOn = m_bShowSoundg;
11148              enum _DisCat old = m_nDisplayCategory;
11149 
11150              PLIB_LoadS57Config();
11151 
11152              //  And then reset the temp values that were overwritten by config load
11153              m_bShowS57Text = bTextOn;
11154              m_bShowSoundg = bSoundingsOn;
11155              m_nDisplayCategory = old;
11156             }
11157             else
11158                 PLIB_LoadS57GlobalConfig();
11159 
11160 
11161             // Pick up any changes in Mariner's Standard object list
11162             PLIB_LoadS57ObjectConfig();
11163 
11164             // Detect and manage "LIGHTS" toggle
11165              bool bshow_lights = !m_lightsOff;
11166              if(!bshow_lights)                     // On, going off
11167                  AddObjNoshow("LIGHTS");
11168              else{                                   // Off, going on
11169                  RemoveObjNoshow("LIGHTS");
11170              }
11171 
11172             const char * categories[] = { "ACHBRT", "ACHARE", "CBLSUB", "PIPARE", "PIPSOL", "TUNNEL", "SBDARE" };
11173             unsigned int num = sizeof(categories) / sizeof(categories[0]);
11174 
11175             // Handle Anchor area toggle
11176             if( (m_nDisplayCategory == OTHER) || (m_nDisplayCategory == MARINERS_STANDARD) ){
11177                 bool bAnchor = m_anchorOn;
11178 
11179 
11180                 if(!bAnchor){
11181                     for( unsigned int c = 0; c < num; c++ )
11182                         AddObjNoshow(categories[c]);
11183                     }
11184                 else{
11185                     for( unsigned int c = 0; c < num; c++ )
11186                     RemoveObjNoshow(categories[c]);
11187 
11188                     //  Force the USER STANDARD object list anchor detail items ON
11189                     unsigned int cnt = 0;
11190                 for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ ) {
11191                     OBJLElement *pOLE = (OBJLElement *) ( pOBJLArray->Item( iPtr ) );
11192                         for( unsigned int c = 0; c < num; c++ ) {
11193                             if( !strncmp( pOLE->OBJLName, categories[c], 6 ) ) {
11194                                 pOLE->nViz = 1;         // force on
11195                                 cnt++;
11196                                 break;
11197                             }
11198                         }
11199                         if( cnt == num ) break;
11200                     }
11201                 }
11202             }
11203         }
11204         m_myConfig = PI_GetPLIBStateHash();
11205     }
11206 
11207 #endif          //BUILDING_PLUGIN
11208 
11209     // Reset the LIGHTS declutter machine
11210     lastLightLat = 0;
11211     lastLightLon = 0;
11212 
11213     //Precalulate the ENC Soundings scale factor
11214     m_SoundingsScaleFactor = exp( m_nSoundingFactor * (log(2.0) / 5.0) );
11215 
11216 
11217 }
11218 
11219 void s52plib::SetAnchorOn(bool val)
11220 {
11221     const char * categories[] = { "ACHBRT", "ACHARE", "CBLSUB", "PIPARE", "PIPSOL", "TUNNEL", "SBDARE" };
11222     unsigned int num = sizeof(categories) / sizeof(categories[0]);
11223 
11224     if( (m_nDisplayCategory == OTHER) || (m_nDisplayCategory == MARINERS_STANDARD) ){
11225         bool bAnchor = val;
11226 
11227         if(!bAnchor){
11228             for( unsigned int c = 0; c < num; c++ ) {
11229                 AddObjNoshow(categories[c]);
11230             }
11231         }
11232         else{
11233             for( unsigned int c = 0; c < num; c++ ) {
11234                 RemoveObjNoshow(categories[c]);
11235             }
11236         }
11237     }
11238     else{                               // if not category OTHER, then anchor-related features are always shown.
11239         for( unsigned int c = 0; c < num; c++ ) {
11240             RemoveObjNoshow(categories[c]);
11241         }
11242     }
11243 
11244     m_anchorOn = val;
11245 }
11246 
11247 void s52plib::SetQualityOfData(bool val)
11248 {
11249     int old_vis = GetQualityOfData();
11250     if(old_vis == val)
11251         return;
11252 
11253     if(old_vis && !val){                            // On, going off
11254         AddObjNoshow("M_QUAL");
11255     }
11256     else if(!old_vis && val){                                   // Off, going on
11257         RemoveObjNoshow("M_QUAL");
11258 
11259         for( unsigned int iPtr = 0; iPtr < pOBJLArray->GetCount(); iPtr++ ) {
11260             OBJLElement *pOLE = (OBJLElement *) ( pOBJLArray->Item( iPtr ) );
11261             if( !strncmp( pOLE->OBJLName, "M_QUAL", 6 ) ) {
11262                 pOLE->nViz = 1;         // force on
11263                 break;
11264             }
11265         }
11266     }
11267 
11268     m_qualityOfDataOn = val;
11269 
11270 }
11271 
11272 void s52plib::ClearTextList( void )
11273 {
11274     //      Clear the current text rectangle list
11275     m_textObjList.Clear();
11276 
11277 }
11278 
11279 bool s52plib::EnableGLLS(bool b_enable)
11280 {
11281     bool return_val = m_benableGLLS;
11282     m_benableGLLS = b_enable;
11283     return return_val;
11284 }
11285 
11286 void s52plib::AdjustTextList( int dx, int dy, int screenw, int screenh )
11287 {
11288     return;
11289     wxRect rScreen( 0, 0, screenw, screenh );
11290     //    Iterate over the text rectangle list
11291     //        1.  Apply the specified offset to the list elements
11292     //        2.. Remove any list elements that are off screen after applied offset
11293 
11294     TextObjList::Node *node = m_textObjList.GetFirst();
11295     TextObjList::Node *next;
11296     while( node ) {
11297         next = node->GetNext();
11298         wxRect *pcurrent = &( node->GetData()->rText );
11299         pcurrent->Offset( dx, dy );
11300         if( !pcurrent->Intersects( rScreen ) ) {
11301             m_textObjList.DeleteNode( node );
11302         }
11303         node = next;
11304     }
11305 }
11306 
11307 bool s52plib::GetPointPixArray( ObjRazRules *rzRules, wxPoint2DDouble* pd, wxPoint *pp, int nv, ViewPort *vp )
11308 {
11309         for( int i = 0; i < nv; i++ ) {
11310             GetPointPixSingle(rzRules, pd[i].m_y, pd[i].m_x, pp + i, vp);
11311         }
11312 
11313     return true;
11314 }
11315 
11316 bool s52plib::GetPointPixSingle( ObjRazRules *rzRules, float north, float east, wxPoint *r, ViewPort *vp )
11317 {
11318         if(vp->m_projection_type == PROJECTION_MERCATOR) {
11319 
11320             double xr =  rzRules->obj->x_rate;
11321             double xo =  rzRules->obj->x_origin;
11322             double yr =  rzRules->obj->y_rate;
11323             double yo =  rzRules->obj->y_origin;
11324 
11325             if(fabs(xo) > 1){                           // cm93 hits this
11326                 if ( vp->GetBBox().GetMaxLon() >= 180. && rzRules->obj->BBObj.GetMaxLon() < vp->GetBBox().GetMinLon() )
11327                     xo += mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
11328                 else if( (vp->GetBBox().GetMinLon() <= -180. &&
11329                     rzRules->obj->BBObj.GetMinLon() > vp->GetBBox().GetMaxLon()) ||
11330                 (rzRules->obj->BBObj.GetMaxLon() >= 180 && vp->GetBBox().GetMinLon() <= 0.))
11331                     xo -= mercator_k0 * WGS84_semimajor_axis_meters * 2.0 * PI;
11332             }
11333 
11334             double valx = ( east * xr ) + xo;
11335             double valy = ( north * yr ) + yo;
11336 
11337             r->x = roundint(((valx - rzRules->sm_transform_parms->easting_vp_center) * vp->view_scale_ppm) + (vp->pix_width / 2) );
11338             r->y = roundint((vp->pix_height/2) - ((valy - rzRules->sm_transform_parms->northing_vp_center) * vp->view_scale_ppm));
11339         } else {
11340               double lat, lon;
11341               fromSM_Plugin(east - rzRules->sm_transform_parms->easting_vp_center,
11342                      north - rzRules->sm_transform_parms->northing_vp_center,
11343                      vp->clat, vp->clon, &lat, &lon);
11344 
11345               *r = vp->GetPixFromLL(north, east);
11346         }
11347 
11348     return true;
11349 }
11350 
11351 void s52plib::GetPixPointSingle( int pixx, int pixy, double *plat, double *plon, ViewPort *vpt )
11352 {
11353 #if 1
11354     vpt->GetLLFromPix(wxPoint(pixx, pixy), plat, plon);
11355 //    if(*plon < 0 && vpt->clon > 180)
11356     //      *plon += 360;
11357 #else
11358     //    Use Mercator estimator
11359     int dx = pixx - ( vpt->pix_width / 2 );
11360     int dy = ( vpt->pix_height / 2 ) - pixy;
11361 
11362     double xp = ( dx * cos( vpt->skew ) ) - ( dy * sin( vpt->skew ) );
11363     double yp = ( dy * cos( vpt->skew ) ) + ( dx * sin( vpt->skew ) );
11364 
11365     double d_east = xp / vpt->view_scale_ppm;
11366     double d_north = yp / vpt->view_scale_ppm;
11367 
11368     double slat, slon;
11369     fromSM( d_east, d_north, vpt->clat, vpt->clon, &slat, &slon );
11370 
11371     *plat = slat;
11372     *plon = slon;
11373 #endif
11374 }
11375 
11376 void s52plib::GetPixPointSingleNoRotate( int pixx, int pixy, double *plat, double *plon, ViewPort *vpt )
11377 {
11378     if(vpt){
11379         double rotation = vpt->rotation;
11380         vpt->SetRotationAngle(0);
11381         vpt->GetLLFromPix(wxPoint(pixx, pixy), plat, plon);
11382         vpt->SetRotationAngle(rotation);
11383     }
11384 }
11385 
11386 
11387 void DrawAALine( wxDC *pDC, int x0, int y0, int x1, int y1, wxColour clrLine, int dash, int space )
11388 {
11389 
11390     int width = 1 + abs( x0 - x1 );
11391     int height = 1 + abs( y0 - y1 );
11392     wxPoint upperLeft( wxMin ( x0, x1 ), wxMin ( y0, y1 ) );
11393 
11394     wxBitmap bm( width, height );
11395     wxMemoryDC mdc( bm );
11396 
11397     mdc.Blit( 0, 0, width, height, pDC, upperLeft.x, upperLeft.y );
11398 
11399 #if wxUSE_GRAPHICS_CONTEXT
11400     wxGCDC gdc( mdc );
11401 #else
11402     wxMemoryDC &gdc( mdc );
11403 #endif
11404 
11405     wxPen pen( clrLine, 1, wxPENSTYLE_USER_DASH );
11406     wxDash dashes[2];
11407     dashes[0] = dash;
11408     dashes[1] = space;
11409     pen.SetDashes( 2, dashes );
11410     gdc.SetPen( pen );
11411 
11412     gdc.DrawLine( x0 - upperLeft.x, y0 - upperLeft.y, x1 - upperLeft.x, y1 - upperLeft.y );
11413 
11414     pDC->Blit( upperLeft.x, upperLeft.y, width, height, &mdc, 0, 0 );
11415 
11416     mdc.SelectObject( wxNullBitmap );
11417 
11418     return;
11419 }
11420 
11421 void s52plib::DrawDashLine( wxPen &pen, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, ViewPort *vp)
11422 {
11423 #ifdef USE_ANDROID_GLES2
11424     glLineWidth( pen.GetWidth() );
11425 
11426     glUseProgram(S52color_tri_shader_program);
11427 
11428     float fBuf[4];
11429     GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
11430     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), fBuf);
11431     glEnableVertexAttribArray(pos);
11432 
11433  /*   GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"MVMatrix");
11434     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)cc1->GetpVP()->vp_transform);
11435  */
11436     float colorv[4];
11437     colorv[0] = pen.GetColour().Red() / float(256);
11438     colorv[1] = pen.GetColour().Green() / float(256);
11439     colorv[2] = pen.GetColour().Blue() / float(256);
11440     colorv[3] = 1.0;
11441 
11442     GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
11443     glUniform4fv(colloc, 1, colorv);
11444 
11445 
11446     if(fabs(vp->rotation) > 0.01){
11447         float cx = vp->pix_width/2.;
11448         float cy = vp->pix_height/2.;
11449         float c = cosf(-vp->rotation );
11450         float s = sinf(-vp->rotation );
11451 
11452         float xn = x1 - cx;
11453         float yn = y1 - cy;
11454         x1 =  xn*c - yn*s + cx;
11455         y1 =  xn*s + yn*c + cy;
11456 
11457         xn = x2 - cx;
11458         yn = y2 - cy;
11459         x2 =  xn*c - yn*s + cx;
11460         y2 =  xn*s + yn*c + cy;
11461     }
11462 
11463     wxDash *dashes;
11464     int n_dashes = pen.GetDashes( &dashes );
11465     if( n_dashes ) {
11466         float angle = atan2f( (float) ( y2 - y1 ), (float) ( x2 - x1 ) );
11467         float cosa = cosf( angle );
11468         float sina = sinf( angle );
11469         float t1 = pen.GetWidth();
11470 
11471         float lpix = sqrtf( powf(x1 - x2, 2) + powf(y1 - y2, 2) );
11472         float lrun = 0.;
11473         float xa = x1;
11474         float ya = y1;
11475         float ldraw = t1 * dashes[0];
11476         float lspace = t1 * dashes[1];
11477 
11478         ldraw = wxMax(ldraw, 4.0);
11479         lspace = wxMax(lspace, 4.0);
11480         lpix = wxMin(lpix, 2000.0);
11481 
11482         while( lrun < lpix ) {
11483             //    Dash
11484             float xb = xa + ldraw * cosa;
11485             float yb = ya + ldraw * sina;
11486 
11487             if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
11488             {
11489                 xb = x2;
11490                 yb = y2;
11491             }
11492 
11493             fBuf[0] = xa;
11494             fBuf[1] = ya;
11495             fBuf[2] = xb;
11496             fBuf[3] = yb;
11497 
11498             glDrawArrays(GL_LINES, 0, 2);
11499 
11500             xa = xa + ( lspace + ldraw ) * cosa;
11501             ya = ya + ( lspace + ldraw ) * sina;
11502             lrun += lspace + ldraw;
11503 
11504         }
11505     } else {                    // not dashed
11506         fBuf[0] = x1;
11507         fBuf[1] = y1;
11508         fBuf[2] = x2;
11509         fBuf[3] = y2;
11510 
11511         glDrawArrays(GL_LINES, 0, 2);
11512     }
11513 #endif
11514 }
11515 
11516 
11517 
11518 RenderFromHPGL::RenderFromHPGL( s52plib* plibarg )
11519 {
11520     plib = plibarg;
11521     renderToDC = false;
11522     renderToOpenGl = false;
11523     renderToGCDC = false;
11524 
11525     if(plib)
11526         scaleFactor = 100.0 / plib->GetPPMM();
11527     else
11528         scaleFactor = 10.0;  //Nominal
11529 
11530 
11531     workBufSize = 0;
11532     workBufIndex = 0;
11533     workBuf = NULL;
11534 
11535     s_odc_tess_work_buf = NULL;
11536     s_odc_tess_vertex_idx = 0;
11537     s_odc_tess_vertex_idx_this = 0;
11538     s_odc_tess_buf_len = 0;
11539 
11540     transparency = 255;
11541 }
11542 
11543 RenderFromHPGL::~RenderFromHPGL( )
11544 {
11545 #ifdef ocpnUSE_GL
11546     if( renderToOpenGl ) {
11547         glDisable (GL_BLEND );
11548     }
11549 
11550     free(workBuf);
11551     free(s_odc_tess_work_buf);
11552 
11553 #endif
11554 }
11555 
11556 void RenderFromHPGL::SetTargetDC( wxDC* pdc )
11557 {
11558     targetDC = pdc;
11559     renderToDC = true;
11560     renderToOpenGl = false;
11561     renderToGCDC = false;
11562 }
11563 
11564 void RenderFromHPGL::SetTargetOpenGl()
11565 {
11566     renderToOpenGl = true;
11567     renderToDC = false;
11568     renderToGCDC = false;
11569 }
11570 
11571 #if wxUSE_GRAPHICS_CONTEXT
11572 void RenderFromHPGL::SetTargetGCDC( wxGCDC* gdc )
11573 {
11574     targetGCDC = gdc;
11575     renderToGCDC = true;
11576     renderToDC = false;
11577     renderToOpenGl = false;
11578 }
11579 #endif
11580 
11581 const char* RenderFromHPGL::findColorNameInRef( char colorCode, char* col )
11582 {
11583     int noColors = strlen( col ) / 6;
11584     for( int i = 0, j=0; i < noColors; i++, j += 6 ) {
11585         if( *(col + j) == colorCode ) return col + j + 1;
11586     }
11587     return col + 1; // Default to first color if not found.
11588 }
11589 
11590 wxPoint RenderFromHPGL::ParsePoint( wxString& argument )
11591 {
11592     long x, y;
11593     int colon = argument.Index( ',' );
11594     argument.Left( colon ).ToLong( &x );
11595     argument.Mid( colon + 1 ).ToLong( &y );
11596     return wxPoint( x, y );
11597 }
11598 
11599 void RenderFromHPGL::SetPen()
11600 {
11601     float nominal_line_width_pix = wxMax(1.0, floor(plib->GetPPMM() / 5.0));             //0.2 mm nominal, but not less than 1 pixel
11602     int pen_width_mod =  floor( penWidth * nominal_line_width_pix);
11603 
11604     pen = wxThePenList->FindOrCreatePen( penColor, pen_width_mod, wxPENSTYLE_SOLID );
11605     brush = wxTheBrushList->FindOrCreateBrush( penColor, wxBRUSHSTYLE_SOLID );
11606 
11607     if( renderToDC ) {
11608         targetDC->SetPen( *pen );
11609         targetDC->SetBrush( *brush );
11610     }
11611 #ifdef ocpnUSE_GL
11612     if( renderToOpenGl ) {
11613         if( plib->GetGLPolygonSmoothing() )
11614             glEnable( GL_POLYGON_SMOOTH );
11615 
11616 #ifndef USE_ANDROID_GLES2
11617         glColor4ub( penColor.Red(), penColor.Green(), penColor.Blue(), transparency );
11618 #endif
11619         int line_width = wxMax(g_GLMinSymbolLineWidth, (float) penWidth * 0.7);
11620         glLineWidth( line_width );
11621 
11622 #ifdef __OCPN__ANDROID__
11623         //  Scale the pen width dependent on the platform display resolution
11624         float nominal_line_width_pix = wxMax(1.0, floor(plib->GetPPMM() / 5.0));             //0.2 mm nominal, but not less than 1 pixel
11625         //qDebug() << nominal_line_width_pix;
11626         line_width =  wxMax(g_GLMinSymbolLineWidth, (float) penWidth * nominal_line_width_pix);
11627         glLineWidth( line_width );
11628 #endif
11629 
11630 #ifndef __OCPN__ANDROID__
11631         if( line_width >= 2 && plib->GetGLLineSmoothing() )
11632             glEnable( GL_LINE_SMOOTH );
11633         else
11634             glDisable( GL_LINE_SMOOTH );
11635         glEnable( GL_BLEND );
11636 #endif
11637     }
11638 #endif
11639 #if wxUSE_GRAPHICS_CONTEXT
11640     if( renderToGCDC ) {
11641         pen = wxThePenList->FindOrCreatePen( penColor, penWidth, wxPENSTYLE_SOLID );
11642         brush = wxTheBrushList->FindOrCreateBrush( penColor, wxBRUSHSTYLE_SOLID );
11643         targetGCDC->SetPen( *pen );
11644         targetGCDC->SetBrush( *brush );
11645     }
11646 #endif
11647 }
11648 
11649 void RenderFromHPGL::Line( wxPoint from, wxPoint to )
11650 {
11651     if( renderToDC ) {
11652         targetDC->DrawLine( from, to );
11653     }
11654 #ifdef ocpnUSE_GL
11655     if( renderToOpenGl ) {
11656 
11657 #ifdef USE_ANDROID_GLES2
11658         glUseProgram(S52color_tri_shader_program);
11659 
11660         glBindBuffer( GL_ARRAY_BUFFER, 0 );
11661         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
11662 
11663 //         mat4x4 Q;
11664 //         mat4x4 X;
11665 //         mat4x4_identity(Q);
11666 //         mat4x4_mul(X, (float (*)[4])m_vp->vp_transform, Q);
11667 //
11668         //GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"MVMatrix");
11669         //glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)cc1->GetpVP()->vp_transform);
11670 
11671         float colorv[4];
11672         colorv[0] = penColor.Red() / float(256);
11673         colorv[1] = penColor.Green() / float(256);
11674         colorv[2] = penColor.Blue() / float(256);
11675         colorv[3] = transparency / float(256);
11676 
11677         GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
11678         glUniform4fv(colloc, 1, colorv);
11679 
11680         float pts[4];
11681         pts[0] = from.x; pts[1] = from.y; pts[2] = to.x; pts[3] = to.y;
11682 
11683         GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
11684         glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pts);
11685         glEnableVertexAttribArray(pos);
11686 
11687         glDrawArrays(GL_LINES, 0, 2);
11688 #else
11689         glBegin( GL_LINES );
11690         glVertex2i( from.x, from.y );
11691         glVertex2i( to.x, to.y );
11692         glEnd();
11693 #endif
11694     }
11695 #endif
11696 #if wxUSE_GRAPHICS_CONTEXT
11697     if( renderToGCDC ) {
11698         targetGCDC->DrawLine( from, to );
11699     }
11700 #endif
11701 }
11702 
11703 void RenderFromHPGL::Circle( wxPoint center, int radius, bool filled )
11704 {
11705     if( renderToDC ) {
11706         if( filled )
11707             targetDC->SetBrush( *brush );
11708         else
11709             targetDC->SetBrush( *wxTRANSPARENT_BRUSH );
11710         targetDC->DrawCircle( center, radius );
11711     }
11712 #ifdef ocpnUSE_GL
11713     if( renderToOpenGl ) {
11714 #ifdef USE_ANDROID_GLES2
11715         if(!m_vp)               // oops, forgot to set the VP parameters
11716             return;
11717 
11718     //      Enable anti-aliased lines, at best quality
11719         glEnable( GL_BLEND );
11720 
11721         float coords[8];
11722         coords[0] = center.x - radius;  coords[1] = center.y + radius;
11723         coords[2] = center.x + radius;  coords[3] = center.y + radius;
11724         coords[4] = center.x - radius;  coords[5] = center.y - radius;
11725         coords[6] = center.x + radius;  coords[7] = center.y - radius;
11726 
11727         glUseProgram( S52circle_filled_shader_program );
11728 
11729         // Get pointers to the attributes in the program.
11730         GLint mPosAttrib = glGetAttribLocation( S52circle_filled_shader_program, "aPos" );
11731 
11732             // Disable VBO's (vertex buffer objects) for attributes.
11733         glBindBuffer( GL_ARRAY_BUFFER, 0 );
11734         glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
11735 
11736         glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
11737         glEnableVertexAttribArray( mPosAttrib );
11738 
11739         //  Circle radius
11740         GLint radiusloc = glGetUniformLocation(S52circle_filled_shader_program,"circle_radius");
11741         glUniform1f(radiusloc, radius);
11742 
11743         //  Circle center point
11744         GLint centerloc = glGetUniformLocation(S52circle_filled_shader_program,"circle_center");
11745         float ctrv[2];
11746         ctrv[0] = center.x; ctrv[1] = m_vp->pix_height - center.y;
11747         glUniform2fv(centerloc, 1, ctrv);
11748 
11749         //  Circle fill color
11750         float colorv[4];
11751         colorv[3] = 0.0;        // transparent default
11752 
11753         if(brush){
11754             colorv[0] = brush->GetColour().Red() / float(256);
11755             colorv[1] = brush->GetColour().Green() / float(256);
11756             colorv[2] = brush->GetColour().Blue() / float(256);
11757             if(filled)
11758                 colorv[3] = 1.0;
11759         }
11760 
11761         GLint colloc = glGetUniformLocation(S52circle_filled_shader_program,"circle_color");
11762         glUniform4fv(colloc, 1, colorv);
11763 
11764         //  Border color
11765         float bcolorv[4];
11766         bcolorv[0] = penColor.Red() / float(256);
11767         bcolorv[1] = penColor.Green() / float(256);
11768         bcolorv[2] = penColor.Blue() / float(256);
11769         bcolorv[3] = penColor.Alpha() / float(256);
11770 
11771         GLint bcolloc = glGetUniformLocation(S52circle_filled_shader_program,"border_color");
11772         glUniform4fv(bcolloc, 1, bcolorv);
11773 
11774         //  Border Width
11775         float nominal_line_width_pix = wxMax(1.0, floor(plib->GetPPMM() / 5.0));             //0.2 mm nominal, but not less than 1 pixel
11776         float line_width =  wxMax(g_GLMinSymbolLineWidth, (float) penWidth * nominal_line_width_pix);
11777 
11778         GLint borderWidthloc = glGetUniformLocation(S52circle_filled_shader_program,"border_width");
11779         glUniform1f(borderWidthloc, line_width);
11780 
11781             // Perform the actual drawing.
11782         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
11783 
11784         //      Enable anti-aliased lines, at best quality
11785         glDisable( GL_BLEND );
11786 
11787 #else
11788         int noSegments = 2 + ( radius * 4 );
11789         if( noSegments > 200 ) noSegments = 200;
11790         glBegin( GL_LINE_STRIP );
11791         for( float a = 0; a <= 2 * M_PI; a += 2 * M_PI / noSegments )
11792             glVertex2f( center.x + radius * sinf( a ),
11793                         center.y + radius * cosf( a ) );
11794         glEnd();
11795 #endif
11796     }
11797 #endif
11798 #if wxUSE_GRAPHICS_CONTEXT
11799     if( renderToGCDC ) {
11800         if( filled ) targetGCDC->SetBrush( *brush );
11801         else
11802             targetGCDC->SetBrush( *wxTRANSPARENT_BRUSH );
11803 
11804         targetGCDC->DrawCircle( center, radius );
11805 
11806         // wxGCDC doesn't update min/max X/Y properly for DrawCircle.
11807         targetGCDC->SetPen( *wxTRANSPARENT_PEN );
11808         targetGCDC->DrawPoint( center.x - radius, center.y );
11809         targetGCDC->DrawPoint( center.x + radius, center.y );
11810         targetGCDC->DrawPoint( center.x, center.y - radius );
11811         targetGCDC->DrawPoint( center.x, center.y + radius );
11812         targetGCDC->SetPen( *pen );
11813     }
11814 #endif
11815 }
11816 
11817 void RenderFromHPGL::Polygon()
11818 {
11819     if( renderToDC ) {
11820         targetDC->DrawPolygon( noPoints, polygon );
11821     }
11822 #ifdef ocpnUSE_GL
11823     if( renderToOpenGl ) {
11824 #ifdef USE_ANDROID_GLES2
11825 
11826         penColor.Set(penColor.Red(), penColor.Green(), penColor.Blue(), transparency);
11827         pen = wxThePenList->FindOrCreatePen( penColor, penWidth, wxPENSTYLE_SOLID );
11828         brush = wxTheBrushList->FindOrCreateBrush( penColor, wxBRUSHSTYLE_SOLID );
11829         DrawPolygon( noPoints, polygon, 0, 0, 1.0, 0 );
11830 
11831 #else
11832         glColor4ub( penColor.Red(), penColor.Green(), penColor.Blue(), transparency );
11833 
11834         glBegin( GL_POLYGON );
11835         for( int ip = 1; ip < noPoints; ip++ )
11836             glVertex2i( polygon[ip].x, polygon[ip].y );
11837         glEnd();
11838 #endif
11839     }
11840 #endif  // OpenGL
11841 
11842 #if wxUSE_GRAPHICS_CONTEXT
11843     if( renderToGCDC ) {
11844         targetGCDC->DrawPolygon( noPoints, polygon );
11845     }
11846 #endif
11847 }
11848 
11849 void RenderFromHPGL::RotatePoint( wxPoint& point, wxPoint origin, double angle )
11850 {
11851     if( angle == 0. ) return;
11852     double sin_rot = sin( angle * PI / 180. );
11853     double cos_rot = cos( angle * PI / 180. );
11854 
11855     double xp = ( (point.x - origin.x) * cos_rot ) - ( (point.y - origin.y) * sin_rot );
11856     double yp = ( (point.x - origin.x) * sin_rot ) + ( (point.y - origin.y) * cos_rot );
11857 
11858     point.x = (int) xp + origin.x;
11859     point.y = (int) yp + origin.y;
11860 }
11861 
11862 bool RenderFromHPGL::Render( char *str, char *col, wxPoint &r, wxPoint &pivot, wxPoint origin, float scale, double rot_angle, bool bSymbol )
11863 {
11864 #ifdef ocpnUSE_GL
11865 #ifndef USE_ANDROID_GLES2
11866     if( renderToOpenGl )
11867         glGetFloatv(GL_CURRENT_COLOR,m_currentColor);
11868 #endif
11869 #endif
11870 
11871     wxPoint lineStart;
11872     wxPoint lineEnd;
11873 
11874     scaleFactor = 100.0 / plib->GetPPMM();
11875     scaleFactor /= scale;
11876     scaleFactor /= g_scaminScale;
11877 
11878     if(bSymbol)
11879         scaleFactor /= plib->GetRVScaleFactor();
11880 
11881     // SW is not always defined, cf. US/US4CA17M/US4CA17M.000
11882     penWidth = 1;
11883 
11884     wxStringTokenizer commands( wxString( str, wxConvUTF8 ), _T(";") );
11885     while( commands.HasMoreTokens() ) {
11886         wxString command = commands.GetNextToken();
11887         wxString arguments = command.Mid( 2 );
11888         command = command.Left( 2 );
11889 
11890         if( command == _T("SP") ) {
11891             S52color* color = plib->getColor( findColorNameInRef( arguments.GetChar( 0 ), col ) );
11892             penColor = wxColor( color->R, color->G, color->B );
11893             brushColor = penColor;
11894             continue;
11895         }
11896         if( command == _T("SW") ) {
11897             arguments.ToLong( &penWidth );
11898             continue;
11899         }
11900         if( command == _T("ST") ) {
11901             long transIndex;
11902             arguments.ToLong( &transIndex );
11903             transparency = (4 - transIndex) * 64;
11904             transparency = wxMin(transparency, 255);
11905             transparency = wxMax(0, transparency);
11906             continue;
11907         }
11908         if( command == _T("PU") ) {
11909             SetPen();
11910             lineStart = ParsePoint( arguments );
11911             RotatePoint( lineStart, origin, rot_angle );
11912             lineStart -= pivot;
11913             lineStart.x /= scaleFactor;
11914             lineStart.y /= scaleFactor;
11915             lineStart += r;
11916             continue;
11917         }
11918         if( command == _T("PD") ) {
11919             if( arguments.Length() == 0 ) {
11920                 lineEnd = lineStart;
11921                 lineEnd.x++;
11922             } else {
11923                 lineEnd = ParsePoint( arguments );
11924                 RotatePoint( lineEnd, origin, rot_angle );
11925                 lineEnd -= pivot;
11926                 lineEnd.x /= scaleFactor;
11927                 lineEnd.y /= scaleFactor;
11928                 lineEnd += r;
11929             }
11930             Line( lineStart, lineEnd );
11931             lineStart = lineEnd; // For next line.
11932             continue;
11933         }
11934         if( command == _T("CI") ) {
11935             long radius;
11936             arguments.ToLong( &radius );
11937             radius = (int) radius / scaleFactor;
11938             Circle( lineStart, radius );
11939             continue;
11940         }
11941         if( command == _T("PM") ) {
11942             noPoints = 1;
11943             polygon[0] = lineStart;
11944 
11945             if( arguments == _T("0") ) {
11946                 do {
11947                     command = commands.GetNextToken();
11948                     arguments = command.Mid( 2 );
11949                     command = command.Left( 2 );
11950 
11951                     if( command == _T("AA") ) {
11952                         wxLogWarning( _T("RenderHPGL: AA instruction not implemented.") );
11953                     }
11954                     if( command == _T("CI") ) {
11955                         long radius;
11956                         arguments.ToLong( &radius );
11957                         radius = (int) radius / scaleFactor;
11958                         Circle( lineStart, radius, HPGL_FILLED );
11959                     }
11960                     if( command == _T("PD") ) {
11961                         wxStringTokenizer points( arguments, _T(",") );
11962                         while( points.HasMoreTokens() ) {
11963                             long x, y;
11964                             points.GetNextToken().ToLong( &x );
11965                             points.GetNextToken().ToLong( &y );
11966                             lineEnd = wxPoint( x, y );
11967                             RotatePoint( lineEnd, origin, rot_angle );
11968                             lineEnd -= pivot;
11969                             lineEnd.x /= scaleFactor;
11970                             lineEnd.y /= scaleFactor;
11971                             lineEnd += r;
11972                             polygon[noPoints++] = lineEnd;
11973                         }
11974                     }
11975                 } while( command != _T("PM") );
11976             }
11977             continue;
11978         }
11979         if( command == _T("FP") ) {
11980             SetPen();
11981             Polygon();
11982             continue;
11983         }
11984 
11985         // Only get here if non of the other cases did a continue.
11986 //        wxString msg( _T("RenderHPGL: The '%s' instruction is not implemented.") );
11987 //        msg += wxString( command );
11988 //        wxLogWarning( msg );
11989     }
11990 
11991     transparency = 255;
11992 
11993 #ifdef ocpnUSE_GL
11994     if( renderToOpenGl ) {
11995         glDisable (GL_BLEND );
11996 #ifndef USE_ANDROID_GLES2
11997         glColor4fv( m_currentColor );
11998 #endif
11999     }
12000 #endif
12001 
12002     return true;
12003 }
12004 
12005 
12006 
12007 wxArrayPtrVoid s52gTesselatorVertices;
12008 
12009 void RenderFromHPGL::DrawPolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, float scale, float angle )
12010 {
12011 //    if( 0 )
12012         //dc->DrawPolygon( n, points, xoffset, yoffset );
12013     #ifdef ocpnUSE_GL
12014  //       else
12015         {
12016 
12017  #ifdef __WXQT__
12018             glDisable( GL_LINE_SMOOTH );
12019             glDisable( GL_POLYGON_SMOOTH );
12020             glDisable( GL_BLEND );
12021 
12022  #else
12023             glEnable( GL_LINE_SMOOTH );
12024             glEnable( GL_POLYGON_SMOOTH );
12025             glEnable( GL_BLEND );
12026 
12027   #endif
12028 
12029  #ifdef USE_ANDROID_GLES2
12030 
12031             //ConfigurePen();
12032             glLineWidth( pen->GetWidth() );
12033 
12034             glEnable( GL_BLEND );
12035 
12036              if(n > 4)
12037                  DrawPolygonTessellated( n, points, xoffset, yoffset);
12038              else
12039             {           // n = 3 or 4, most common case for pre-tesselated shapes
12040 
12041 
12042             //  Grow the work buffer as necessary
12043             if( workBufSize < (size_t)n*2 ){
12044                 workBuf = (float *)realloc(workBuf, (n*4) * sizeof(float));
12045                 workBufSize = n*4;
12046             }
12047 
12048             for( int i = 0; i < n; i++ ){
12049                 workBuf[i*2] = (points[i].x * scale); // + xoffset;
12050                 workBuf[i*2 + 1] = (points[i].y * scale); // + yoffset;
12051             }
12052 
12053             glUseProgram( S52color_tri_shader_program );
12054 
12055             // Get pointers to the attributes in the program.
12056             GLint mPosAttrib = glGetAttribLocation( S52color_tri_shader_program, "position" );
12057 
12058             // Disable VBO's (vertex buffer objects) for attributes.
12059             glBindBuffer( GL_ARRAY_BUFFER, 0 );
12060             glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
12061 
12062             glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, workBuf );
12063             glEnableVertexAttribArray( mPosAttrib );
12064 
12065 
12066             //  Border color
12067             float bcolorv[4];
12068             bcolorv[0] = pen->GetColour().Red() / float(256);
12069             bcolorv[1] = pen->GetColour().Green() / float(256);
12070             bcolorv[2] = pen->GetColour().Blue() / float(256);
12071             bcolorv[3] = pen->GetColour().Alpha() / float(256);
12072 
12073             GLint bcolloc = glGetUniformLocation(S52color_tri_shader_program,"color");
12074             glUniform4fv(bcolloc, 1, bcolorv);
12075 
12076 ///
12077 #if 0
12078             wxWindow *win = GetOCPNCanvasWindow();
12079             ChartCanvas *canvas = wxDynamicCast( win, ChartCanvas); //classname * wxDynamicCast(ptr, classname)
12080 
12081             //ViewPort *pvp = (ViewPort *)&vp;
12082             ViewPort *pvp = canvas->GetpVP();
12083 
12084             // Rotate
12085             mat4x4 I, Q;
12086             mat4x4_identity(I);
12087             mat4x4_rotate_Z(Q, I, angle);
12088 
12089             // Translate
12090             Q[3][0] = xoffset;
12091             Q[3][1] = yoffset;
12092 
12093             mat4x4 X;
12094             mat4x4_mul(X, (float (*)[4])pvp->vp_transform, Q);
12095 
12096             GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"MVMatrix");
12097             glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)X );
12098 ///
12099 #endif
12100             // Perform the actual drawing.
12101             glDrawArrays(GL_LINE_LOOP, 0, n);
12102 
12103             //  Fill color
12104             bcolorv[0] = brush->GetColour().Red() / float(256);
12105             bcolorv[1] = brush->GetColour().Green() / float(256);
12106             bcolorv[2] = brush->GetColour().Blue() / float(256);
12107             bcolorv[3] = brush->GetColour().Alpha() / float(256);
12108 
12109             glUniform4fv(bcolloc, 1, bcolorv);
12110 
12111             // For the simple common case of a convex rectangle...
12112             //  swizzle the array points to enable GL_TRIANGLE_STRIP
12113             if(n == 4){
12114                 float x1 = workBuf[4];
12115                 float y1 = workBuf[5];
12116                 workBuf[4] = workBuf[6];
12117                 workBuf[5] = workBuf[7];
12118                 workBuf[6] = x1;
12119                 workBuf[7] = y1;
12120 
12121                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
12122             }
12123             else if(n == 3){
12124                 glDrawArrays(GL_TRIANGLES, 0, 3);
12125             }
12126             }
12127 
12128 
12129 #else
12130 
12131             wxColour c = brush->GetColour();
12132             glColor4ub( c.Red(), c.Green(), c.Blue(), c.Alpha() );
12133 
12134             glEnable( GL_POLYGON_SMOOTH );
12135             glBegin( GL_POLYGON );
12136             for( int i = 0; i < n; i++ )
12137                 glVertex2f( (points[i].x * scale) + xoffset, (points[i].y * scale) + yoffset );
12138             glEnd();
12139             glDisable( GL_POLYGON_SMOOTH );
12140 
12141             int width = pen->GetWidth();
12142             glLineWidth( width );
12143 
12144             glEnable( GL_LINE_SMOOTH );
12145             glBegin( GL_LINE_LOOP );
12146             for( int i = 0; i < n; i++ )
12147                 glVertex2f( (points[i].x * scale) + xoffset, (points[i].y * scale) + yoffset );
12148             glEnd();
12149             glDisable( GL_LINE_SMOOTH );
12150 #endif
12151 
12152             glDisable( GL_LINE_SMOOTH );
12153             glDisable( GL_POLYGON_SMOOTH );
12154             glDisable( GL_BLEND );
12155 
12156         }
12157         #endif
12158 }
12159 
12160 #ifdef ocpnUSE_GL
12161 
12162 // GL callbacks
12163 
12164 typedef union {
12165     GLdouble data[6];
12166     struct sGLvertex {
12167         GLdouble x;
12168         GLdouble y;
12169         GLdouble z;
12170         GLdouble r;
12171         GLdouble g;
12172         GLdouble b;
12173     } info;
12174 } GLvertex;
12175 
12176 #ifndef USE_ANDROID_GLES2
12177 void APIENTRY s52DCcombineCallback( GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4],
12178                                      GLdouble **dataOut )
12179 {
12180     GLvertex *vertex;
12181 
12182     vertex = new GLvertex();
12183     s52gTesselatorVertices.Add(vertex );
12184 
12185     vertex->info.x = coords[0];
12186     vertex->info.y = coords[1];
12187     vertex->info.z = coords[2];
12188 
12189     for( int i = 3; i < 6; i++ ) {
12190         vertex->data[i] = weight[0] * vertex_data[0][i] + weight[1] * vertex_data[1][i];
12191     }
12192 
12193     *dataOut = &(vertex->data[0]);
12194 }
12195 
12196 void APIENTRY s52DCvertexCallback( GLvoid* arg )
12197 {
12198     GLvertex* vertex;
12199     vertex = (GLvertex*) arg;
12200     glVertex2f( (float)vertex->info.x, (float)vertex->info.y );
12201 }
12202 
12203 void APIENTRY s52DCerrorCallback( GLenum errorCode )
12204 {
12205     const GLubyte *estring;
12206     estring = gluErrorString(errorCode);
12207     //wxLogMessage( _T("OpenGL Tessellation Error: %s"), (char *)estring );
12208 }
12209 
12210 void APIENTRY s52DCbeginCallback( GLenum type )
12211 {
12212     glBegin( type );
12213 }
12214 
12215 void APIENTRY s52DCendCallback()
12216 {
12217     glEnd();
12218 }
12219 #endif
12220 
12221 
12222 // GLSL callbacks
12223 
12224 
12225 #ifdef USE_ANDROID_GLES2
12226 
12227 static std::list<double*> odc_combine_work_data;
12228 static void s52_combineCallbackD(GLdouble coords[3],
12229                                  GLdouble *vertex_data[4],
12230                                  GLfloat weight[4], GLdouble **dataOut, void *data )
12231 {
12232     //     double *vertex = new double[3];
12233     //     odc_combine_work_data.push_back(vertex);
12234     //     memcpy(vertex, coords, 3*(sizeof *coords));
12235     //     *dataOut = vertex;
12236 }
12237 
12238 void s52_vertexCallbackD_GLSL(GLvoid *vertex, void *data)
12239 {
12240     RenderFromHPGL* plib = (RenderFromHPGL*)data;
12241 
12242     // Grow the work buffer if necessary
12243     if(plib->s_odc_tess_vertex_idx > plib->s_odc_tess_buf_len - 8)
12244     {
12245         int new_buf_len = plib->s_odc_tess_buf_len + 100;
12246         GLfloat * tmp = plib->s_odc_tess_work_buf;
12247 
12248         plib->s_odc_tess_work_buf = (GLfloat *)realloc(plib->s_odc_tess_work_buf, new_buf_len * sizeof(GLfloat));
12249         if (NULL == plib->s_odc_tess_work_buf)
12250         {
12251             free(tmp);
12252             tmp = NULL;
12253         }
12254         else
12255             plib->s_odc_tess_buf_len = new_buf_len;
12256     }
12257 
12258     GLdouble *pointer = (GLdouble *) vertex;
12259 
12260     plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx++] = (float)pointer[0];
12261     plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx++] = (float)pointer[1];
12262 
12263     plib->s_odc_nvertex++;
12264 }
12265 
12266 void s52_beginCallbackD_GLSL( GLenum mode, void *data)
12267 {
12268     RenderFromHPGL* plib = (RenderFromHPGL*)data;
12269     plib->s_odc_tess_vertex_idx_this =  plib->s_odc_tess_vertex_idx;
12270     plib->s_odc_tess_mode = mode;
12271     plib->s_odc_nvertex = 0;
12272 }
12273 
12274 void s52_endCallbackD_GLSL(void *data)
12275 {
12276     //qDebug() << "End" << s_odc_nvertex << s_odc_tess_buf_len << s_odc_tess_vertex_idx << s_odc_tess_vertex_idx_this;
12277     //End 5 100 10 0
12278     #if 1
12279     RenderFromHPGL* plib = (RenderFromHPGL*)data;
12280 
12281     glUseProgram(S52color_tri_shader_program);
12282 
12283     // Disable VBO's (vertex buffer objects) for attributes.
12284     glBindBuffer( GL_ARRAY_BUFFER, 0 );
12285     glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
12286 
12287     float *bufPt = &(plib->s_odc_tess_work_buf[plib->s_odc_tess_vertex_idx_this]);
12288     GLint pos = glGetAttribLocation(S52color_tri_shader_program, "position");
12289     glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufPt);
12290     glEnableVertexAttribArray(pos);
12291 
12292     ///GLint matloc = glGetUniformLocation(color_tri_shader_program,"MVMatrix");
12293     ///glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)s_tessVP.vp_transform);
12294 
12295     float colorv[4];
12296     wxColour c = plib->getBrush()->GetColour();
12297 
12298     colorv[0] = c.Red() / float(256);
12299     colorv[1] = c.Green() / float(256);
12300     colorv[2] = c.Blue() / float(256);
12301     colorv[3] = c.Alpha() / float(256);
12302 
12303     GLint colloc = glGetUniformLocation(S52color_tri_shader_program,"color");
12304     glUniform4fv(colloc, 1, colorv);
12305 
12306     glDrawArrays(plib->s_odc_tess_mode, 0, plib->s_odc_nvertex);
12307     #endif
12308 }
12309 #endif
12310 
12311 
12312 #endif          //#ifdef ocpnUSE_GL
12313 
12314 void RenderFromHPGL::DrawPolygonTessellated( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset )
12315 {
12316 //    if( 0 )
12317         //dc->DrawPolygon( n, points, xoffset, yoffset );
12318     #ifdef ocpnUSE_GL
12319         //else
12320         {
12321              #if !defined(ocpnUSE_GLES) || defined(USE_ANDROID_GLES2) // tessalator in glues is broken
12322              if( n < 5 )
12323              # endif
12324              {
12325                  DrawPolygon( n, points, xoffset, yoffset, 1.0, 0 );
12326                  return;
12327              }
12328 
12329 
12330 
12331  #ifdef USE_ANDROID_GLES2
12332             m_tobj = gluNewTess();
12333             s_odc_tess_vertex_idx = 0;
12334 
12335             gluTessCallback( m_tobj, GLU_TESS_VERTEX_DATA, (_GLUfuncptr) &s52_vertexCallbackD_GLSL  );
12336             gluTessCallback( m_tobj, GLU_TESS_BEGIN_DATA, (_GLUfuncptr) &s52_beginCallbackD_GLSL  );
12337             gluTessCallback( m_tobj, GLU_TESS_END_DATA, (_GLUfuncptr) &s52_endCallbackD_GLSL  );
12338             gluTessCallback( m_tobj, GLU_TESS_COMBINE_DATA, (_GLUfuncptr) &s52_combineCallbackD );
12339 
12340             gluTessNormal( m_tobj, 0, 0, 1);
12341             gluTessProperty( m_tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
12342 
12343                 gluTessBeginPolygon( m_tobj, this );
12344                 gluTessBeginContour( m_tobj );
12345 
12346  //               ViewPort *pvp = cc1->GetpVP();
12347 
12348                 for( int i = 0; i < n; i++ ) {
12349                     double *p = new double[6];
12350 
12351 //                     if(fabs(pvp->rotation) > 0.01){
12352 //                         float cx = pvp->pix_width/2.;
12353 //                         float cy = pvp->pix_height/2.;
12354 //                         float c = cosf(pvp->rotation );
12355 //                         float s = sinf(pvp->rotation );
12356 //                         float xn = points[i].x - cx;
12357 //                         float yn = points[i].y - cy;
12358 //                         p[0] =  xn*c - yn*s + cx;
12359 //                         p[1] =  xn*s + yn*c + cy;
12360 //                         p[2] = 0;
12361 //                     }
12362 //                     else
12363                         p[0] = points[i].x, p[1] = points[i].y, p[2] = 0;
12364 
12365                     gluTessVertex(m_tobj, p, p);
12366                 }
12367 
12368                 gluTessEndContour( m_tobj );
12369                 gluTessEndPolygon( m_tobj );
12370             //}
12371 
12372             gluDeleteTess(m_tobj);
12373 
12374 
12375             //         for(std::list<double*>::iterator i = odc_combine_work_data.begin(); i!=odc_combine_work_data.end(); i++)
12376             //             delete [] *i;
12377             //         odc_combine_work_data.clear();
12378 
12379         }
12380  #else
12381         static GLUtesselator *tobj = NULL;
12382         if( ! tobj ) tobj = gluNewTess();
12383 
12384                         gluTessCallback( tobj, GLU_TESS_VERTEX, (_GLUfuncptr) &s52DCvertexCallback );
12385         gluTessCallback( tobj, GLU_TESS_BEGIN, (_GLUfuncptr) &s52DCbeginCallback );
12386         gluTessCallback( tobj, GLU_TESS_END, (_GLUfuncptr) &s52DCendCallback );
12387         gluTessCallback( tobj, GLU_TESS_COMBINE, (_GLUfuncptr) &s52DCcombineCallback );
12388         gluTessCallback( tobj, GLU_TESS_ERROR, (_GLUfuncptr) &s52DCerrorCallback );
12389 
12390         gluTessNormal( tobj, 0, 0, 1);
12391         gluTessProperty( tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
12392 
12393             wxColour c = brush->GetColour();
12394             glColor4ub( c.Red(), c.Green(), c.Blue(), c.Alpha() );
12395 
12396             gluTessBeginPolygon( tobj, NULL );
12397             gluTessBeginContour( tobj );
12398 
12399             for( int i = 0; i < n; i++ ) {
12400                 GLvertex* vertex = new GLvertex();
12401                 s52gTesselatorVertices.Add( vertex );
12402                 vertex->info.x = (GLdouble) points[i].x;
12403                 vertex->info.y = (GLdouble) points[i].y;
12404                 vertex->info.z = (GLdouble) 0.0;
12405                 vertex->info.r = (GLdouble) 0.0;
12406                 vertex->info.g = (GLdouble) 0.0;
12407                 vertex->info.b = (GLdouble) 0.0;
12408                 gluTessVertex( tobj, (GLdouble*)vertex, (GLdouble*)vertex );
12409             }
12410             gluTessEndContour( tobj );
12411             gluTessEndPolygon( tobj );
12412 
12413             for( unsigned int i=0; i<s52gTesselatorVertices.Count(); i++ )
12414                 delete (GLvertex*)s52gTesselatorVertices.Item(i);
12415             s52gTesselatorVertices.Clear();
12416 
12417         gluDeleteTess(tobj);
12418 
12419 }
12420 #endif
12421 #endif
12422 }
12423 
12424 
12425 
12426 
12427 #ifdef ocpnUSE_GL
12428 /* draw a half circle using triangles */
12429 void PLIBDrawEndCap(float x1, float y1, float t1, float angle)
12430 {
12431 #ifndef USE_ANDROID_GLES2
12432     const int steps = 16;
12433     float xa, ya;
12434     bool first = true;
12435     for(int i = 0; i <= steps; i++) {
12436         float a = angle + M_PI/2 + M_PI/steps*i;
12437 
12438         float xb = x1 + t1 / 2 * cos( a );
12439         float yb = y1 + t1 / 2 * sin( a );
12440         if(first)
12441             first = false;
12442         else {
12443             glVertex2f( x1, y1 );
12444             glVertex2f( xa, ya );
12445             glVertex2f( xb, yb );
12446         }
12447         xa = xb, ya = yb;
12448     }
12449 #endif
12450 }
12451 #endif
12452 
12453 // Draws a line between (x1,y1) - (x2,y2) with a start thickness of t1
12454 void PLIBDrawGLThickLine( float x1, float y1, float x2, float y2, wxPen pen, bool b_hiqual )
12455 {
12456 #ifdef ocpnUSE_GL
12457 #ifndef USE_ANDROID_GLES2
12458     float angle = atan2f( y2 - y1, x2 - x1 );
12459     float t1 = pen.GetWidth();
12460     float t2sina1 = t1 / 2 * sinf( angle );
12461     float t2cosa1 = t1 / 2 * cosf( angle );
12462 
12463     glBegin( GL_TRIANGLES );
12464 
12465     //    n.b.  The dwxDash interpretation for GL only allows for 2 elements in the dash table.
12466     //    The first is assumed drawn, second is assumed space
12467     wxDash *dashes;
12468     int n_dashes = pen.GetDashes( &dashes );
12469     if( n_dashes ) {
12470         float lpix = sqrtf( powf( (float) (x1 - x2), 2) + powf( (float) (y1 - y2), 2) );
12471         float lrun = 0.;
12472         float xa = x1;
12473         float ya = y1;
12474         float ldraw = t1 * (unsigned char)dashes[0];
12475         float lspace = t1 * (unsigned char)dashes[1];
12476 
12477         if((ldraw < 0) || (lspace < 0)){
12478             glEnd();
12479             return;
12480         }
12481 
12482         while( lrun < lpix ) {
12483             //    Dash
12484             float xb = xa + ldraw * cosf( angle );
12485             float yb = ya + ldraw * sinf( angle );
12486 
12487             if( ( lrun + ldraw ) >= lpix )         // last segment is partial draw
12488             {
12489                 xb = x2;
12490                 yb = y2;
12491             }
12492 
12493             glVertex2f( xa + t2sina1, ya - t2cosa1 );
12494             glVertex2f( xb + t2sina1, yb - t2cosa1 );
12495             glVertex2f( xb - t2sina1, yb + t2cosa1 );
12496 
12497             glVertex2f( xb - t2sina1, yb + t2cosa1 );
12498             glVertex2f( xa - t2sina1, ya + t2cosa1 );
12499             glVertex2f( xa + t2sina1, ya - t2cosa1 );
12500 
12501             xa = xb;
12502             ya = yb;
12503             lrun += ldraw;
12504 
12505             //    Space
12506             xb = xa + lspace * cos( angle );
12507             yb = ya + lspace * sin( angle );
12508 
12509             xa = xb;
12510             ya = yb;
12511             lrun += lspace;
12512         }
12513     } else {
12514         glVertex2f( x1 + t2sina1, y1 - t2cosa1 );
12515         glVertex2f( x2 + t2sina1, y2 - t2cosa1 );
12516         glVertex2f( x2 - t2sina1, y2 + t2cosa1 );
12517 
12518         glVertex2f( x2 - t2sina1, y2 + t2cosa1 );
12519         glVertex2f( x1 - t2sina1, y1 + t2cosa1 );
12520         glVertex2f( x1 + t2sina1, y1 - t2cosa1 );
12521 
12522         /* wx draws a nice rounded end in dc mode, so replicate
12523          *           this for opengl mode, should this be done for the dashed mode case? */
12524         if(pen.GetCap() == wxCAP_ROUND) {
12525             PLIBDrawEndCap( x1, y1, t1, angle);
12526             PLIBDrawEndCap( x2, y2, t1, angle + M_PI);
12527         }
12528 
12529     }
12530 
12531     glEnd();
12532 #endif
12533 #endif
12534 }
12535 
12536 #ifdef USE_ANDROID_GLES2
12537 
12538 #include <gl2.h>
12539 
12540 // Simple colored triangle shader
12541 
12542 static const GLchar* S52color_tri_vertex_shader_source =
12543     "attribute vec2 position;\n"
12544     "uniform mat4 MVMatrix;\n"
12545     "uniform mat4 TransformMatrix;\n"
12546     "uniform vec4 color;\n"
12547     "varying vec4 fragColor;\n"
12548     "void main() {\n"
12549     "   fragColor = color;\n"
12550     "   gl_Position = MVMatrix * TransformMatrix  * vec4(position, 0.0, 1.0);\n"
12551     "}\n";
12552 
12553     static const GLchar* S52color_tri_fragment_shader_source =
12554     "precision highp float;\n"
12555     "varying vec4 fragColor;\n"
12556     "void main() {\n"
12557     "   gl_FragColor = fragColor;\n"
12558     "}\n";
12559 
12560     // Simple 2D texture shader
12561 static const GLchar* S52texture_2D_vertex_shader_source =
12562     "attribute vec2 position;\n"
12563     "attribute vec2 aUV;\n"
12564     "uniform mat4 MVMatrix;\n"
12565     "uniform mat4 TransformMatrix;\n"
12566     "varying vec2 varCoord;\n"
12567     "void main() {\n"
12568     "   gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
12569     "   //varCoord = aUV.st;\n"
12570     "   varCoord = aUV;\n"
12571     "}\n";
12572 
12573 static const GLchar* S52texture_2D_fragment_shader_source =
12574     "precision highp float;\n"
12575     "uniform sampler2D uTex;\n"
12576     "varying vec2 varCoord;\n"
12577     "void main() {\n"
12578     "   gl_FragColor = texture2D(uTex, varCoord);\n"
12579     "}\n";
12580 
12581 
12582     // 2D texture shader with color modulation, used for colored text
12583 static const GLchar* S52texture_2D_ColorMod_vertex_shader_source =
12584     "precision highp float;\n"
12585     "attribute vec2 position;\n"
12586     "attribute vec2 aUV;\n"
12587     "uniform mat4 MVMatrix;\n"
12588     "uniform mat4 TransformMatrix;\n"
12589     "varying vec2 varCoord;\n"
12590     "void main() {\n"
12591     "   gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
12592     "   //varCoord = aUV.st;\n"
12593     "   varCoord = aUV;\n"
12594     "}\n";
12595 
12596 static const GLchar* S52texture_2D_ColorMod_fragment_shader_source =
12597     "precision highp float;\n"
12598     "uniform sampler2D uTex;\n"
12599     "uniform vec4 color;\n"
12600     "varying vec2 varCoord;\n"
12601     "void main() {\n"
12602     "   vec4 col=texture2D(uTex, varCoord);\n"
12603     "   gl_FragColor = color;\n"
12604     "   gl_FragColor.a = col.a;\n"
12605     "}\n";
12606 
12607 
12608     //  Circle shader
12609 
12610 static const GLchar* S52circle_filled_vertex_shader_source =
12611     "precision highp float;\n"
12612     "attribute vec2 aPos;\n"
12613     "uniform mat4 MVMatrix;\n"
12614     "uniform mat4 TransformMatrix;\n"
12615     "void main() {\n"
12616     "   gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n"
12617     "}\n";
12618 
12619 static const GLchar* S52circle_filled_fragment_shader_source =
12620     "precision highp float;\n"
12621     "uniform float border_width;\n"
12622     "uniform float circle_radius;\n"
12623     "uniform vec4 circle_color;\n"
12624     "uniform vec4 border_color;\n"
12625     "uniform vec2 circle_center;\n"
12626     "void main(){\n"
12627     "float d = distance(gl_FragCoord.xy, circle_center);\n"
12628     "if (d < (circle_radius - border_width)) { gl_FragColor = circle_color; }\n"
12629     "else if (d < circle_radius) { gl_FragColor = border_color; }\n"
12630     "else { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); }\n"
12631     "}\n";
12632 
12633     //  Ring shader
12634 
12635 static const GLchar* S52ring_vertex_shader_source =
12636     "precision highp float;\n"
12637     "attribute vec2 aPos;\n"
12638     "uniform mat4 MVMatrix;\n"
12639     "uniform mat4 TransformMatrix;\n"
12640     "void main() {\n"
12641     "   gl_Position = MVMatrix * TransformMatrix * vec4(aPos, 0.0, 1.0);\n"
12642     "}\n";
12643 
12644 static const GLchar* S52ring_fragment_shader_source =
12645     "precision highp float;\n"
12646     "uniform float border_width;\n"
12647     "uniform float circle_radius;\n"
12648     "uniform float ring_width;\n"
12649     "uniform vec4 circle_color;\n"
12650     "uniform vec4 border_color;\n"
12651     "uniform vec2 circle_center;\n"
12652     "uniform float sector_1;\n"
12653     "uniform float sector_2;\n"
12654 
12655     "void main(){\n"
12656     "const float PI = 3.14159265358979323846264;\n"
12657     "bool bdraw = false;\n"
12658 
12659     "float angle = atan(gl_FragCoord.y-circle_center.y, gl_FragCoord.x-circle_center.x);\n"
12660     "angle = PI/2.0 - angle;\n"
12661     "if(angle < 0.0) angle += PI * 2.0;\n"
12662 
12663     "if(sector_2 > PI * 2.0){\n"
12664     "    if((angle > sector_1) && (angle < (PI * 2.0) )){\n"
12665     "        bdraw = true;\n"
12666     "    }\n"
12667     "    if(angle < sector_2 - (PI * 2.0)){\n"
12668     "        bdraw = true;\n"
12669     "    }\n"
12670     "} else {\n"
12671     "    if((angle > sector_1) && (angle < sector_2)){\n"
12672     "        bdraw = true;\n"
12673     "    }\n"
12674     "}\n"
12675 
12676     "if(bdraw){\n"
12677     "   float d = distance(gl_FragCoord.xy, circle_center);\n"
12678     "   if (d > circle_radius) {\n"
12679     "       discard;\n"
12680     "   } else if( d > (circle_radius - border_width)) {\n"
12681     "       gl_FragColor = border_color;\n"
12682     "   } else if( d > (circle_radius - border_width - ring_width)) {\n"
12683     "       gl_FragColor = circle_color;\n"
12684     "   } else if( d > (circle_radius - border_width - ring_width - border_width)) {\n"
12685     "       gl_FragColor = border_color;\n"
12686     "   } else  {\n"
12687     "       discard;\n"
12688     "   }\n"
12689     "} else{\n"
12690     "   discard;\n"
12691     "}\n"
12692     "}\n";
12693 
12694 
12695     // Dash/Dot line shader
12696 static const GLchar* S52Dash_vertex_shader_source =
12697     "attribute vec2 position;\n"
12698     "uniform mat4 MVMatrix;\n"
12699     "uniform mat4 TransformMatrix;\n"
12700     "void main() {\n"
12701     "   gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
12702     "}\n";
12703 
12704 static const GLchar* S52Dash_fragment_shader_source =
12705     "precision highp float;\n"
12706     "uniform sampler2D uTex;\n"
12707     "uniform vec2 startPos;\n"
12708     "uniform float texWidth;\n"
12709     "uniform vec4 color;\n"
12710     "void main() {\n"
12711     "   float d = distance(gl_FragCoord.xy, startPos);\n"
12712     "   float x = mod(d,texWidth) / texWidth;\n"
12713     "   if(x < 0.5) gl_FragColor = color;\n"
12714     "   else gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n"
12715     "}\n";
12716 
12717 
12718     // Texture shader for Area Pattern
12719 static const GLchar* S52AP_vertex_shader_source =
12720     "attribute vec2 position;\n"
12721     "attribute vec2 aUV;\n"
12722     "uniform mat4 MVMatrix;\n"
12723     "uniform mat4 TransformMatrix;\n"
12724     "void main() {\n"
12725     "   gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
12726     "}\n";
12727 
12728 #if 0
12729 static const GLchar* S52AP_fragment_shader_source =
12730     "precision highp float;\n"
12731     "uniform sampler2D uTex;\n"
12732     "uniform float texWidth;\n"
12733     "uniform float texHeight;\n"
12734     "uniform float texPOTWidth;\n"
12735     "uniform float texPOTHeight;\n"
12736     "uniform float staggerFactor;\n"
12737     "uniform vec4 color;\n"
12738     "uniform float xOff;\n"
12739     "uniform float yOff;\n"
12740     "void main() {\n"
12741     "   float yp = floor((gl_FragCoord.y + yOff) / texHeight);\n"
12742     "   float fstagger = 0.0;\n"
12743     "   //if(mod(yp, 2.0) < 0.1) fstagger = staggerFactor;\n"
12744     "   float xStag = xOff + (fstagger * texWidth);\n"
12745     "   float x = mod((gl_FragCoord.x - xStag),texWidth) / texPOTWidth;\n"
12746     "   float y = mod((gl_FragCoord.y + yOff),texHeight) / texPOTHeight;\n"
12747      "   gl_FragColor = texture2D(uTex, vec2(x, (texHeight / texPOTHeight) - y));\n"
12748     "}\n";
12749 #else
12750 
12751 static const GLchar* S52AP_fragment_shader_source =
12752     "precision highp float;\n"
12753     "uniform sampler2D uTex;\n"
12754     "uniform float texWidth;\n"
12755     "uniform float texHeight;\n"
12756     "uniform float texPOTWidth;\n"
12757     "uniform float texPOTHeight;\n"
12758     "uniform float staggerFactor;\n"
12759     "uniform vec4 color;\n"
12760     "uniform float xOff;\n"
12761     "uniform float yOff;\n"
12762     "uniform float yOffM;\n"
12763     "void main() {\n"
12764     "   float yp = floor((gl_FragCoord.y + yOffM ) / texPOTHeight);\n"
12765     "   float fstagger = 0.0;\n"
12766     "   if(mod(yp, 2.0) < 0.1) fstagger = 0.5;\n"
12767     "   float x = (gl_FragCoord.x - xOff) / texPOTWidth;\n"
12768     "   float y = (gl_FragCoord.y + yOff) / texPOTHeight;\n"
12769     "   gl_FragColor = texture2D(uTex, vec2(x + fstagger, y));\n"
12770     "}\n";
12771 #endif
12772 
12773     GLint S52color_tri_fragment_shader;
12774     GLint S52color_tri_vertex_shader;
12775 
12776     GLint S52texture_2D_fragment_shader;
12777     GLint S52texture_2D_vertex_shader;
12778 
12779     GLint S52texture_2D_ColorMod_fragment_shader;
12780     GLint S52texture_2D_ColorMod_vertex_shader;
12781 
12782     GLint S52circle_filled_vertex_shader;
12783     GLint S52circle_filled_fragment_shader;
12784 
12785     GLint S52ring_vertex_shader;
12786     GLint S52ring_fragment_shader;
12787 
12788     GLint S52Dash_vertex_shader;
12789     GLint S52Dash_fragment_shader;
12790 
12791     GLint S52AP_vertex_shader;
12792     GLint S52AP_fragment_shader;
12793 
12794 bool loadS52Shaders()
12795 {
12796 
12797     bool ret_val = true;
12798     GLint success;
12799 
12800     enum Consts {INFOLOG_LEN = 512};
12801     GLchar infoLog[INFOLOG_LEN];
12802 
12803     // Are the shaders ready?
12804 
12805     // Simple colored triangle shader
12806 
12807     if(!S52color_tri_vertex_shader){
12808        /* Vertex shader */
12809        S52color_tri_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
12810        glShaderSource(S52color_tri_vertex_shader, 1, &S52color_tri_vertex_shader_source, NULL);
12811        glCompileShader(S52color_tri_vertex_shader);
12812        glGetShaderiv(S52color_tri_vertex_shader, GL_COMPILE_STATUS, &success);
12813        if (!success) {
12814           glGetShaderInfoLog(S52color_tri_vertex_shader, INFOLOG_LEN, NULL, infoLog);
12815 //        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
12816         ret_val = false;
12817       }
12818     }
12819 
12820     if(!S52color_tri_fragment_shader){
12821         /* Fragment shader */
12822         S52color_tri_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
12823         glShaderSource(S52color_tri_fragment_shader, 1, &S52color_tri_fragment_shader_source, NULL);
12824         glCompileShader(S52color_tri_fragment_shader);
12825         glGetShaderiv(S52color_tri_fragment_shader, GL_COMPILE_STATUS, &success);
12826       if (!success) {
12827           glGetShaderInfoLog(S52color_tri_fragment_shader, INFOLOG_LEN, NULL, infoLog);
12828 //        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
12829         ret_val = false;
12830      }
12831     }
12832 
12833     if(!S52color_tri_shader_program){
12834       /* Link shaders */
12835       S52color_tri_shader_program = glCreateProgram();
12836       glAttachShader(S52color_tri_shader_program, S52color_tri_fragment_shader);
12837       glAttachShader(S52color_tri_shader_program, S52color_tri_vertex_shader);
12838       glLinkProgram(S52color_tri_shader_program);
12839       glGetProgramiv(S52color_tri_shader_program, GL_LINK_STATUS, &success);
12840       if (!success) {
12841           glGetProgramInfoLog(S52color_tri_shader_program, INFOLOG_LEN, NULL, infoLog);
12842 //        printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
12843         ret_val = false;
12844       }
12845     }
12846 
12847 
12848         // Simple 2D texture shader
12849 
12850     if(!S52texture_2D_vertex_shader){
12851        /* Vertex shader */
12852        S52texture_2D_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
12853        glShaderSource(S52texture_2D_vertex_shader, 1, &S52texture_2D_vertex_shader_source, NULL);
12854        glCompileShader(S52texture_2D_vertex_shader);
12855        glGetShaderiv(S52texture_2D_vertex_shader, GL_COMPILE_STATUS, &success);
12856       if (!success) {
12857           glGetShaderInfoLog(S52texture_2D_vertex_shader, INFOLOG_LEN, NULL, infoLog);
12858 //        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
12859         ret_val = false;
12860       }
12861     }
12862 
12863     if(!S52texture_2D_fragment_shader){
12864         /* Fragment shader */
12865         S52texture_2D_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
12866         glShaderSource(S52texture_2D_fragment_shader, 1, &S52texture_2D_fragment_shader_source, NULL);
12867         glCompileShader(S52texture_2D_fragment_shader);
12868         glGetShaderiv(S52texture_2D_fragment_shader, GL_COMPILE_STATUS, &success);
12869       if (!success) {
12870           glGetShaderInfoLog(S52texture_2D_fragment_shader, INFOLOG_LEN, NULL, infoLog);
12871 //        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
12872         ret_val = false;
12873       }
12874 }
12875 
12876     if(!S52texture_2D_shader_program){
12877       /* Link shaders */
12878       S52texture_2D_shader_program = glCreateProgram();
12879       glAttachShader(S52texture_2D_shader_program, S52texture_2D_vertex_shader);
12880       glAttachShader(S52texture_2D_shader_program, S52texture_2D_fragment_shader);
12881       glLinkProgram(S52texture_2D_shader_program);
12882       glGetProgramiv(S52texture_2D_shader_program, GL_LINK_STATUS, &success);
12883       if (!success) {
12884           glGetProgramInfoLog(S52texture_2D_shader_program, INFOLOG_LEN, NULL, infoLog);
12885 //        printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
12886         ret_val = false;
12887       }
12888     }
12889 
12890     // 2D texture shader with color Modulate
12891 
12892     if(!S52texture_2D_ColorMod_vertex_shader){
12893        /* Vertex shader */
12894        S52texture_2D_ColorMod_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
12895        glShaderSource(S52texture_2D_ColorMod_vertex_shader, 1, &S52texture_2D_ColorMod_vertex_shader_source, NULL);
12896        glCompileShader(S52texture_2D_ColorMod_vertex_shader);
12897        glGetShaderiv(S52texture_2D_ColorMod_vertex_shader, GL_COMPILE_STATUS, &success);
12898       if (!success) {
12899           glGetShaderInfoLog(S52texture_2D_ColorMod_vertex_shader, INFOLOG_LEN, NULL, infoLog);
12900 //        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
12901         ret_val = false;
12902       }
12903     }
12904 
12905     if(!S52texture_2D_ColorMod_fragment_shader){
12906         /* Fragment shader */
12907         S52texture_2D_ColorMod_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
12908         glShaderSource(S52texture_2D_ColorMod_fragment_shader, 1, &S52texture_2D_ColorMod_fragment_shader_source, NULL);
12909         glCompileShader(S52texture_2D_ColorMod_fragment_shader);
12910         glGetShaderiv(S52texture_2D_ColorMod_fragment_shader, GL_COMPILE_STATUS, &success);
12911         if (!success) {
12912           glGetShaderInfoLog(S52texture_2D_ColorMod_fragment_shader, INFOLOG_LEN, NULL, infoLog);
12913 //        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
12914         ret_val = false;
12915       }
12916     }
12917 
12918     if(!S52texture_2D_ColorMod_shader_program){
12919       /* Link shaders */
12920       S52texture_2D_ColorMod_shader_program = glCreateProgram();
12921       glAttachShader(S52texture_2D_ColorMod_shader_program, S52texture_2D_ColorMod_vertex_shader);
12922       glAttachShader(S52texture_2D_ColorMod_shader_program, S52texture_2D_ColorMod_fragment_shader);
12923       glLinkProgram(S52texture_2D_ColorMod_shader_program);
12924       glGetProgramiv(S52texture_2D_ColorMod_shader_program, GL_LINK_STATUS, &success);
12925       if (!success) {
12926           glGetProgramInfoLog(S52texture_2D_ColorMod_shader_program, INFOLOG_LEN, NULL, infoLog);
12927 //        printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
12928         ret_val = false;
12929       }
12930     }
12931 
12932 
12933 
12934 
12935     // Circle shader
12936     if(!S52circle_filled_vertex_shader){
12937         /* Vertex shader */
12938         S52circle_filled_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
12939         glShaderSource(S52circle_filled_vertex_shader, 1, &S52circle_filled_vertex_shader_source, NULL);
12940         glCompileShader(S52circle_filled_vertex_shader);
12941         glGetShaderiv(S52circle_filled_vertex_shader, GL_COMPILE_STATUS, &success);
12942         if (!success) {
12943             glGetShaderInfoLog(S52circle_filled_vertex_shader, INFOLOG_LEN, NULL, infoLog);
12944 //            printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
12945             ret_val = false;
12946         }
12947     }
12948 
12949     if(!S52circle_filled_fragment_shader){
12950         /* Fragment shader */
12951         S52circle_filled_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
12952         glShaderSource(S52circle_filled_fragment_shader, 1, &S52circle_filled_fragment_shader_source, NULL);
12953         glCompileShader(S52circle_filled_fragment_shader);
12954         glGetShaderiv(S52circle_filled_fragment_shader, GL_COMPILE_STATUS, &success);
12955         if (!success) {
12956             glGetShaderInfoLog(S52circle_filled_fragment_shader, INFOLOG_LEN, NULL, infoLog);
12957 //            printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
12958             ret_val = false;
12959         }
12960     }
12961 
12962     if(!S52circle_filled_shader_program){
12963         /* Link shaders */
12964         S52circle_filled_shader_program = glCreateProgram();
12965         glAttachShader(S52circle_filled_shader_program, S52circle_filled_vertex_shader);
12966         glAttachShader(S52circle_filled_shader_program, S52circle_filled_fragment_shader);
12967         glLinkProgram(S52circle_filled_shader_program);
12968         glGetProgramiv(S52circle_filled_shader_program, GL_LINK_STATUS, &success);
12969         if (!success) {
12970             glGetProgramInfoLog(S52circle_filled_shader_program, INFOLOG_LEN, NULL, infoLog);
12971 //            printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
12972             ret_val = false;
12973         }
12974     }
12975 
12976 
12977     // Ring shader
12978     if(!S52ring_vertex_shader){
12979         /* Vertex shader */
12980         S52ring_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
12981         glShaderSource(S52ring_vertex_shader, 1, &S52ring_vertex_shader_source, NULL);
12982         glCompileShader(S52ring_vertex_shader);
12983         glGetShaderiv(S52ring_vertex_shader, GL_COMPILE_STATUS, &success);
12984         if (!success) {
12985             glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog);
12986  //           printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
12987             ret_val = false;
12988         }
12989     }
12990 
12991     if(!S52ring_fragment_shader){
12992         /* Fragment shader */
12993         S52ring_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
12994         glShaderSource(S52ring_fragment_shader, 1, &S52ring_fragment_shader_source, NULL);
12995         glCompileShader(S52ring_fragment_shader);
12996         glGetShaderiv(S52ring_fragment_shader, GL_COMPILE_STATUS, &success);
12997         if (!success) {
12998             glGetShaderInfoLog(S52ring_fragment_shader, INFOLOG_LEN, NULL, infoLog);
12999 //            printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
13000             ret_val = false;
13001         }
13002     }
13003 
13004     if(!S52ring_shader_program){
13005         /* Link shaders */
13006         S52ring_shader_program = glCreateProgram();
13007         glAttachShader(S52ring_shader_program, S52ring_vertex_shader);
13008         glAttachShader(S52ring_shader_program, S52ring_fragment_shader);
13009         glLinkProgram(S52ring_shader_program);
13010         glGetProgramiv(S52ring_shader_program, GL_LINK_STATUS, &success);
13011         if (!success) {
13012             glGetProgramInfoLog(S52ring_shader_program, INFOLOG_LEN, NULL, infoLog);
13013 //            printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
13014             ret_val = false;
13015         }
13016     }
13017 
13018     // Dash shader
13019     if(!S52Dash_vertex_shader){
13020         /* Vertex shader */
13021         S52Dash_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
13022         glShaderSource(S52Dash_vertex_shader, 1, &S52Dash_vertex_shader_source, NULL);
13023         glCompileShader(S52Dash_vertex_shader);
13024         glGetShaderiv(S52Dash_vertex_shader, GL_COMPILE_STATUS, &success);
13025         if (!success) {
13026             glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog);
13027 //            printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
13028             ret_val = false;
13029         }
13030     }
13031 
13032     if(!S52Dash_fragment_shader){
13033         /* Fragment shader */
13034         S52Dash_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
13035         glShaderSource(S52Dash_fragment_shader, 1, &S52Dash_fragment_shader_source, NULL);
13036         glCompileShader(S52Dash_fragment_shader);
13037         glGetShaderiv(S52Dash_fragment_shader, GL_COMPILE_STATUS, &success);
13038         if (!success) {
13039             glGetShaderInfoLog(S52Dash_fragment_shader, INFOLOG_LEN, NULL, infoLog);
13040 //            printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
13041             ret_val = false;
13042         }
13043     }
13044 
13045     if(!S52Dash_shader_program){
13046         /* Link shaders */
13047         S52Dash_shader_program = glCreateProgram();
13048         glAttachShader(S52Dash_shader_program, S52Dash_vertex_shader);
13049         glAttachShader(S52Dash_shader_program, S52Dash_fragment_shader);
13050         glLinkProgram(S52Dash_shader_program);
13051         glGetProgramiv(S52Dash_shader_program, GL_LINK_STATUS, &success);
13052         if (!success) {
13053             glGetProgramInfoLog(S52Dash_shader_program, INFOLOG_LEN, NULL, infoLog);
13054 //            printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
13055             ret_val = false;
13056         }
13057     }
13058 
13059 
13060     // AP shader
13061     if(!S52AP_vertex_shader){
13062         /* Vertex shader */
13063         S52AP_vertex_shader = glCreateShader(GL_VERTEX_SHADER);
13064         glShaderSource(S52AP_vertex_shader, 1, &S52AP_vertex_shader_source, NULL);
13065         glCompileShader(S52AP_vertex_shader);
13066         glGetShaderiv(S52AP_vertex_shader, GL_COMPILE_STATUS, &success);
13067         if (!success) {
13068             glGetShaderInfoLog(S52ring_vertex_shader, INFOLOG_LEN, NULL, infoLog);
13069 //            printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
13070             ret_val = false;
13071         }
13072     }
13073 
13074     if(!S52AP_fragment_shader){
13075         /* Fragment shader */
13076         S52AP_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
13077         glShaderSource(S52AP_fragment_shader, 1, &S52AP_fragment_shader_source, NULL);
13078         glCompileShader(S52AP_fragment_shader);
13079         glGetShaderiv(S52AP_fragment_shader, GL_COMPILE_STATUS, &success);
13080         if (!success) {
13081             glGetShaderInfoLog(S52AP_fragment_shader, INFOLOG_LEN, NULL, infoLog);
13082   //          printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
13083             ret_val = false;
13084         }
13085     }
13086 
13087     if(!S52AP_shader_program){
13088         /* Link shaders */
13089         S52AP_shader_program = glCreateProgram();
13090         glAttachShader(S52AP_shader_program, S52AP_vertex_shader);
13091         glAttachShader(S52AP_shader_program, S52AP_fragment_shader);
13092         glLinkProgram(S52AP_shader_program);
13093         glGetProgramiv(S52AP_shader_program, GL_LINK_STATUS, &success);
13094         if (!success) {
13095             glGetProgramInfoLog(S52AP_shader_program, INFOLOG_LEN, NULL, infoLog);
13096 //            printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
13097             ret_val = false;
13098         }
13099     }
13100 
13101     return ret_val;
13102 }
13103 
13104 
13105 void PrepareS52ShaderUniforms(ViewPort *vp)
13106 {
13107     mat4x4 m;
13108     float    vp_transform[16];
13109     mat4x4_identity(m);
13110     mat4x4_scale_aniso((float (*)[4])vp_transform, m, 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height, 1.0);
13111     //Rotate
13112     mat4x4 Q;
13113     mat4x4_rotate_Z(Q, (float (*)[4])vp_transform, vp->rotation);
13114     mat4x4_translate_in_place(Q, -vp->pix_width / 2.0, -vp->pix_height / 2.0, 0);
13115 
13116 
13117     mat4x4 I;
13118     mat4x4_identity(I);
13119 
13120     glUseProgram(S52color_tri_shader_program);
13121     GLint matloc = glGetUniformLocation(S52color_tri_shader_program,"MVMatrix");
13122     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13123     GLint transloc = glGetUniformLocation(S52color_tri_shader_program,"TransformMatrix");
13124     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13125 
13126     glUseProgram(S52texture_2D_shader_program);
13127     matloc = glGetUniformLocation(S52texture_2D_shader_program,"MVMatrix");
13128     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13129     transloc = glGetUniformLocation(S52texture_2D_shader_program,"TransformMatrix");
13130     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13131 
13132     glUseProgram(S52texture_2D_ColorMod_shader_program);
13133     matloc = glGetUniformLocation(S52texture_2D_ColorMod_shader_program,"MVMatrix");
13134     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13135     transloc = glGetUniformLocation(S52texture_2D_ColorMod_shader_program,"TransformMatrix");
13136     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13137 
13138     glUseProgram(S52circle_filled_shader_program);
13139     matloc = glGetUniformLocation(S52circle_filled_shader_program,"MVMatrix");
13140     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13141     transloc = glGetUniformLocation(S52circle_filled_shader_program,"TransformMatrix");
13142     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13143 
13144     glUseProgram(S52ring_shader_program);
13145     matloc = glGetUniformLocation(S52ring_shader_program,"MVMatrix");
13146     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13147     transloc = glGetUniformLocation(S52ring_shader_program,"TransformMatrix");
13148     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13149 
13150     glUseProgram(S52Dash_shader_program);
13151     matloc = glGetUniformLocation(S52Dash_shader_program,"MVMatrix");
13152     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13153     transloc = glGetUniformLocation(S52Dash_shader_program,"TransformMatrix");
13154     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13155 
13156     glUseProgram(S52AP_shader_program);
13157     matloc = glGetUniformLocation(S52AP_shader_program,"MVMatrix");
13158     glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
13159     transloc = glGetUniformLocation(S52AP_shader_program,"TransformMatrix");
13160     glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
13161 }
13162 
13163 #endif
13164