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