KiCad PCB EDA Suite
bitmap.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2017-2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 
26 #include <wx/image.h>
27 #include <wx/bitmap.h>
28 #include <wx/mstream.h>
29 #include <wx/menu.h>
30 #include <wx/menuitem.h>
31 #include <wx/aui/auibar.h>
32 
33 #include <cstdint>
34 #include <mutex>
35 #include <unordered_map>
36 
37 #include <common.h>
38 #include <bitmaps.h>
39 #include <pgm_base.h>
40 #include <eda_base_frame.h>
41 #include <draw_frame.h>
42 
43 
46  int scale;
47 
48  bool operator==( SCALED_BITMAP_ID const& other ) const noexcept
49  {
50  return bitmap == other.bitmap && scale == other.scale;
51  }
52 };
53 
54 
55 namespace std {
56  template<> struct hash<SCALED_BITMAP_ID>
57  {
59  typedef std::size_t result_type;
60 
61  result_type operator()( argument_type const& id ) const noexcept
62  {
63  static const bool sz64 = sizeof( uintptr_t ) == 8;
64  static const size_t mask = sz64 ? 0xF000000000000000uLL : 0xF0000000uL;
65  static const size_t offset = sz64 ? 60 : 28;
66 
67  // The hash only needs to be fast and simple, not necessarily accurate - a collision
68  // only makes things slower, not broken. BITMAP_DEF is a pointer, so the most
69  // significant several bits are generally going to be the same for all. Just convert
70  // it to an integer and stuff the scale factor into those bits.
71  return
72  ( (uintptr_t)( id.bitmap ) & ~mask ) |
73  ( ( (uintptr_t)( id.scale ) & 0xF ) << offset );
74  }
75  };
76 }
77 
78 
79 wxBitmap KiBitmap( BITMAP_DEF aBitmap )
80 {
81  wxMemoryInputStream is( aBitmap->png, aBitmap->byteCount );
82  wxImage image( is, wxBITMAP_TYPE_PNG );
83  wxBitmap bitmap( image );
84 
85  return bitmap;
86 }
87 
88 
89 int KiIconScale( wxWindow* aWindow )
90 {
91  const int vert_size = aWindow->ConvertDialogToPixels( wxSize( 0, 8 ) ).y;
92 
93  // Autoscale won't exceed unity until the system has quite high resolution,
94  // because we don't want the icons to look obviously scaled on a system
95  // where it's easy to see it.
96 
97  if( vert_size > 34 ) return 8;
98  else if( vert_size > 29 ) return 7;
99  else if( vert_size > 24 ) return 6;
100  else return 4;
101 }
102 
103 
104 static int get_scale_factor( EDA_BASE_FRAME* aWindow )
105 {
106  int requested_scale;
107  Pgm().CommonSettings()->Read( ICON_SCALE_KEY, &requested_scale, 0 );
108 
109  if( requested_scale > 0 )
110  return requested_scale;
111  else
112  return KiIconScale( aWindow );
113 }
114 
115 
116 wxBitmap KiScaledBitmap( BITMAP_DEF aBitmap, EDA_BASE_FRAME* aWindow )
117 {
118  // Bitmap conversions are cached because they can be slow.
119  static std::unordered_map<SCALED_BITMAP_ID, wxBitmap> bitmap_cache;
120  static std::mutex bitmap_cache_mutex;
121  const int scale = get_scale_factor( aWindow );
122 
123  SCALED_BITMAP_ID id = { aBitmap, scale };
124 
125  std::lock_guard<std::mutex> guard( bitmap_cache_mutex );
126  auto it = bitmap_cache.find( id );
127 
128  if( it != bitmap_cache.end() )
129  {
130  return it->second;
131  }
132  else
133  {
134  wxMemoryInputStream is( aBitmap->png, aBitmap->byteCount );
135  wxImage image( is, wxBITMAP_TYPE_PNG );
136 
137  // Bilinear seems to genuinely look better for these line-drawing icons
138  // than bicubic, despite claims in the wx documentation that bicubic is
139  // "highest quality". I don't recommend changing this. Bicubic looks
140  // blurry and makes me want an eye exam.
141  image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
142  wxIMAGE_QUALITY_BILINEAR );
143  return bitmap_cache.emplace( id, wxBitmap( image ) ).first->second;
144  }
145 }
146 
147 
148 wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, EDA_BASE_FRAME* aWindow )
149 {
150  const int scale = get_scale_factor( aWindow );
151 
152  if( scale == 4)
153  {
154  return wxBitmap( aBitmap );
155  }
156  else
157  {
158  wxImage image = aBitmap.ConvertToImage();
159  image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
160  wxIMAGE_QUALITY_BILINEAR );
161 
162  return wxBitmap( image );
163  }
164 }
165 
166 
167 void KiScaledSeparator( wxAuiToolBar* aToolbar, EDA_BASE_FRAME* aWindow )
168 {
169  const int scale = get_scale_factor( aWindow );
170 
171  if( scale > 4 )
172  {
173  aToolbar->AddSpacer( 16 * ( scale - 4 ) / 4 );
174  }
175 
176  aToolbar->AddSeparator();
177 
178  if( scale > 4 )
179  {
180  aToolbar->AddSpacer( 16 * ( scale - 4 ) / 4 );
181  }
182 }
183 
184 
185 wxBitmap* KiBitmapNew( BITMAP_DEF aBitmap )
186 {
187  wxMemoryInputStream is( aBitmap->png, aBitmap->byteCount );
188  wxImage image( is, wxBITMAP_TYPE_PNG );
189  wxBitmap* bitmap = new wxBitmap( image );
190 
191  return bitmap;
192 }
193 
194 
195 bool SaveCanvasImageToFile( EDA_DRAW_FRAME* aFrame, const wxString& aFileName,
196  wxBitmapType aBitmapType )
197 {
198  wxCHECK( aFrame != nullptr, false );
199 
200  bool retv = true;
201 
202  // Make a screen copy of the canvas:
203  wxSize image_size = aFrame->GetGalCanvas()->GetClientSize();
204 
205  wxClientDC dc( aFrame->GetGalCanvas() );
206  wxBitmap bitmap( image_size.x, image_size.y );
207  wxMemoryDC memdc;
208 
209  memdc.SelectObject( bitmap );
210  memdc.Blit( 0, 0, image_size.x, image_size.y, &dc, 0, 0 );
211  memdc.SelectObject( wxNullBitmap );
212 
213  wxImage image = bitmap.ConvertToImage();
214 
215  if( !image.SaveFile( aFileName, aBitmapType ) )
216  retv = false;
217 
218  image.Destroy();
219  return retv;
220 }
221 
222 
223 wxMenuItem* AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
224  const wxBitmap& aImage, wxItemKind aType = wxITEM_NORMAL )
225 {
226  wxMenuItem* item;
227 
228  item = new wxMenuItem( aMenu, aId, aText, wxEmptyString, aType );
229 
230  // Retrieve the global applicaton show icon option:
231  bool useImagesInMenus;
232  Pgm().CommonSettings()->Read( USE_ICONS_IN_MENUS_KEY, &useImagesInMenus );
233 
234  if( useImagesInMenus )
235  {
236  if( aType == wxITEM_CHECK || aType == wxITEM_RADIO )
237  {
238  #if defined( __WINDOWS__ )
239  item->SetBitmaps( KiBitmap( checked_ok_xpm ), aImage );
240  // A workaround to a strange bug on Windows, wx Widgets 3.0:
241  // size of bitmaps is not taken in account for wxITEM_{CHECK,RADIO} menu
242  // unless we call SetFont
243  item->SetFont( *wxNORMAL_FONT );
244  #endif
245  }
246  else if( aType != wxITEM_RADIO )
247  {
248  item->SetBitmap( aImage );
249  }
250  }
251 
252  aMenu->Append( item );
253 
254  return item;
255 }
256 
257 
258 wxMenuItem* AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
259  const wxString& aHelpText, const wxBitmap& aImage,
260  wxItemKind aType = wxITEM_NORMAL )
261 {
262  wxMenuItem* item;
263 
264  item = new wxMenuItem( aMenu, aId, aText, aHelpText, aType );
265 
266  // Retrieve the global applicaton show icon option:
267  bool useImagesInMenus;
268  Pgm().CommonSettings()->Read( USE_ICONS_IN_MENUS_KEY, &useImagesInMenus );
269 
270  if( useImagesInMenus )
271  {
272  if( aType == wxITEM_CHECK || aType == wxITEM_RADIO )
273  {
274  #if defined( __WINDOWS__ )
275  item->SetBitmaps( KiBitmap( checked_ok_xpm ), aImage );
276  // A workaround to a strange bug on Windows, wx Widgets 3.0:
277  // size of bitmaps is not taken in account for wxITEM_{CHECK,RADIO} menu
278  // unless we call SetFont
279  item->SetFont( *wxNORMAL_FONT );
280  #endif
281  }
282  else if( aType != wxITEM_RADIO )
283  {
284  item->SetBitmap( aImage );
285  }
286  }
287 
288  aMenu->Append( item );
289 
290  return item;
291 }
292 
293 
294 wxMenuItem* AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId,
295  const wxString& aText, const wxBitmap& aImage )
296 {
297  wxMenuItem* item;
298 
299  item = new wxMenuItem( aMenu, aId, aText );
300  item->SetSubMenu( aSubMenu );
301 
302  // Retrieve the global applicaton show icon option:
303  bool useImagesInMenus;
304  Pgm().CommonSettings()->Read( USE_ICONS_IN_MENUS_KEY, &useImagesInMenus );
305 
306  if( useImagesInMenus )
307  item->SetBitmap( aImage );
308 
309  aMenu->Append( item );
310 
311  return item;
312 }
313 
314 
315 wxMenuItem* AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId,
316  const wxString& aText, const wxString& aHelpText,
317  const wxBitmap& aImage )
318 {
319  wxMenuItem* item;
320 
321  item = new wxMenuItem( aMenu, aId, aText, aHelpText );
322  item->SetSubMenu( aSubMenu );
323 
324  // Retrieve the global applicaton show icon option:
325  bool useImagesInMenus;
326  Pgm().CommonSettings()->Read( USE_ICONS_IN_MENUS_KEY, &useImagesInMenus );
327 
328  if( useImagesInMenus )
329  item->SetBitmap( aImage );
330 
331  aMenu->Append( item );
332 
333  return item;
334 }
PNG memory record (file in memory).
Definition: bitmap_types.h:43
bool SaveCanvasImageToFile(EDA_DRAW_FRAME *aFrame, const wxString &aFileName, wxBitmapType aBitmapType)
Save the current view as an image file.
Definition: bitmap.cpp:195
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Function AddMenuItem is an inline helper function to create and insert a menu item with an icon into ...
Definition: bitmap.cpp:223
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition: bitmap.cpp:89
Template specialization to enable wxStrings for certain containers (e.g. unordered_map) ...
Definition: bitmap.cpp:55
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:66
wxBitmap KiScaledBitmap(BITMAP_DEF aBitmap, EDA_BASE_FRAME *aWindow)
Construct a wxBitmap from a memory record, scaling it if device DPI demands it.
Definition: bitmap.cpp:116
The base class for create windows for drawing purpose.
Definition: draw_frame.h:78
const unsigned char * png
Definition: bitmap_types.h:45
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:79
result_type operator()(argument_type const &id) const noexcept
Definition: bitmap.cpp:61
wxBitmap * KiBitmapNew(BITMAP_DEF aBitmap)
Allocate a wxBitmap on heap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:185
SCALED_BITMAP_ID argument_type
Definition: bitmap.cpp:58
Base window classes and related definitions.
#define ICON_SCALE_KEY
Definition: pgm_base.h:45
BITMAP_DEF bitmap
Definition: bitmap.cpp:45
see class PGM_BASE
Class EDA_BASE_FRAME is the base frame for deriving all KiCad main window classes.
VTBL_ENTRY wxConfigBase * CommonSettings() const
Definition: pgm_base.h:187
#define USE_ICONS_IN_MENUS_KEY
Definition: pgm_base.h:44
The common library.
void KiScaledSeparator(wxAuiToolBar *aToolbar, EDA_BASE_FRAME *aWindow)
Add a separator to the given toolbar scaled the same way as KiScaledBitmap.
Definition: bitmap.cpp:167
EDA_DRAW_PANEL_GAL * GetGalCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
Definition: draw_frame.h:928
bool operator==(SCALED_BITMAP_ID const &other) const noexcept
Definition: bitmap.cpp:48
static int get_scale_factor(EDA_BASE_FRAME *aWindow)
Definition: bitmap.cpp:104