OpenCPN Partial API docs
Loading...
Searching...
No Matches
waypointman_gui.cpp
1
2/***************************************************************************
3 *
4 * Project: OpenCPN
5 * Purpose: implement waypointman_gui.h: WayPointman drawing stuff
6 * Author: David Register, Alec Leamas
7 *
8 ***************************************************************************
9 * Copyright (C) 2022 by David Register, Alec Leamas *
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 ******************A********************************************************/
26
27#if defined(__ANDROID__)
28#include <qopengl.h>
29#include <GL/gl_private.h> // this is a cut-down version of gl.h
30#include <GLES2/gl2.h>
31
32#elif defined(ocpnUSE_GL)
33
34#if defined(__MSVC__)
35#include "glew.h"
36#include <GL/glu.h>
37
38#elif defined(__WXOSX__)
39#include <OpenGL/gl.h>
40#include <OpenGL/glu.h>
41typedef void (*_GLUfuncptr)();
42#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
43
44#elif defined(__WXQT__) || defined(__WXGTK__)
45#include <GL/glew.h>
46#include <GL/glu.h>
47#endif // ocpnUSE_GL
48#endif
49
50#include <wx/arrstr.h>
51#include <wx/bitmap.h>
52#include <wx/dir.h>
53#include <wx/filename.h>
54#include <wx/gdicmn.h>
55#include <wx/log.h>
56#include <wx/string.h>
57#include <wx/utils.h>
58
59#include "model/base_platform.h"
60#include "model/cutil.h"
61#include "model/MarkIcon.h"
62#include "model/route_point.h"
63#include "styles.h"
64#include "model/svg_utils.h"
65#include "waypointman_gui.h"
66#include "ocpn_plugin.h"
67
68extern BasePlatform *g_BasePlatform;
69extern float g_MarkScaleFactorExp;
70extern ocpnStyle::StyleManager *g_StyleManager;
71extern bool g_bUserIconsFirst;
72
73static int CompareMarkIcons(MarkIcon *mi1, MarkIcon *mi2) {
74 return (mi1->icon_name.CmpNoCase(mi2->icon_name));
75}
76
77void WayPointmanGui::ProcessUserIcons(ocpnStyle::Style *style,
78 double displayDPmm) {
79 wxString msg;
80 msg.Printf(_T("DPMM: %g ScaleFactorExp: %g"), displayDPmm,
81 g_MarkScaleFactorExp);
82 wxLogMessage(msg);
83
84 wxString UserIconPath = g_BasePlatform->GetPrivateDataDir();
85 wxChar sep = wxFileName::GetPathSeparator();
86 if (UserIconPath.Last() != sep) UserIconPath.Append(sep);
87 UserIconPath.Append(_T("UserIcons/"));
88
89 wxLogMessage(_T("Looking for UserIcons at ") + UserIconPath);
90
91 if (wxDir::Exists(UserIconPath)) {
92 wxLogMessage(_T("Loading UserIcons from ") + UserIconPath);
93 wxArrayString FileList;
94
95 wxBitmap default_bm = wxBitmap(1, 1); // empty
96
97 int n_files =
98 wxDir::GetAllFiles(UserIconPath, &FileList, _T(""), wxDIR_FILES);
99
100 for (int ifile = 0; ifile < n_files; ifile++) {
101 wxString name =
102 g_bUserIconsFirst ? FileList[n_files - ifile - 1] : FileList[ifile];
103
104 wxFileName fn(name);
105 wxString iconname = fn.GetName();
106 wxBitmap icon1;
107 if (fn.GetExt().Lower() == _T("xpm")) {
108 if (icon1.LoadFile(name, wxBITMAP_TYPE_XPM)) {
109 wxLogMessage(_T("Adding icon: ") + iconname);
110 wxImage image = icon1.ConvertToImage();
111 ProcessIcon(image, iconname, iconname, g_bUserIconsFirst);
112 }
113 }
114 if (fn.GetExt().Lower() == _T("png")) {
115 if (icon1.LoadFile(name, wxBITMAP_TYPE_PNG)) {
116 wxLogMessage(_T("Adding icon: ") + iconname);
117 wxImage image = icon1.ConvertToImage();
118 ProcessIcon(image, iconname, iconname, g_bUserIconsFirst);
119 }
120 }
121 if (fn.GetExt().Lower() == _T("svg")) {
122 unsigned int w, h;
123 SVGDocumentPixelSize(name, w, h);
124
125 // This is to be a mark icon
126 // Make it a nominal max size
127 double bm_size_nom = wxMin(wxMax(w, h), floor(displayDPmm * 20));
128 // We want certain minimal size for the icons, 15px (approx 3mm) be it
129 bm_size_nom = wxMax(bm_size_nom, 15);
130
131 bm_size_nom /= OCPN_GetWinDIPScaleFactor();
132 bm_size_nom *= g_MarkScaleFactorExp;
133
134 MarkIcon *pmi = NULL;
135 double aspect =
136 1.0; // Use default aspect ratio of 1 if width/height are missing.
137 if (w != 0 && h != 0) {
138 aspect = h / w;
139 }
140
141 // Make the rendered icon square, if necessary
142 if (fabs(aspect - 1.0) > .05) {
143 wxImage image =
144 LoadSVG(name, (int)bm_size_nom, (int)bm_size_nom, &default_bm)
145 .ConvertToImage();
146
147 if (image.IsOk()) {
148 wxRect rClip = CropImageOnAlpha(image);
149 wxImage imageClip = image.GetSubImage(rClip);
150 imageClip.Rescale(bm_size_nom, bm_size_nom / aspect,
151 wxIMAGE_QUALITY_BICUBIC);
152 pmi = ProcessIcon(imageClip, iconname, iconname, g_bUserIconsFirst);
153 }
154 } else {
155 const unsigned int bm_size = bm_size_nom; // horizontal
156 wxImage iconSVG = LoadSVG(name, bm_size, bm_size, &default_bm, false)
157 .ConvertToImage();
158 wxRect rClip = CropImageOnAlpha(iconSVG);
159 wxImage imageClip = iconSVG.GetSubImage(rClip);
160 pmi = ProcessIcon(iconSVG, iconname, iconname, g_bUserIconsFirst);
161 }
162
163 if (pmi) pmi->preScaled = true;
164 }
165 }
166 }
167}
168
169MarkIcon *WayPointmanGui::ProcessIcon(wxImage image, const wxString &key,
170 const wxString &description,
171 bool add_in_front) {
172 MarkIcon *pmi = 0;
173
174 bool newIcon = true;
175
176 // avoid adding duplicates
177 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
178 pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
179 if (pmi->icon_name.IsSameAs(key)) {
180 newIcon = false;
181 delete pmi->piconBitmap;
182 break;
183 }
184 }
185
186 if (newIcon) {
187 pmi = new MarkIcon;
188 pmi->icon_name = key; // Used for sorting
189 if (add_in_front)
190 m_waypoint_man.m_pIconArray->Insert(pmi, 0);
191 else {
192 m_waypoint_man.m_pIconArray->Add(pmi);
193 }
194 }
195
196 wxBitmap *pbm = new wxBitmap(image);
197 pmi->icon_name = key;
198 pmi->icon_description = description;
199 pmi->piconBitmap = NULL;
200 pmi->icon_texture = 0; /* invalidate */
201 pmi->preScaled = false;
202 pmi->iconImage = pbm->ConvertToImage();
203 pmi->m_blistImageOK = false;
204 delete pbm;
205
206 return pmi;
207}
208
209void WayPointmanGui::ProcessIcons(ocpnStyle::Style *style, double displayDPmm) {
210 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
211 MarkIcon *pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
212 delete pmi->piconBitmap;
213 delete pmi;
214 }
215 m_waypoint_man.m_pIconArray->Clear();
216
217 ProcessDefaultIcons(displayDPmm);
218
219 // Load user defined icons.
220 // Done after default icons are initialized,
221 // so that user may substitute an icon by using the same name in the Usericons
222 // file.
223 ProcessUserIcons(style, displayDPmm);
224
225 if (NULL != m_waypoint_man.pmarkicon_image_list) {
226 m_waypoint_man.pmarkicon_image_list->RemoveAll();
227 delete m_waypoint_man.pmarkicon_image_list;
228 m_waypoint_man.pmarkicon_image_list = NULL;
229 }
230
231 // First find the largest bitmap size, to use as the base size for lists of
232 // icons
233 int w = 0;
234 int h = 0;
235
236 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
237 MarkIcon *pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
238 w = wxMax(w, pmi->iconImage.GetWidth());
239 h = wxMax(h, pmi->iconImage.GetHeight());
240 }
241
242 m_waypoint_man.m_bitmapSizeForList = wxMax(w, h);
243 m_waypoint_man.m_bitmapSizeForList =
244 wxMin(100, m_waypoint_man.m_bitmapSizeForList);
245}
246
247void WayPointmanGui::ProcessDefaultIcons(double displayDPmm) {
248 wxString iconDir = g_BasePlatform->GetSharedDataDir();
249 appendOSDirSlash(&iconDir);
250 iconDir.append(_T("uidata"));
251 appendOSDirSlash(&iconDir);
252 iconDir.append(_T("markicons"));
253 appendOSDirSlash(&iconDir);
254
255 MarkIcon *pmi = 0;
256
257 // Add the legacy icons to their own sorted array
258 if (m_waypoint_man.m_pLegacyIconArray)
259 m_waypoint_man.m_pLegacyIconArray->Clear();
260 else
261 m_waypoint_man.m_pLegacyIconArray =
262 new SortedArrayOfMarkIcon(CompareMarkIcons);
263
264 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Empty.svg"), _T("empty"),
265 _T("Empty"), displayDPmm);
266 if (pmi) pmi->preScaled = true;
267 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Triangle.svg"), _T("triangle"),
268 _T("Triangle"), displayDPmm);
269 if (pmi) pmi->preScaled = true;
270 pmi = ProcessLegacyIcon(iconDir + _T("1st-Active-Waypoint.svg"),
271 _T("activepoint"), _T("Active WP"), displayDPmm);
272 if (pmi) pmi->preScaled = true;
273 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Boarding-Location.svg"),
274 _T("boarding"), _T("Boarding Location"), displayDPmm);
275 if (pmi) pmi->preScaled = true;
276 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Airplane.svg"), _T("airplane"),
277 _T("Airplane"), displayDPmm);
278 if (pmi) pmi->preScaled = true;
279 pmi = ProcessLegacyIcon(iconDir + _T("1st-Anchorage.svg"), _T("anchorage"),
280 _T("Anchorage"), displayDPmm);
281 if (pmi) pmi->preScaled = true;
282 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Anchor2.svg"), _T("anchor"),
283 _T("Anchor"), displayDPmm);
284 if (pmi) pmi->preScaled = true;
285 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Boundary.svg"), _T("boundary"),
286 _T("Boundary Mark"), displayDPmm);
287 if (pmi) pmi->preScaled = true;
288 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Buoy-TypeA.svg"), _T("buoy1"),
289 _T("buoy Type A"), displayDPmm);
290 if (pmi) pmi->preScaled = true;
291 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Buoy-TypeB.svg"), _T("buoy2"),
292 _T("buoy Type B"), displayDPmm);
293 if (pmi) pmi->preScaled = true;
294 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Campfire.svg"), _T("campfire"),
295 _T("Campfire"), displayDPmm);
296 if (pmi) pmi->preScaled = true;
297 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Camping.svg"), _T("camping"),
298 _T("Camping Spot"), displayDPmm);
299 if (pmi) pmi->preScaled = true;
300 pmi = ProcessLegacyIcon(iconDir + _T("Sea-Floor-Coral.svg"), _T("coral"),
301 _T("Coral"), displayDPmm);
302 if (pmi) pmi->preScaled = true;
303 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Fishing.svg"), _T("fishhaven"),
304 _T("Fish Haven"), displayDPmm);
305 if (pmi) pmi->preScaled = true;
306 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Fishing.svg"), _T("fishing"),
307 _T("Fishing Spot"), displayDPmm);
308 if (pmi) pmi->preScaled = true;
309 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Fishing.svg"), _T("fish"),
310 _T("Fish"), displayDPmm);
311 if (pmi) pmi->preScaled = true;
312 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Mooring-Buoy.svg"), _T("float"),
313 _T("Float"), displayDPmm);
314 if (pmi) pmi->preScaled = true;
315 pmi = ProcessLegacyIcon(iconDir + _T("Service-Food.svg"), _T("food"),
316 _T("Food"), displayDPmm);
317 if (pmi) pmi->preScaled = true;
318 pmi = ProcessLegacyIcon(iconDir + _T("Service-Fuel-Pump-Diesel-Petrol.svg"),
319 _T("fuel"), _T("Fuel"), displayDPmm);
320 if (pmi) pmi->preScaled = true;
321 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Green.svg"),
322 _T("greenlite"), _T("Green Light"), displayDPmm);
323 if (pmi) pmi->preScaled = true;
324 pmi = ProcessLegacyIcon(iconDir + _T("Sea-Floor-Sea-Weed.svg"), _T("kelp"),
325 _T("Kelp"), displayDPmm);
326 if (pmi) pmi->preScaled = true;
327 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-TypeA.svg"), _T("light"),
328 _T("Light Type A"), displayDPmm);
329 if (pmi) pmi->preScaled = true;
330 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-TypeB.svg"), _T("light1"),
331 _T("Light Type B"), displayDPmm);
332 if (pmi) pmi->preScaled = true;
333 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Vessel.svg"),
334 _T("litevessel"), _T("litevessel"), displayDPmm);
335 if (pmi) pmi->preScaled = true;
336 pmi = ProcessLegacyIcon(iconDir + _T("1st-Man-Overboard.svg"), _T("mob"),
337 _T("MOB"), displayDPmm);
338 if (pmi) pmi->preScaled = true;
339 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Mooring-Buoy.svg"), _T("mooring"),
340 _T("Mooring buoy"), displayDPmm);
341 if (pmi) pmi->preScaled = true;
342 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Mooring-Buoy-Super.svg"),
343 _T("oilbuoy"), _T("Oil buoy"), displayDPmm);
344 if (pmi) pmi->preScaled = true;
345 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Oil-Platform.svg"),
346 _T("platform"), _T("Platform"), displayDPmm);
347 if (pmi) pmi->preScaled = true;
348 pmi =
349 ProcessLegacyIcon(iconDir + _T("Marks-Light-Red-Green.svg"),
350 _T("redgreenlite"), _T("Red/Green Light"), displayDPmm);
351 if (pmi) pmi->preScaled = true;
352 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Red.svg"), _T("redlite"),
353 _T("Red Light"), displayDPmm);
354 if (pmi) pmi->preScaled = true;
355 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Rock-Exposed.svg"), _T("rock1"),
356 _T("Rock (exposed)"), displayDPmm);
357 if (pmi) pmi->preScaled = true;
358 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Rock-Awash.svg"), _T("rock2"),
359 _T("Rock, (awash)"), displayDPmm);
360 if (pmi) pmi->preScaled = true;
361 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Sandbar.svg"), _T("sand"),
362 _T("Sand"), displayDPmm);
363 if (pmi) pmi->preScaled = true;
364 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Diving-Scuba-Flag.svg"),
365 _T("scuba"), _T("Scuba"), displayDPmm);
366 if (pmi) pmi->preScaled = true;
367 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Sandbar.svg"), _T("shoal"),
368 _T("Shoal"), displayDPmm);
369 if (pmi) pmi->preScaled = true;
370 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Snag.svg"), _T("snag"),
371 _T("Snag"), displayDPmm);
372 if (pmi) pmi->preScaled = true;
373 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Square.svg"), _T("square"),
374 _T("Square"), displayDPmm);
375 if (pmi) pmi->preScaled = true;
376 pmi = ProcessLegacyIcon(iconDir + _T("1st-Diamond.svg"), _T("diamond"),
377 _T("Diamond"), displayDPmm);
378 if (pmi) pmi->preScaled = true;
379 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Circle.svg"), _T("circle"),
380 _T("Circle"), displayDPmm);
381 if (pmi) pmi->preScaled = true;
382 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Wreck1.svg"), _T("wreck1"),
383 _T("Wreck A"), displayDPmm);
384 if (pmi) pmi->preScaled = true;
385 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Wreck2.svg"), _T("wreck2"),
386 _T("Wreck B"), displayDPmm);
387 if (pmi) pmi->preScaled = true;
388 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-X-Small-Blue.svg"), _T("xmblue"),
389 _T("Blue X"), displayDPmm);
390 if (pmi) pmi->preScaled = true;
391 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-X-Small-Green.svg"),
392 _T("xmgreen"), _T("Green X"), displayDPmm);
393 if (pmi) pmi->preScaled = true;
394 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-X-Small-Red.svg"), _T("xmred"),
395 _T("Red X"), displayDPmm);
396 if (pmi) pmi->preScaled = true;
397
398 // Add the extended icons to their own sorted array
399 if (m_waypoint_man.m_pExtendedIconArray) {
400 m_waypoint_man.m_pExtendedIconArray->Clear();
401 } else {
402 m_waypoint_man.m_pExtendedIconArray =
403 new SortedArrayOfMarkIcon(CompareMarkIcons);
404 }
405
406#if 0
407 wxArrayString FileList;
408 double bm_size = -1;
409
410 int n_files = wxDir::GetAllFiles( iconDir, &FileList );
411
412 // If the scale factor is not unity, measure the first icon in the list
413 // So that we may apply the scale factor exactly to all
414 if( fabs(g_ChartScaleFactorExp - 1.0) > 0.1){
415
416 for( int ifile = 0; ifile < n_files; ifile++ ) {
417 wxString name = FileList[ifile];
418
419 wxFileName fn( name );
420
421 if( fn.GetExt().Lower() == _T("svg") ) {
422 wxBitmap bmt = LoadSVG(name, -1, -1 );
423 bm_size = bmt.GetWidth() * g_ChartScaleFactorExp;
424 break;
425 }
426 }
427 }
428
429 for( int ifile = 0; ifile < n_files; ifile++ ) {
430 wxString name = FileList[ifile];
431
432 wxFileName fn( name );
433 wxString iconname = fn.GetName();
434 wxBitmap icon1;
435 if( fn.GetExt().Lower() == _T("svg") ) {
436 wxImage iconSVG = LoadSVG( name, (int)bm_size, (int)bm_size );
437 MarkIcon * pmi = ProcessExtendedIcon( iconSVG, iconname, iconname );
438 if(pmi)
439 pmi->preScaled = true;
440 }
441 }
442#else
443
444 wxArrayString FileList;
445 // nominal size, but not less than 4 pixel
446 double bm_size = wxMax(4.0, floor(displayDPmm * 12.0));
447 bm_size /= OCPN_GetWinDIPScaleFactor();
448 bm_size *= g_MarkScaleFactorExp;
449
450 int n_files = wxDir::GetAllFiles(iconDir, &FileList);
451
452 g_BasePlatform->ShowBusySpinner();
453
454 for (int ifile = 0; ifile < n_files; ifile++) {
455 wxString name = FileList[ifile];
456
457 wxFileName fn(name);
458 wxString iconname = fn.GetName();
459 wxBitmap icon1;
460
461 if (fn.GetExt().Lower() == _T("svg")) {
462 unsigned int w, h;
463
464 SVGDocumentPixelSize(name, w, h);
465
466 // We want certain minimal size for the icons
467 w = wxMax(wxMax(w, h), 15);
468 bm_size = w * g_MarkScaleFactorExp;
469 bm_size /= OCPN_GetWinDIPScaleFactor();
470#ifdef __ANDROID__
471 // Set the onscreen size of the symbol
472 // Compensate for various display resolutions
473 // Develop empirically, making a "diamond" symbol about 4 mm square
474 // Android uses "density buckets", so simple math produces poor results.
475 // Thus, these factors have been empirically tweaked to provide good
476 // results on a variety of devices
477 float nominal_legacy_icon_size_pixels =
478 wxMax(4.0, floor(displayDPmm * 12.0));
479 // legacy icon size
480 float pix_factor = nominal_legacy_icon_size_pixels / 68.0;
481 bm_size *= pix_factor;
482#endif
483
484 wxBitmap bmp = LoadSVG(name, (int)bm_size, (int)bm_size);
485 if (bmp.IsOk()) {
486 wxImage iconSVG = bmp.ConvertToImage();
487
488 MarkIcon *pmi = ProcessExtendedIcon(iconSVG, iconname, iconname);
489 if (pmi) pmi->preScaled = true;
490 } else {
491 wxLogMessage("Failed loading mark icon " + name);
492 }
493 }
494 }
495 g_BasePlatform->HideBusySpinner();
496#endif
497
498 // Walk the two sorted lists, adding icons to the un-sorted master list
499
500 auto size = m_waypoint_man.m_pLegacyIconArray->GetCount();
501 for (unsigned int i = 0; i < size; i++) {
502 pmi = (MarkIcon *)m_waypoint_man.m_pLegacyIconArray->Item(i);
503 m_waypoint_man.m_pIconArray->Add(pmi);
504 }
505
506 size = m_waypoint_man.m_pExtendedIconArray->GetCount();
507 for (unsigned int i = 0; i < size; i++) {
508 pmi = (MarkIcon *)m_waypoint_man.m_pExtendedIconArray->Item(i);
509
510 // Do not add any icons from the extended array if they have already been
511 // used as legacy substitutes
512 bool noAdd = false;
513 auto legacy_count = m_waypoint_man.m_pLegacyIconArray->GetCount();
514 for (unsigned int j = 0; j < legacy_count; j++) {
515 MarkIcon *pmiLegacy =
516 (MarkIcon *)m_waypoint_man.m_pLegacyIconArray->Item(j);
517 if (pmiLegacy->icon_name.IsSameAs(pmi->icon_name)) {
518 noAdd = true;
519 break;
520 }
521 }
522 if (!noAdd) m_waypoint_man.m_pIconArray->Add(pmi);
523 }
524}
525
526void WayPointmanGui::ReloadAllIcons(double displayDPmm) {
527 ProcessIcons(g_StyleManager->GetCurrentStyle(), displayDPmm);
528
529 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
530 MarkIcon *pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
531 wxImage dim_image;
532 if (m_waypoint_man.m_cs == GLOBAL_COLOR_SCHEME_DUSK) {
533 dim_image = m_waypoint_man.CreateDimImage(pmi->iconImage, .50);
534 pmi->iconImage = dim_image;
535 } else if (m_waypoint_man.m_cs == GLOBAL_COLOR_SCHEME_NIGHT) {
536 dim_image = m_waypoint_man.CreateDimImage(pmi->iconImage, .20);
537 pmi->iconImage = dim_image;
538 }
539 }
540 ReloadRoutepointIcons();
541}
542
543void WayPointmanGui::SetColorScheme(ColorScheme cs, double displayDPmm) {
544 m_waypoint_man.m_cs = cs;
545 ReloadAllIcons(displayDPmm);
546}
547
548MarkIcon *WayPointmanGui::ProcessLegacyIcon(wxString fileName,
549 const wxString &key,
550 const wxString &description,
551 double displayDPmm) {
552 double bm_size = -1.0;
553
554#ifndef ocpnUSE_wxBitmapBundle
555#ifndef __ANDROID__
556 if (fabs(g_MarkScaleFactorExp - 1.0) > 0.1) {
557 wxBitmap img = LoadSVG(fileName, -1, -1);
558 bm_size = img.GetWidth() * g_MarkScaleFactorExp;
559 bm_size /= OCPN_GetWinDIPScaleFactor();
560 }
561#else
562 // Set the onscreen size of the symbol
563 // Compensate for various display resolutions
564 // Develop empirically, making a "diamond" symbol about 4 mm square
565 // Android uses "density buckets", so simple math produces poor results.
566 // Thus, these factors have been empirically tweaked to provide good results
567 // on a variety of devices
568 float nominal_legacy_icon_size_pixels = wxMax(4.0, floor(displayDPmm * 12.0));
569 // legacy icon size
570 float pix_factor = nominal_legacy_icon_size_pixels / 68.0;
571
572 unsigned int w, h;
573 SVGDocumentPixelSize(fileName, w, h);
574
575 bm_size = w * pix_factor * g_MarkScaleFactorExp;
576
577#endif
578#else
579 unsigned int w, h;
580 SVGDocumentPixelSize(fileName, w, h);
581 w = wxMax(wxMax(w, h), 15); // We want certain minimal size for the icons,
582 // 15px (approx 3mm) be it
583 bm_size = w * g_MarkScaleFactorExp; // SVGPixelsToDisplay(w);
584 bm_size /= OCPN_GetWinDIPScaleFactor();
585#endif
586
587 wxBitmap bm = LoadSVG(fileName, (int)bm_size, (int)bm_size);
588 if (!bm.IsOk()) return NULL;
589
590 wxImage image =
591 LoadSVG(fileName, (int)bm_size, (int)bm_size).ConvertToImage();
592
593 wxRect rClip = CropImageOnAlpha(image);
594 wxImage imageClip = image.GetSubImage(rClip);
595
596 MarkIcon *pmi = 0;
597
598 bool newIcon = true;
599
600 // avoid adding duplicates
601 for (unsigned int i = 0; i < m_waypoint_man.m_pLegacyIconArray->GetCount();
602 i++) {
603 pmi = (MarkIcon *)m_waypoint_man.m_pLegacyIconArray->Item(i);
604 if (pmi->icon_name.IsSameAs(key)) {
605 newIcon = false;
606 delete pmi->piconBitmap;
607 break;
608 }
609 }
610
611 if (newIcon) {
612 pmi = new MarkIcon;
613 pmi->icon_name = key; // Used for sorting
614 m_waypoint_man.m_pLegacyIconArray->Add(pmi);
615 }
616
617 pmi->icon_name = key;
618 pmi->icon_description = description;
619 pmi->piconBitmap = NULL;
620 pmi->icon_texture = 0; /* invalidate */
621 pmi->preScaled = false;
622 pmi->iconImage = imageClip;
623 pmi->m_blistImageOK = false;
624
625 return pmi;
626}
627
628MarkIcon *WayPointmanGui::ProcessExtendedIcon(wxImage &image,
629 const wxString &key,
630 const wxString &description) {
631 MarkIcon *pmi = 0;
632
633 bool newIcon = true;
634
635 // avoid adding duplicates
636 auto size = m_waypoint_man.m_pExtendedIconArray->GetCount();
637 for (unsigned int i = 0; i < size; i++) {
638 pmi = (MarkIcon *)m_waypoint_man.m_pExtendedIconArray->Item(i);
639 if (pmi->icon_name.IsSameAs(key)) {
640 newIcon = false;
641 delete pmi->piconBitmap;
642 break;
643 }
644 }
645
646 if (newIcon) {
647 pmi = new MarkIcon;
648 pmi->icon_name = key; // Used for sorting
649 m_waypoint_man.m_pExtendedIconArray->Add(pmi);
650 }
651
652 wxRect rClip = CropImageOnAlpha(image);
653 wxImage imageClip = image.GetSubImage(rClip);
654
655 pmi->icon_name = key;
656 pmi->icon_description = description;
657 pmi->piconBitmap = new wxBitmap(imageClip);
658 pmi->icon_texture = 0; /* invalidate */
659 pmi->preScaled = false;
660 pmi->iconImage = imageClip;
661 pmi->m_blistImageOK = false;
662
663 return pmi;
664}
665
666wxRect WayPointmanGui::CropImageOnAlpha(wxImage &image) {
667 const int w = image.GetWidth();
668 const int h = image.GetHeight();
669
670 wxRect rv = wxRect(0, 0, w, h);
671 if (!image.HasAlpha()) return rv;
672
673 unsigned char *pAlpha = image.GetAlpha();
674
675 int leftCrop = w;
676 int topCrop = h;
677 int rightCrop = w;
678 int bottomCrop = h;
679
680 // Horizontal
681 for (int i = 0; i < h; i++) {
682 int lineStartIndex = i * w;
683
684 int j = 0;
685 while ((j < w) && (pAlpha[lineStartIndex + j] == 0)) j++;
686 leftCrop = wxMin(leftCrop, j);
687
688 int k = w - 1;
689 while (k && (pAlpha[lineStartIndex + k] == 0)) k--;
690 rightCrop = wxMin(rightCrop, image.GetWidth() - k - 2);
691 }
692
693 // Vertical
694 for (int i = 0; i < w; i++) {
695 int columnStartIndex = i;
696
697 int j = 0;
698 while ((j < h) && (pAlpha[columnStartIndex + (j * w)] == 0)) j++;
699 topCrop = wxMin(topCrop, j);
700
701 int k = h - 1;
702 while (k && (pAlpha[columnStartIndex + (k * w)] == 0)) k--;
703 bottomCrop = wxMin(bottomCrop, h - k - 2);
704 }
705
706 int xcrop = wxMin(rightCrop, leftCrop);
707 int ycrop = wxMin(topCrop, bottomCrop);
708 int crop = wxMin(xcrop, ycrop);
709
710 rv.x = wxMax(crop, 0);
711 rv.width = wxMax(1, w - (2 * crop));
712 rv.width = wxMin(rv.width, w);
713 rv.y = rv.x;
714 rv.height = rv.width;
715
716 return rv;
717}
718
719void WayPointmanGui::ReloadRoutepointIcons() {
720 // Iterate on the RoutePoint list, requiring each to reload icon
721
722 wxRoutePointListNode *node = m_waypoint_man.m_pWayPointList->GetFirst();
723 while (node) {
724 RoutePoint *pr = node->GetData();
725 pr->ReLoadIcon();
726 node = node->GetNext();
727 }
728}
729
730unsigned int WayPointmanGui::GetIconTexture(const wxBitmap *pbm, int &glw,
731 int &glh) {
732#ifdef ocpnUSE_GL
733 int index = m_waypoint_man.GetIconIndex(pbm);
734 MarkIcon *pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(index);
735
736 if (!pmi->icon_texture) {
737 /* make rgba texture */
738 wxImage image = pbm->ConvertToImage();
739 unsigned char *d = image.GetData();
740 if (d == 0) {
741 // don't create a texture with junk
742 return 0;
743 }
744
745 glGenTextures(1, &pmi->icon_texture);
746 glBindTexture(GL_TEXTURE_2D, pmi->icon_texture);
747
748 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
749 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
751
752 int w = image.GetWidth(), h = image.GetHeight();
753
754 pmi->tex_w = NextPow2(w);
755 pmi->tex_h = NextPow2(h);
756
757 unsigned char *a = image.GetAlpha();
758
759 unsigned char mr, mg, mb;
760 if (!a) image.GetOrFindMaskColour(&mr, &mg, &mb);
761
762 unsigned char *e = new unsigned char[4 * w * h];
763 for (int y = 0; y < h; y++) {
764 for (int x = 0; x < w; x++) {
765 unsigned char r, g, b;
766 int off = (y * w + x);
767 r = d[off * 3 + 0];
768 g = d[off * 3 + 1];
769 b = d[off * 3 + 2];
770 e[off * 4 + 0] = r;
771 e[off * 4 + 1] = g;
772 e[off * 4 + 2] = b;
773
774 e[off * 4 + 3] =
775 a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
776 }
777 }
778
779 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pmi->tex_w, pmi->tex_h, 0, GL_RGBA,
780 GL_UNSIGNED_BYTE, NULL);
781 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, e);
782
783 delete[] e;
784 }
785
786 glw = pmi->tex_w;
787 glh = pmi->tex_h;
788
789 return pmi->icon_texture;
790#else
791 return 0;
792#endif
793}
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
PlugIn Object Definition/API.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
SVG utilities.