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 <[email protected]>
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
50static 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
64namespace 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
88static std::unordered_map<SCALED_BITMAP_ID, wxBitmap> s_ScaledBitmapCache;
89
90static 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
105wxBitmap 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
112wxBitmap 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
122int KiIconScale( wxWindow* aWindow )
123{
124 #if defined( __WXMSW__) && wxCHECK_VERSION( 3, 1, 6 )
125 // Basically don't try and scale within kicad and let wx do its thing
126 // with wx introducing bitmap bundles, it will auto scale automatically with dpi
127 // the issue is, none of the scaling factors have any tie to system scaling
128 // this means wx is actually going to scale again causing even more distorted icons
129 return 4;
130 #else
131 const int vert_size = aWindow->ConvertDialogToPixels( wxSize( 0, 8 ) ).y;
132
133 // Autoscale won't exceed unity until the system has quite high resolution,
134 // because we don't want the icons to look obviously scaled on a system
135 // where it's easy to see it.
136
137 if( vert_size > 34 ) return 8;
138 else if( vert_size > 29 ) return 7;
139 else if( vert_size > 24 ) return 6;
140 else return 4;
141 #endif
142}
143
144
145static int get_scale_factor( wxWindow* aWindow )
146{
147 int requested_scale = Pgm().GetCommonSettings()->m_Appearance.icon_scale;
148
149 if( requested_scale > 0 )
150 return requested_scale;
151 else
152 return KiIconScale( aWindow );
153}
154
155
156wxBitmap KiScaledBitmap( BITMAPS aBitmap, wxWindow* aWindow, int aHeight, bool aQuantized )
157{
158 // Bitmap conversions are cached because they can be slow.
159 int scale = get_scale_factor( aWindow );
160
161 if( aQuantized )
162 scale = KiROUND( (double) scale / 4.0 ) * 4;
163
164 SCALED_BITMAP_ID id = { static_cast<BITMAPS>( aBitmap ), scale };
165
166 std::lock_guard<std::mutex> guard( s_BitmapCacheMutex );
167 auto it = s_ScaledBitmapCache.find( id );
168
169 if( it != s_ScaledBitmapCache.end() )
170 {
171 return it->second;
172 }
173 else
174 {
175 wxBitmap bitmap = GetBitmapStore()->GetBitmapScaled( aBitmap, scale, aHeight );
176 return s_ScaledBitmapCache.emplace( id, bitmap ).first->second;
177 }
178}
179
180
182{
183 std::lock_guard<std::mutex> guard( s_BitmapCacheMutex );
184 s_ScaledBitmapCache.clear();
185}
186
187
188wxBitmap KiScaledBitmap( const wxBitmap& aBitmap, wxWindow* aWindow )
189{
190 const int scale = get_scale_factor( aWindow );
191
192 if( scale == 4 )
193 {
194 return wxBitmap( aBitmap );
195 }
196 else
197 {
198 wxImage image = aBitmap.ConvertToImage();
199 image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
200 wxIMAGE_QUALITY_BILINEAR );
201
202 return wxBitmap( image );
203 }
204}
205
206
207wxBitmap* KiBitmapNew( BITMAPS aBitmap )
208{
209 wxBitmap* bitmap = new wxBitmap( GetBitmapStore()->GetBitmap( aBitmap ) );
210
211 return bitmap;
212}
213
214
215bool SaveCanvasImageToFile( EDA_DRAW_FRAME* aFrame, const wxString& aFileName,
216 BITMAP_TYPE aBitmapType )
217{
218 wxCHECK( aFrame != nullptr, false );
219
220 bool retv = true;
221
222 // Make a screen copy of the canvas:
223 wxSize image_size = aFrame->GetCanvas()->GetClientSize();
224
225 wxClientDC dc( aFrame->GetCanvas() );
226 wxBitmap bitmap( image_size.x, image_size.y );
227 wxMemoryDC memdc;
228
229 memdc.SelectObject( bitmap );
230 memdc.Blit( 0, 0, image_size.x, image_size.y, &dc, 0, 0 );
231 memdc.SelectObject( wxNullBitmap );
232
233 wxImage image = bitmap.ConvertToImage();
234
235 wxBitmapType type = wxBITMAP_TYPE_PNG;
236 switch( aBitmapType )
237 {
238 case BITMAP_TYPE::PNG: type = wxBITMAP_TYPE_PNG; break;
239 case BITMAP_TYPE::BMP: type = wxBITMAP_TYPE_BMP; break;
240 case BITMAP_TYPE::JPG: type = wxBITMAP_TYPE_JPEG; break;
241 }
242
243 if( !image.SaveFile( aFileName, type ) )
244 retv = false;
245
246 image.Destroy();
247 return retv;
248}
249
250
251void AddBitmapToMenuItem( wxMenuItem* aMenu, const wxBitmap& aImage )
252{
253 // Retrieve the global application show icon option:
254 bool useImagesInMenus = Pgm().GetCommonSettings()->m_Appearance.use_icons_in_menus;
255
256 wxItemKind menu_type = aMenu->GetKind();
257
258 if( useImagesInMenus && menu_type != wxITEM_CHECK && menu_type != wxITEM_RADIO )
259 {
260 aMenu->SetBitmap( aImage );
261 }
262}
263
264
265wxMenuItem* AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
266 const wxBitmap& aImage, wxItemKind aType = wxITEM_NORMAL )
267{
268 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, wxEmptyString, aType );
269 AddBitmapToMenuItem( item, aImage );
270
271 aMenu->Append( item );
272
273 return item;
274}
275
276
277wxMenuItem* AddMenuItem( wxMenu* aMenu, int aId, const wxString& aText,
278 const wxString& aHelpText, const wxBitmap& aImage,
279 wxItemKind aType = wxITEM_NORMAL )
280{
281 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, aHelpText, aType );
282 AddBitmapToMenuItem( item, aImage );
283
284 aMenu->Append( item );
285
286 return item;
287}
288
289
290wxMenuItem* AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId,
291 const wxString& aText, const wxBitmap& aImage )
292{
293 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText );
294 item->SetSubMenu( aSubMenu );
295 AddBitmapToMenuItem( item, aImage );
296
297 aMenu->Append( item );
298
299 return item;
300}
301
302
303wxMenuItem* AddMenuItem( wxMenu* aMenu, wxMenu* aSubMenu, int aId,
304 const wxString& aText, const wxString& aHelpText,
305 const wxBitmap& aImage )
306{
307 wxMenuItem* item = new wxMenuItem( aMenu, aId, aText, aHelpText );
308 item->SetSubMenu( aSubMenu );
309 AddBitmapToMenuItem( item, aImage );
310
311 aMenu->Append( item );
312
313 return item;
314}
wxBitmap * KiBitmapNew(BITMAPS aBitmap)
Allocate a wxBitmap on heap from a memory record, held in a BITMAPS.
Definition: bitmap.cpp:207
static std::unique_ptr< BITMAP_STORE > s_BitmapStore
Definition: bitmap.cpp:50
void ClearScaledBitmapCache()
Wipes out the scaled bitmap cache so that the icon theme can be changed.
Definition: bitmap.cpp:181
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
static std::mutex s_BitmapCacheMutex
Definition: bitmap.cpp:90
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
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:265
BITMAP_STORE * GetBitmapStore()
Definition: bitmap.cpp:93
static std::unordered_map< SCALED_BITMAP_ID, wxBitmap > s_ScaledBitmapCache
Definition: bitmap.cpp:88
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:156
static int get_scale_factor(wxWindow *aWindow)
Definition: bitmap.cpp:145
bool SaveCanvasImageToFile(EDA_DRAW_FRAME *aFrame, const wxString &aFileName, BITMAP_TYPE aBitmapType)
Save the current view as an image file.
Definition: bitmap.cpp:215
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:251
BITMAP_TYPE
Definition: bitmap_types.h:42
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:33
Helper to retrieve bitmaps while handling icon themes and scaling.
Definition: bitmap_store.h:44
wxBitmap GetBitmap(BITMAPS aBitmapId, int aHeight=-1)
Retrieves a bitmap from the given bitmap id.
wxBitmap GetBitmapScaled(BITMAPS aBitmapId, int aScaleFactor, int aHeight=-1)
Retrieves a bitmap from the given bitmap id, scaled to a given factor.
The base class for create windows for drawing purpose.
virtual EDA_DRAW_PANEL_GAL * GetCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
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
Base window classes and related definitions.
Definition: bitmap.cpp:64
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
const int scale
PNG memory record (file in memory).
Definition: bitmap_opaque.h:26
const unsigned char * png
Definition: bitmap_opaque.h:27
bool operator==(SCALED_BITMAP_ID const &other) const noexcept
Definition: bitmap.cpp:57
BITMAPS bitmap
Definition: bitmap.cpp:54
SCALED_BITMAP_ID argument_type
Definition: bitmap.cpp:67
result_type operator()(argument_type const &id) const noexcept
Definition: bitmap.cpp:70
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:85