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#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 "base_platform.h"
37#include "MarkIcon.h"
38#include "route_point.h"
39#include "styles.h"
40#include "svg_utils.h"
41#include "waypointman_gui.h"
42#include "ocpn_plugin.h"
43
44extern BasePlatform* g_BasePlatform;
45extern float g_MarkScaleFactorExp;
46extern ocpnStyle::StyleManager *g_StyleManager;
47
48static int CompareMarkIcons(MarkIcon *mi1, MarkIcon *mi2) {
49 return (mi1->icon_name.CmpNoCase(mi2->icon_name));
50}
51
52
53void WayPointmanGui::ProcessUserIcons(ocpnStyle::Style *style,
54 double displayDPmm) {
55 wxString msg;
56 msg.Printf(_T("DPMM: %g ScaleFactorExp: %g"), displayDPmm,
57 g_MarkScaleFactorExp);
58 wxLogMessage(msg);
59
60 wxString UserIconPath = g_BasePlatform->GetPrivateDataDir();
61 wxChar sep = wxFileName::GetPathSeparator();
62 if (UserIconPath.Last() != sep) UserIconPath.Append(sep);
63 UserIconPath.Append(_T("UserIcons/"));
64
65 wxLogMessage(_T("Looking for UserIcons at ") + UserIconPath);
66
67 if (wxDir::Exists(UserIconPath)) {
68 wxLogMessage(_T("Loading UserIcons from ") + UserIconPath);
69 wxArrayString FileList;
70
71 int n_files =
72 wxDir::GetAllFiles(UserIconPath, &FileList, _T(""), wxDIR_FILES);
73
74 for (int ifile = 0; ifile < n_files; ifile++) {
75 wxString name = FileList[ifile];
76
77 wxFileName fn(name);
78 wxString iconname = fn.GetName();
79 wxBitmap icon1;
80
81 if (fn.GetExt().Lower() == _T("xpm")) {
82 if (icon1.LoadFile(name, wxBITMAP_TYPE_XPM)) {
83 wxLogMessage(_T("Adding icon: ") + iconname);
84 ProcessIcon(icon1, iconname, iconname);
85 }
86 }
87 if (fn.GetExt().Lower() == _T("png")) {
88 if (icon1.LoadFile(name, wxBITMAP_TYPE_PNG)) {
89 wxLogMessage(_T("Adding icon: ") + iconname);
90 ProcessIcon(icon1, iconname, iconname);
91 }
92 }
93 if (fn.GetExt().Lower() == _T("svg")) {
94 unsigned int w, h;
95 SVGDocumentPixelSize(name, w, h);
96 w = wxMax(wxMax(w, h), 15); // We want certain minimal size for the
97 // icons, 15px (approx 3mm) be it
98 const unsigned int bm_size = w; //SVGPixelsToDisplay(w);
99 wxBitmap iconSVG = LoadSVG(name, bm_size, bm_size);
100 MarkIcon *pmi = ProcessIcon(iconSVG, iconname, iconname);
101 if (pmi) pmi->preScaled = true;
102 }
103 }
104 }
105}
106
107MarkIcon* WayPointmanGui::ProcessIcon(wxBitmap pimage, const wxString& key,
108 const wxString& description) {
109 MarkIcon *pmi = 0;
110
111 bool newIcon = true;
112
113 // avoid adding duplicates
114 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
115 pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
116 if (pmi->icon_name.IsSameAs(key)) {
117 newIcon = false;
118 delete pmi->piconBitmap;
119 break;
120 }
121 }
122
123 if (newIcon) {
124 pmi = new MarkIcon;
125 pmi->icon_name = key; // Used for sorting
126 m_waypoint_man.m_pIconArray->Add(pmi);
127 }
128
129 wxBitmap *pbm = new wxBitmap(pimage);
130 pmi->icon_name = key;
131 pmi->icon_description = description;
132 pmi->piconBitmap = NULL;
133 pmi->icon_texture = 0; /* invalidate */
134 pmi->preScaled = false;
135 pmi->iconImage = pbm->ConvertToImage();
136 pmi->m_blistImageOK = false;
137 delete pbm;
138
139 return pmi;
140}
141
142void WayPointmanGui::ProcessIcons(ocpnStyle::Style *style, double displayDPmm) {
143 m_waypoint_man.m_pIconArray->Clear();
144
145 ProcessDefaultIcons(displayDPmm);
146
147 // Load user defined icons.
148 // Done after default icons are initialized,
149 // so that user may substitute an icon by using the same name in the Usericons
150 // file.
151 ProcessUserIcons(style, displayDPmm);
152
153 if (NULL != m_waypoint_man.pmarkicon_image_list) {
154 m_waypoint_man.pmarkicon_image_list->RemoveAll();
155 delete m_waypoint_man.pmarkicon_image_list;
156 m_waypoint_man.pmarkicon_image_list = NULL;
157 }
158
159 // First find the largest bitmap size, to use as the base size for lists of
160 // icons
161 int w = 0;
162 int h = 0;
163
164 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
165 MarkIcon *pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
166 w = wxMax(w, pmi->iconImage.GetWidth());
167 h = wxMax(h, pmi->iconImage.GetHeight());
168 }
169
170 m_waypoint_man.m_bitmapSizeForList = wxMax(w, h);
171 m_waypoint_man.m_bitmapSizeForList =
172 wxMin(100, m_waypoint_man.m_bitmapSizeForList);
173}
174
175void WayPointmanGui::ProcessDefaultIcons(double displayDPmm) {
176 wxString iconDir = g_BasePlatform->GetSharedDataDir();
177 appendOSDirSlash(&iconDir);
178 iconDir.append(_T("uidata"));
179 appendOSDirSlash(&iconDir);
180 iconDir.append(_T("markicons"));
181 appendOSDirSlash(&iconDir);
182
183 MarkIcon *pmi = 0;
184
185 // Add the legacy icons to their own sorted array
186 if (m_waypoint_man.m_pLegacyIconArray)
187 m_waypoint_man.m_pLegacyIconArray->Clear();
188 else
189 m_waypoint_man.m_pLegacyIconArray =
190 new SortedArrayOfMarkIcon(CompareMarkIcons);
191
192 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Empty.svg"), _T("empty"),
193 _T("Empty"), displayDPmm);
194 if (pmi) pmi->preScaled = true;
195 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Triangle.svg"), _T("triangle"),
196 _T("Triangle"), displayDPmm);
197 if (pmi) pmi->preScaled = true;
198 pmi = ProcessLegacyIcon(iconDir + _T("1st-Active-Waypoint.svg"),
199 _T("activepoint"), _T("Active WP"), displayDPmm);
200 if (pmi) pmi->preScaled = true;
201 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Boarding-Location.svg"),
202 _T("boarding"), _T("Boarding Location"), displayDPmm);
203 if (pmi) pmi->preScaled = true;
204 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Airplane.svg"), _T("airplane"),
205 _T("Airplane"), displayDPmm);
206 if (pmi) pmi->preScaled = true;
207 pmi = ProcessLegacyIcon(iconDir + _T("1st-Anchorage.svg"), _T("anchorage"),
208 _T("Anchorage"), displayDPmm);
209 if (pmi) pmi->preScaled = true;
210 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Anchor2.svg"), _T("anchor"),
211 _T("Anchor"), displayDPmm);
212 if (pmi) pmi->preScaled = true;
213 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Boundary.svg"), _T("boundary"),
214 _T("Boundary Mark"), displayDPmm);
215 if (pmi) pmi->preScaled = true;
216 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Buoy-TypeA.svg"), _T("bouy1"),
217 _T("Bouy Type A"), displayDPmm);
218 if (pmi) pmi->preScaled = true;
219 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Buoy-TypeB.svg"), _T("bouy2"),
220 _T("Bouy Type B"), displayDPmm);
221 if (pmi) pmi->preScaled = true;
222 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Campfire.svg"), _T("campfire"),
223 _T("Campfire"), displayDPmm);
224 if (pmi) pmi->preScaled = true;
225 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Camping.svg"), _T("camping"),
226 _T("Camping Spot"), displayDPmm);
227 if (pmi) pmi->preScaled = true;
228 pmi = ProcessLegacyIcon(iconDir + _T("Sea-Floor-Coral.svg"), _T("coral"),
229 _T("Coral"), displayDPmm);
230 if (pmi) pmi->preScaled = true;
231 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Fishing.svg"), _T("fishhaven"),
232 _T("Fish Haven"), displayDPmm);
233 if (pmi) pmi->preScaled = true;
234 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Fishing.svg"), _T("fishing"),
235 _T("Fishing Spot"), displayDPmm);
236 if (pmi) pmi->preScaled = true;
237 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Fishing.svg"), _T("fish"),
238 _T("Fish"), displayDPmm);
239 if (pmi) pmi->preScaled = true;
240 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Mooring-Buoy.svg"), _T("float"),
241 _T("Float"), displayDPmm);
242 if (pmi) pmi->preScaled = true;
243 pmi = ProcessLegacyIcon(iconDir + _T("Service-Food.svg"), _T("food"),
244 _T("Food"), displayDPmm);
245 if (pmi) pmi->preScaled = true;
246 pmi = ProcessLegacyIcon(iconDir + _T("Service-Fuel-Pump-Diesel-Petrol.svg"),
247 _T("fuel"), _T("Fuel"), displayDPmm);
248 if (pmi) pmi->preScaled = true;
249 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Green.svg"),
250 _T("greenlite"), _T("Green Light"), displayDPmm);
251 if (pmi) pmi->preScaled = true;
252 pmi = ProcessLegacyIcon(iconDir + _T("Sea-Floor-Sea-Weed.svg"), _T("kelp"),
253 _T("Kelp"), displayDPmm);
254 if (pmi) pmi->preScaled = true;
255 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-TypeA.svg"), _T("light"),
256 _T("Light Type A"), displayDPmm);
257 if (pmi) pmi->preScaled = true;
258 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-TypeB.svg"), _T("light1"),
259 _T("Light Type B"), displayDPmm);
260 if (pmi) pmi->preScaled = true;
261 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Vessel.svg"),
262 _T("litevessel"), _T("litevessel"), displayDPmm);
263 if (pmi) pmi->preScaled = true;
264 pmi = ProcessLegacyIcon(iconDir + _T("1st-Man-Overboard.svg"), _T("mob"),
265 _T("MOB"), displayDPmm);
266 if (pmi) pmi->preScaled = true;
267 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Mooring-Buoy.svg"), _T("mooring"),
268 _T("Mooring Bouy"), displayDPmm);
269 if (pmi) pmi->preScaled = true;
270 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Mooring-Buoy-Super.svg"),
271 _T("oilbouy"), _T("Oil Bouy"), displayDPmm);
272 if (pmi) pmi->preScaled = true;
273 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Oil-Platform.svg"),
274 _T("platform"), _T("Platform"), displayDPmm);
275 if (pmi) pmi->preScaled = true;
276 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Red-Green.svg"),
277 _T("redgreenlite"), _T("Red/Green Light"), displayDPmm);
278 if (pmi) pmi->preScaled = true;
279 pmi = ProcessLegacyIcon(iconDir + _T("Marks-Light-Red.svg"), _T("redlite"),
280 _T("Red Light"), displayDPmm);
281 if (pmi) pmi->preScaled = true;
282 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Rock-Exposed.svg"), _T("rock1"),
283 _T("Rock (exposed)"), displayDPmm);
284 if (pmi) pmi->preScaled = true;
285 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Rock-Awash.svg"), _T("rock2"),
286 _T("Rock, (awash)"), displayDPmm);
287 if (pmi) pmi->preScaled = true;
288 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Sandbar.svg"), _T("sand"),
289 _T("Sand"), displayDPmm);
290 if (pmi) pmi->preScaled = true;
291 pmi = ProcessLegacyIcon(iconDir + _T("Activity-Diving-Scuba-Flag.svg"),
292 _T("scuba"), _T("Scuba"), displayDPmm);
293 if (pmi) pmi->preScaled = true;
294 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Sandbar.svg"), _T("shoal"),
295 _T("Shoal"), displayDPmm);
296 if (pmi) pmi->preScaled = true;
297 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Snag.svg"), _T("snag"),
298 _T("Snag"), displayDPmm);
299 if (pmi) pmi->preScaled = true;
300 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Square.svg"), _T("square"),
301 _T("Square"), displayDPmm);
302 if (pmi) pmi->preScaled = true;
303 pmi = ProcessLegacyIcon(iconDir + _T("1st-Diamond.svg"), _T("diamond"),
304 _T("Diamond"), displayDPmm);
305 if (pmi) pmi->preScaled = true;
306 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-Circle.svg"), _T("circle"),
307 _T("Circle"), displayDPmm);
308 if (pmi) pmi->preScaled = true;
309 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Wreck1.svg"), _T("wreck1"),
310 _T("Wreck A"), displayDPmm);
311 if (pmi) pmi->preScaled = true;
312 pmi = ProcessLegacyIcon(iconDir + _T("Hazard-Wreck2.svg"), _T("wreck2"),
313 _T("Wreck B"), displayDPmm);
314 if (pmi) pmi->preScaled = true;
315 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-X-Small-Blue.svg"), _T("xmblue"),
316 _T("Blue X"), displayDPmm);
317 if (pmi) pmi->preScaled = true;
318 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-X-Small-Green.svg"),
319 _T("xmgreen"), _T("Green X"), displayDPmm);
320 if (pmi) pmi->preScaled = true;
321 pmi = ProcessLegacyIcon(iconDir + _T("Symbol-X-Small-Red.svg"), _T("xmred"),
322 _T("Red X"), displayDPmm);
323 if (pmi) pmi->preScaled = true;
324
325 // Add the extended icons to their own sorted array
326 if (m_waypoint_man.m_pExtendedIconArray)
327 m_waypoint_man.m_pExtendedIconArray->Clear();
328 else
329 m_waypoint_man.m_pExtendedIconArray =
330 new SortedArrayOfMarkIcon(CompareMarkIcons);
331
332#if 0
333 wxArrayString FileList;
334 double bm_size = -1;
335
336 int n_files = wxDir::GetAllFiles( iconDir, &FileList );
337
338 // If the scale factor is not unity, measure the first icon in the list
339 // So that we may apply the scale factor exactly to all
340 if( fabs(g_ChartScaleFactorExp - 1.0) > 0.1){
341
342 for( int ifile = 0; ifile < n_files; ifile++ ) {
343 wxString name = FileList[ifile];
344
345 wxFileName fn( name );
346
347 if( fn.GetExt().Lower() == _T("svg") ) {
348 wxBitmap bmt = LoadSVG(name, -1, -1 );
349 bm_size = bmt.GetWidth() * g_ChartScaleFactorExp;
350 break;
351 }
352 }
353 }
354
355 for( int ifile = 0; ifile < n_files; ifile++ ) {
356 wxString name = FileList[ifile];
357
358 wxFileName fn( name );
359 wxString iconname = fn.GetName();
360 wxBitmap icon1;
361 if( fn.GetExt().Lower() == _T("svg") ) {
362 wxImage iconSVG = LoadSVG( name, (int)bm_size, (int)bm_size );
363 MarkIcon * pmi = ProcessExtendedIcon( iconSVG, iconname, iconname );
364 if(pmi)
365 pmi->preScaled = true;
366 }
367 }
368#else
369
370 wxArrayString FileList;
371 // nominal size, but not less than 4 pixel
372 double bm_size = wxMax(4.0, floor(displayDPmm * 12.0));
373 bm_size /= OCPN_GetWinDIPScaleFactor();
374 bm_size *= g_MarkScaleFactorExp;
375
376 int n_files = wxDir::GetAllFiles(iconDir, &FileList);
377
378 g_BasePlatform->ShowBusySpinner();
379
380 for (int ifile = 0; ifile < n_files; ifile++) {
381 wxString name = FileList[ifile];
382
383 wxFileName fn(name);
384 wxString iconname = fn.GetName();
385 wxBitmap icon1;
386
387 if (fn.GetExt().Lower() == _T("svg")) {
388 unsigned int w, h;
389
390 SVGDocumentPixelSize(name, w, h);
391 w = wxMax(wxMax(w, h), 15); // We want certain minimal size for the
392 // icons, 15px (approx 3mm) be it
393
394 bm_size = w * g_MarkScaleFactorExp; //= SVGPixelsToDisplay(w);
395 bm_size /= OCPN_GetWinDIPScaleFactor();
396
397
398 wxBitmap bmp = LoadSVG(name, (int)bm_size, (int)bm_size);
399 if (bmp.IsOk()) {
400 wxImage iconSVG = bmp.ConvertToImage();
401
402 MarkIcon *pmi = ProcessExtendedIcon(iconSVG, iconname, iconname);
403 if (pmi) pmi->preScaled = true;
404 } else {
405 wxLogMessage("Failed loading mark icon " + name);
406 }
407 }
408 }
409 g_BasePlatform->HideBusySpinner();
410#endif
411
412 // Walk the two sorted lists, adding icons to the un-sorted master list
413
414 auto size = m_waypoint_man.m_pLegacyIconArray->GetCount();
415 for (unsigned int i = 0; i < size; i++) {
416 pmi = (MarkIcon *)m_waypoint_man.m_pLegacyIconArray->Item(i);
417 m_waypoint_man.m_pIconArray->Add(pmi);
418 }
419
420 size = m_waypoint_man.m_pExtendedIconArray->GetCount();
421 for (unsigned int i = 0; i < size; i++) {
422 pmi = (MarkIcon *) m_waypoint_man.m_pExtendedIconArray->Item(i);
423
424 // Do not add any icons from the extended array if they have already been
425 // used as legacy substitutes
426 bool noAdd = false;
427 auto legacy_count = m_waypoint_man.m_pLegacyIconArray->GetCount();
428 for (unsigned int j = 0; j < legacy_count; j++) {
429 MarkIcon *pmiLegacy =
430 (MarkIcon *)m_waypoint_man.m_pLegacyIconArray->Item(j);
431 if (pmiLegacy->icon_name.IsSameAs(pmi->icon_name)) {
432 noAdd = true;
433 break;
434 }
435 }
436 if (!noAdd) m_waypoint_man.m_pIconArray->Add(pmi);
437 }
438}
439
440void WayPointmanGui::ReloadAllIcons(double displayDPmm) {
441 ProcessIcons(g_StyleManager->GetCurrentStyle(), displayDPmm);
442
443 for (unsigned int i = 0; i < m_waypoint_man.m_pIconArray->GetCount(); i++) {
444 MarkIcon *pmi = (MarkIcon *)m_waypoint_man.m_pIconArray->Item(i);
445 wxImage dim_image;
446 if (m_waypoint_man.m_cs == GLOBAL_COLOR_SCHEME_DUSK) {
447 dim_image = m_waypoint_man.CreateDimImage(pmi->iconImage, .50);
448 pmi->iconImage = dim_image;
449 } else if (m_waypoint_man.m_cs == GLOBAL_COLOR_SCHEME_NIGHT) {
450 dim_image = m_waypoint_man.CreateDimImage(pmi->iconImage, .20);
451 pmi->iconImage = dim_image;
452 }
453 }
454 ReloadRoutepointIcons();
455}
456
457void WayPointmanGui::SetColorScheme(ColorScheme cs, double displayDPmm) {
458 m_waypoint_man.m_cs = cs;
459 ReloadAllIcons(displayDPmm);
460}
461
462MarkIcon *WayPointmanGui::ProcessLegacyIcon(wxString fileName, const wxString &key,
463 const wxString &description,
464 double displayDPmm) {
465 double bm_size = -1.0;
466
467#ifndef ocpnUSE_wxBitmapBundle
468#ifndef __ANDROID__
469 if (fabs(g_MarkScaleFactorExp - 1.0) > 0.1) {
470 wxBitmap img = LoadSVG(fileName, -1, -1);
471 bm_size = img.GetWidth() * g_MarkScaleFactorExp;
472 bm_size /= OCPN_GetWinDIPScaleFactor();
473 }
474#else
475 // Set the onscreen size of the symbol
476 // Compensate for various display resolutions
477 // Develop empirically, making a "diamond" symbol about 4 mm square
478 // Android uses "density buckets", so simple math produces poor results.
479 // Thus, these factors have been empirically tweaked to provide good results
480 // on a variety of devices
481 float nominal_legacy_icon_size_pixels = wxMax(4.0,
482 floor(displayDPmm * 12.0));
483 // legacy icon size
484 float pix_factor = nominal_legacy_icon_size_pixels / 68.0;
485
486 wxBitmap img = LoadSVG(fileName, -1, -1);
487 bm_size = img.GetWidth() * pix_factor * g_MarkScaleFactorExp;
488#endif
489#else
490 unsigned int w, h;
491 SVGDocumentPixelSize(fileName, w, h);
492 w = wxMax(wxMax(w, h), 15); // We want certain minimal size for the icons,
493 // 15px (approx 3mm) be it
494 bm_size = w * g_MarkScaleFactorExp; //SVGPixelsToDisplay(w);
495 bm_size /= OCPN_GetWinDIPScaleFactor();
496#endif
497
498 wxImage image =
499 LoadSVG(fileName, (int)bm_size, (int)bm_size).ConvertToImage();
500
501 wxRect rClip = CropImageOnAlpha(image);
502 wxImage imageClip = image.GetSubImage(rClip);
503
504 MarkIcon *pmi = 0;
505
506 bool newIcon = true;
507
508 // avoid adding duplicates
509 for (unsigned int i = 0; i < m_waypoint_man.m_pLegacyIconArray->GetCount(); i++) {
510 pmi = (MarkIcon *)m_waypoint_man.m_pLegacyIconArray->Item(i);
511 if (pmi->icon_name.IsSameAs(key)) {
512 newIcon = false;
513 delete pmi->piconBitmap;
514 break;
515 }
516 }
517
518 if (newIcon) {
519 pmi = new MarkIcon;
520 pmi->icon_name = key; // Used for sorting
521 m_waypoint_man.m_pLegacyIconArray->Add(pmi);
522 }
523
524 pmi->icon_name = key;
525 pmi->icon_description = description;
526 pmi->piconBitmap = NULL;
527 pmi->icon_texture = 0; /* invalidate */
528 pmi->preScaled = false;
529 pmi->iconImage = imageClip;
530 pmi->m_blistImageOK = false;
531
532 return pmi;
533}
534
535MarkIcon *WayPointmanGui::ProcessExtendedIcon(wxImage &image,
536 const wxString &key,
537 const wxString &description) {
538 MarkIcon *pmi = 0;
539
540 bool newIcon = true;
541
542 // avoid adding duplicates
543 auto size = m_waypoint_man.m_pExtendedIconArray->GetCount();
544 for (unsigned int i = 0; i < size; i++) {
545 pmi = (MarkIcon *)m_waypoint_man.m_pExtendedIconArray->Item(i);
546 if (pmi->icon_name.IsSameAs(key)) {
547 newIcon = false;
548 delete pmi->piconBitmap;
549 break;
550 }
551 }
552
553 if (newIcon) {
554 pmi = new MarkIcon;
555 pmi->icon_name = key; // Used for sorting
556 m_waypoint_man.m_pExtendedIconArray->Add(pmi);
557 }
558
559 wxRect rClip = CropImageOnAlpha(image);
560 wxImage imageClip = image.GetSubImage(rClip);
561
562 pmi->icon_name = key;
563 pmi->icon_description = description;
564 pmi->piconBitmap = NULL;
565 pmi->icon_texture = 0; /* invalidate */
566 pmi->preScaled = false;
567 pmi->iconImage = imageClip;
568 pmi->m_blistImageOK = false;
569
570 return pmi;
571}
572
573wxRect WayPointmanGui::CropImageOnAlpha(wxImage &image) {
574 const int w = image.GetWidth();
575 const int h = image.GetHeight();
576
577 wxRect rv = wxRect(0, 0, w, h);
578 if (!image.HasAlpha()) return rv;
579
580 unsigned char *pAlpha = image.GetAlpha();
581
582 int leftCrop = w;
583 int topCrop = h;
584 int rightCrop = w;
585 int bottomCrop = h;
586
587 // Horizontal
588 for (int i = 0; i < h; i++) {
589 int lineStartIndex = i * w;
590
591 int j = 0;
592 while ((j < w) && (pAlpha[lineStartIndex + j] == 0)) j++;
593 leftCrop = wxMin(leftCrop, j);
594
595 int k = w - 1;
596 while (k && (pAlpha[lineStartIndex + k] == 0)) k--;
597 rightCrop = wxMin(rightCrop, image.GetWidth() - k - 2);
598 }
599
600 // Vertical
601 for (int i = 0; i < w; i++) {
602 int columnStartIndex = i;
603
604 int j = 0;
605 while ((j < h) && (pAlpha[columnStartIndex + (j * w)] == 0)) j++;
606 topCrop = wxMin(topCrop, j);
607
608 int k = h - 1;
609 while (k && (pAlpha[columnStartIndex + (k * w)] == 0)) k--;
610 bottomCrop = wxMin(bottomCrop, h - k - 2);
611 }
612
613 int xcrop = wxMin(rightCrop, leftCrop);
614 int ycrop = wxMin(topCrop, bottomCrop);
615 int crop = wxMin(xcrop, ycrop);
616
617 rv.x = wxMax(crop, 0);
618 rv.width = wxMax(1, w - (2 * crop));
619 rv.width = wxMin(rv.width, w);
620 rv.y = rv.x;
621 rv.height = rv.width;
622
623 return rv;
624}
625
626void WayPointmanGui::ReloadRoutepointIcons() {
627 // Iterate on the RoutePoint list, requiring each to reload icon
628
629 wxRoutePointListNode *node = m_waypoint_man.m_pWayPointList->GetFirst();
630 while (node) {
631 RoutePoint *pr = node->GetData();
632 pr->ReLoadIcon();
633 node = node->GetNext();
634 }
635}
636
637