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
53 struct SCALED_BITMAP_ID {
54 BITMAPS bitmap;
55 int scale;
56
operator ==SCALED_BITMAP_ID57 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 {
67 typedef SCALED_BITMAP_ID argument_type;
68 typedef std::size_t result_type;
69
operator ()std::hash70 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
GetBitmapStore()93 BITMAP_STORE* GetBitmapStore()
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
KiBitmap(BITMAPS aBitmap,int aHeightTag)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
KiBitmap(const BITMAP_OPAQUE * aBitmap)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
KiIconScale(wxWindow * aWindow)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
get_scale_factor(wxWindow * aWindow)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
KiScaledBitmap(BITMAPS aBitmap,wxWindow * aWindow,int aHeight,bool aQuantized)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
ClearScaledBitmapCache()173 void ClearScaledBitmapCache()
174 {
175 std::lock_guard<std::mutex> guard( s_BitmapCacheMutex );
176 s_ScaledBitmapCache.clear();
177 }
178
179
KiScaledBitmap(const wxBitmap & aBitmap,wxWindow * aWindow)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
KiBitmapNew(BITMAPS aBitmap)199 wxBitmap* KiBitmapNew( BITMAPS aBitmap )
200 {
201 wxBitmap* bitmap = new wxBitmap( GetBitmapStore()->GetBitmap( aBitmap ) );
202
203 return bitmap;
204 }
205
206
SaveCanvasImageToFile(EDA_DRAW_FRAME * aFrame,const wxString & aFileName,BITMAP_TYPE aBitmapType)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
AddBitmapToMenuItem(wxMenuItem * aMenu,const wxBitmap & aImage)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
AddMenuItem(wxMenu * aMenu,int aId,const wxString & aText,const wxBitmap & aImage,wxItemKind aType=wxITEM_NORMAL)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
AddMenuItem(wxMenu * aMenu,int aId,const wxString & aText,const wxString & aHelpText,const wxBitmap & aImage,wxItemKind aType=wxITEM_NORMAL)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
AddMenuItem(wxMenu * aMenu,wxMenu * aSubMenu,int aId,const wxString & aText,const wxBitmap & aImage)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
AddMenuItem(wxMenu * aMenu,wxMenu * aSubMenu,int aId,const wxString & aText,const wxString & aHelpText,const wxBitmap & aImage)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 }
307