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-2021 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/gdicmn.h>
29 #include <wx/mstream.h>
30 #include <wx/menu.h>
31 #include <wx/menuitem.h>
32 #include <wx/aui/auibar.h>
33 #include <wx/dcclient.h>
34 #include <wx/dcmemory.h>
35 
36 #include <cstdint>
37 #include <mutex>
38 #include <unordered_map>
39 
40 #include <asset_archive.h>
41 #include <bitmaps.h>
42 #include <bitmap_store.h>
43 #include <bitmaps/bitmap_opaque.h> // for pcb_calculator compatibility shim
44 #include <pgm_base.h>
45 #include <eda_base_frame.h>
46 #include <eda_draw_frame.h>
47 #include <paths.h>
48 
49 
50 static std::unique_ptr<BITMAP_STORE> s_BitmapStore;
51 
52 
55  int scale;
56 
57  bool operator==( SCALED_BITMAP_ID const& other ) const noexcept
58  {
59  return bitmap == other.bitmap && scale == other.scale;
60  }
61 };
62 
63 
64 namespace std {
65  template<> struct hash<SCALED_BITMAP_ID>
66  {
68  typedef std::size_t result_type;
69 
70  result_type operator()( argument_type const& id ) const noexcept
71  {
72  static const bool sz64 = sizeof( uintptr_t ) == 8;
73  static const size_t mask = sz64 ? 0xF000000000000000uLL : 0xF0000000uL;
74  static const size_t offset = sz64 ? 60 : 28;
75 
76  // The hash only needs to be fast and simple, not necessarily accurate - a collision
77  // only makes things slower, not broken. BITMAPS is a pointer, so the most
78  // significant several bits are generally going to be the same for all. Just convert
79  // it to an integer and stuff the scale factor into those bits.
80  return
81  ( (uintptr_t)( id.bitmap ) & ~mask ) |
82  ( ( (uintptr_t)( id.scale ) & 0xF ) << offset );
83  }
84  };
85 }
86 
87 
88 static std::unordered_map<SCALED_BITMAP_ID, wxBitmap> s_ScaledBitmapCache;
89 
90 static std::mutex s_BitmapCacheMutex;
91 
92 
94 {
95  if( !s_BitmapStore )
96  {
97  wxFileName path( PATHS::GetStockDataPath() + wxT( "/resources" ), wxT( "images.zip" ) );
98  s_BitmapStore = std::make_unique<BITMAP_STORE>();
99  }
100 
101  return s_BitmapStore.get();
102 }
103 
104 
105 wxBitmap KiBitmap( BITMAPS aBitmap, int aHeightTag )
106 {
107  return GetBitmapStore()->GetBitmap( aBitmap, aHeightTag );
108 }
109 
110 
111 // TODO: Remove this once pcb_calculator images are moved into the main bitmap system
112 wxBitmap KiBitmap( const BITMAP_OPAQUE* aBitmap )
113 {
114  wxMemoryInputStream is( aBitmap->png, aBitmap->byteCount );
115  wxImage image( is, wxBITMAP_TYPE_PNG );
116  wxBitmap bitmap( image );
117 
118  return bitmap;
119 }
120 
121 
122 int KiIconScale( wxWindow* aWindow )
123 {
124  const int vert_size = aWindow->ConvertDialogToPixels( wxSize( 0, 8 ) ).y;
125 
126  // Autoscale won't exceed unity until the system has quite high resolution,
127  // because we don't want the icons to look obviously scaled on a system
128  // where it's easy to see it.
129 
130  if( vert_size > 34 ) return 8;
131  else if( vert_size > 29 ) return 7;
132  else if( vert_size > 24 ) return 6;
133  else return 4;
134 }
135 
136 
137 static int get_scale_factor( wxWindow* aWindow )
138 {
139  int requested_scale = Pgm().GetCommonSettings()->m_Appearance.icon_scale;
140 
141  if( requested_scale > 0 )
142  return requested_scale;
143  else
144  return KiIconScale( aWindow );
145 }
146 
147 
148 wxBitmap KiScaledBitmap( BITMAPS aBitmap, wxWindow* aWindow, int aHeight, bool aQuantized )
149 {
150  // Bitmap conversions are cached because they can be slow.
151  int scale = get_scale_factor( aWindow );
152 
153  if( aQuantized )
154  scale = KiROUND( (double) scale / 4.0 ) * 4;
155 
156  SCALED_BITMAP_ID id = { static_cast<BITMAPS>( aBitmap ), scale };
157 
158  std::lock_guard<std::mutex> guard( s_BitmapCacheMutex );
159  auto it = s_ScaledBitmapCache.find( id );
160 
161  if( it != s_ScaledBitmapCache.end() )
162  {
163  return it->second;
164  }
165  else
166  {
167  wxBitmap bitmap = GetBitmapStore()->GetBitmapScaled( aBitmap, scale, aHeight );
168  return s_ScaledBitmapCache.emplace( id, bitmap ).first->second;
169  }
170 }
171 
172 
174 {
175  std::lock_guard<std::mutex> guard( s_BitmapCacheMutex );
176  s_ScaledBitmapCache.clear();
177 }
178 
179 
180 wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, wxWindow* aWindow )
181 {
182  const int scale = get_scale_factor( aWindow );
183 
184  if( scale == 4 )
185  {
186  return wxBitmap( aBitmap );
187  }
188  else
189  {
190  wxImage image = aBitmap.ConvertToImage();
191  image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
192  wxIMAGE_QUALITY_BILINEAR );
193 
194  return wxBitmap( image );
195  }
196 }
197 
198 
199 wxBitmap* KiBitmapNew( BITMAPS aBitmap )
200 {
201  wxBitmap* bitmap = new wxBitmap( GetBitmapStore()->GetBitmap( aBitmap ) );
202 
203  return bitmap;
204 }
205 
206 
207 bool SaveCanvasImageToFile( EDA_DRAW_FRAME* aFrame, const wxString& aFileName,
208  BITMAP_TYPE aBitmapType )
209 {
210  wxCHECK( aFrame != nullptr, false );
211 
212  bool retv = true;
213 
214  // Make a screen copy of the canvas:
215  wxSize image_size = aFrame->GetCanvas()->GetClientSize();
216 
217  wxClientDC dc( aFrame->GetCanvas() );
218  wxBitmap bitmap( image_size.x, image_size.y );
219  wxMemoryDC memdc;
220 
221  memdc.SelectObject( bitmap );
222  memdc.Blit( 0, 0, image_size.x, image_size.y, &dc, 0, 0 );
223  memdc.SelectObject( wxNullBitmap );
224 
225  wxImage image = bitmap.ConvertToImage();
226 
227  wxBitmapType type = wxBITMAP_TYPE_PNG;
228  switch( aBitmapType )
229  {
230  case BITMAP_TYPE::PNG: type = wxBITMAP_TYPE_PNG; break;
231  case BITMAP_TYPE::BMP: type = wxBITMAP_TYPE_BMP; break;
232  case BITMAP_TYPE::JPG: type = wxBITMAP_TYPE_JPEG; break;
233  }
234 
235  if( !image.SaveFile( aFileName, type ) )
236  retv = false;
237 
238  image.Destroy();
239  return retv;
240 }
241 
242 
243 void AddBitmapToMenuItem( wxMenuItem* aMenu, const wxBitmap& aImage )
244 {
245  // Retrieve the global application show icon option:
246  bool useImagesInMenus = Pgm().GetCommonSettings()->m_Appearance.use_icons_in_menus;
247 
248  wxItemKind menu_type = aMenu->GetKind();
249 
250  if( useImagesInMenus && menu_type != wxITEM_CHECK && menu_type != wxITEM_RADIO )
251  {
252  aMenu->SetBitmap( aImage );
253  }
254 }
255 
256 
257 wxMenuItem* AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
258  const wxBitmap& aImage, wxItemKind aType = wxITEM_NORMAL )
259 {
260  wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, wxEmptyString, aType );
261  AddBitmapToMenuItem( item, aImage );
262 
263  aMenu->Append( item );
264 
265  return item;
266 }
267 
268 
269 wxMenuItem* AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
270  const wxString& aHelpText, const wxBitmap& aImage,
271  wxItemKind aType = wxITEM_NORMAL )
272 {
273  wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, aHelpText, aType );
274  AddBitmapToMenuItem( item, aImage );
275 
276  aMenu->Append( item );
277 
278  return item;
279 }
280 
281 
282 wxMenuItem* AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId,
283  const wxString& aText, const wxBitmap& aImage )
284 {
285  wxMenuItem* item = new wxMenuItem( aMenu, aId, aText );
286  item->SetSubMenu( aSubMenu );
287  AddBitmapToMenuItem( item, aImage );
288 
289  aMenu->Append( item );
290 
291  return item;
292 }
293 
294 
295 wxMenuItem* AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId,
296  const wxString& aText, const wxString& aHelpText,
297  const wxBitmap& aImage )
298 {
299  wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, aHelpText );
300  item->SetSubMenu( aSubMenu );
301  AddBitmapToMenuItem( item, aImage );
302 
303  aMenu->Append( item );
304 
305  return item;
306 }
PNG memory record (file in memory).
Definition: bitmap_opaque.h:25
wxBitmap GetBitmapScaled(BITMAPS aBitmapId, int aScaleFactor, int aHeight=-1)
Retrieves a bitmap from the given bitmap id, scaled to a given factor.
static std::unordered_map< SCALED_BITMAP_ID, wxBitmap > s_ScaledBitmapCache
Definition: bitmap.cpp:88
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
Definition: bitmap.cpp:257
static int get_scale_factor(wxWindow *aWindow)
Definition: bitmap.cpp:137
virtual EDA_DRAW_PANEL_GAL * GetCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
Helper to retrieve bitmaps while handling icon themes and scaling.
Definition: bitmap_store.h:43
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition: bitmap.cpp:122
BITMAP_TYPE
Definition: bitmap_types.h:41
Definition: bitmap.cpp:64
void ClearScaledBitmapCache()
Wipes out the scaled bitmap cache so that the icon theme can be changed.
Definition: bitmap.cpp:173
The base class for create windows for drawing purpose.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
static wxString GetStockDataPath(bool aRespectRunFromBuildDir=true)
Gets the stock (install) data path, which is the base path for things like scripting,...
Definition: paths.cpp:150
const unsigned char * png
Definition: bitmap_opaque.h:27
result_type operator()(argument_type const &id) const noexcept
Definition: bitmap.cpp:70
static std::unique_ptr< BITMAP_STORE > s_BitmapStore
Definition: bitmap.cpp:50
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:243
bool SaveCanvasImageToFile(EDA_DRAW_FRAME *aFrame, const wxString &aFileName, BITMAP_TYPE aBitmapType)
Save the current view as an image file.
Definition: bitmap.cpp:207
SCALED_BITMAP_ID argument_type
Definition: bitmap.cpp:67
Base window classes and related definitions.
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
wxBitmap KiScaledBitmap(BITMAPS aBitmap, wxWindow *aWindow, int aHeight, bool aQuantized)
Construct a wxBitmap from a memory record, scaling it if device DPI demands it.
Definition: bitmap.cpp:148
wxBitmap * KiBitmapNew(BITMAPS aBitmap)
Allocate a wxBitmap on heap from a memory record, held in a BITMAPS.
Definition: bitmap.cpp:199
BITMAPS bitmap
Definition: bitmap.cpp:54
const int scale
see class PGM_BASE
static std::mutex s_BitmapCacheMutex
Definition: bitmap.cpp:90
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:32
wxBitmap GetBitmap(BITMAPS aBitmapId, int aHeight=-1)
Retrieves a bitmap from the given bitmap id.
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
bool operator==(SCALED_BITMAP_ID const &other) const noexcept
Definition: bitmap.cpp:57
BITMAP_STORE * GetBitmapStore()
Definition: bitmap.cpp:93