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( §r1 );
6397 carc_hash += slong;
6398 carc_hash += _T(".");
6399
6400 // sectr2
6401 slong = tkz.GetNextToken();
6402 double sectr2;
6403 slong.ToDouble( §r2 );
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( §or_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( §r1 );
6656 carc_hash += slong;
6657 carc_hash += _T(".");
6658
6659 // sectr2
6660 slong = tkz.GetNextToken();
6661 double sectr2;
6662 slong.ToDouble( §r2 );
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( §or_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