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