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