KiCad PCB EDA Suite
Loading...
Searching...
No Matches
bitmap2cmp_panel.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) 1992-2010 jean-pierre.charras
5 * Copyright (C) 1992-2023 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#include <bitmap2cmp_frame.h>
26#include <bitmap2component.h>
27#include <bitmap2cmp_panel.h>
28#include <bitmap2cmp_settings.h>
29#include <bitmap_io.h>
30#include <bitmaps.h>
31#include <common.h>
32#include <kiface_base.h>
33#include <math/util.h> // for KiROUND
34#include <pgm_base.h>
35#include <potracelib.h>
36#include <wx/clipbrd.h>
37#include <wx/rawbmp.h>
38#include <wx/msgdlg.h>
39#include <wx/dcclient.h>
40#include <wx/log.h>
41
42#define DEFAULT_DPI 300 // the image DPI used in formats that do not define a DPI
43
44
46 BITMAP2CMP_PANEL_BASE( aParent ),
47 m_parentFrame( aParent ), m_negative( false ),
48 m_aspectRatio( 1.0 )
49{
50 for( wxString unit : { _( "mm" ), _( "Inch" ), _( "DPI" ) } )
51 m_PixelUnit->Append( unit );
52
57
60
61 m_buttonExportFile->Enable( false );
62 m_buttonExportClipboard->Enable( false );
63}
64
65
67{
68}
69
70
72{
73 return m_Notebook->GetCurrentPage();
74}
75
76
78{
79 int u_select = cfg->m_Units;
80
81 if( u_select < 0 || u_select > 2 ) // Validity control
82 u_select = 0;
83
84 m_PixelUnit->SetSelection( u_select );
85
86 m_sliderThreshold->SetValue( cfg->m_Threshold );
87
89 m_checkNegative->SetValue( cfg->m_Negative );
90
91 m_aspectRatio = 1.0;
92 m_aspectRatioCheckbox->SetValue( true );
93
94 int format = cfg->m_LastFormat;
95
96 if( format < 0 || format > FINAL_FMT )
97 format = PCBNEW_KICAD_MOD;
98
99 m_rbOutputFormat->SetSelection( format );
100
101 bool enable = format == PCBNEW_KICAD_MOD;
102 m_chPCBLayer->Enable( enable );
103
104 int last_layer = cfg->m_LastModLayer;
105
106 if( last_layer < 0 || last_layer > static_cast<int>( MOD_LYR_FINAL ) ) // Out of range
107 last_layer = MOD_LYR_FSILKS;
108
109 m_chPCBLayer->SetSelection( last_layer );
110}
111
112
114{
115 cfg->m_Threshold = m_sliderThreshold->GetValue();
116 cfg->m_Negative = m_checkNegative->IsChecked();
117 cfg->m_LastFormat = m_rbOutputFormat->GetSelection();
118 cfg->m_LastModLayer = m_chPCBLayer->GetSelection();
119 cfg->m_Units = m_PixelUnit->GetSelection();
120}
121
122
123void BITMAP2CMP_PANEL::OnPaintInit( wxPaintEvent& event )
124{
125#ifdef __WXMAC__
126 // Otherwise fails due: using wxPaintDC without being in a native paint event
127 wxClientDC pict_dc( m_InitialPicturePanel );
128#else
129 wxPaintDC pict_dc( m_InitialPicturePanel );
130#endif
131
132 m_InitialPicturePanel->PrepareDC( pict_dc );
133
134 // OSX crashes with empty bitmaps (on initial refreshes)
135 if( m_Pict_Bitmap.IsOk() )
136 pict_dc.DrawBitmap( m_Pict_Bitmap, 0, 0, !!m_Pict_Bitmap.GetMask() );
137
138 event.Skip();
139}
140
141
142void BITMAP2CMP_PANEL::OnPaintGreyscale( wxPaintEvent& event )
143{
144#ifdef __WXMAC__
145 // Otherwise fails due: using wxPaintDC without being in a native paint event
146 wxClientDC greyscale_dc( m_GreyscalePicturePanel );
147#else
148 wxPaintDC greyscale_dc( m_GreyscalePicturePanel );
149#endif
150
151 m_GreyscalePicturePanel->PrepareDC( greyscale_dc );
152
153 // OSX crashes with empty bitmaps (on initial refreshes)
154 if( m_Greyscale_Bitmap.IsOk() )
155 greyscale_dc.DrawBitmap( m_Greyscale_Bitmap, 0, 0, !!m_Greyscale_Bitmap.GetMask() );
156
157 event.Skip();
158}
159
160
161void BITMAP2CMP_PANEL::OnPaintBW( wxPaintEvent& event )
162{
163#ifdef __WXMAC__
164 // Otherwise fails due: using wxPaintDC without being in a native paint event
165 wxClientDC nb_dc( m_BNPicturePanel );
166#else
167 wxPaintDC nb_dc( m_BNPicturePanel );
168#endif
169
170 m_BNPicturePanel->PrepareDC( nb_dc );
171
172 if( m_BN_Bitmap.IsOk() )
173 nb_dc.DrawBitmap( m_BN_Bitmap, 0, 0, !!m_BN_Bitmap.GetMask() );
174
175 event.Skip();
176}
177
178
179void BITMAP2CMP_PANEL::OnLoadFile( wxCommandEvent& event )
180{
182}
183
184
185bool BITMAP2CMP_PANEL::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
186{
187 m_Pict_Image.Destroy();
188
189 if( !m_Pict_Image.LoadFile( aFileSet[0] ) )
190 {
191 // LoadFile has its own UI, no need for further failure notification here
192 return false;
193 }
194
195 m_Pict_Bitmap = wxBitmap( m_Pict_Image );
196
197 // Determine image resolution in DPI (does not existing in all formats).
198 // the resolution can be given in bit per inches or bit per cm in file
199
200 int imageDPIx = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
201 int imageDPIy = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
202
203 if( imageDPIx > 1 && imageDPIy > 1 )
204 {
205 if( m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
206 {
207 imageDPIx = KiROUND( imageDPIx * 2.54 );
208 imageDPIy = KiROUND( imageDPIy * 2.54 );
209 }
210 }
211 else // fallback to a default value (DEFAULT_DPI)
212 {
213 imageDPIx = imageDPIy = DEFAULT_DPI;
214 }
215
216 m_InputXValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIx ) );
217 m_InputYValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIy ) );
218
219 int h = m_Pict_Bitmap.GetHeight();
220 int w = m_Pict_Bitmap.GetWidth();
221 m_aspectRatio = (double) w / h;
222
223 m_outputSizeX.SetOriginalDPI( imageDPIx );
225 m_outputSizeY.SetOriginalDPI( imageDPIy );
227
228 // Update display to keep aspect ratio
229 wxCommandEvent dummy;
231
233
234 m_InitialPicturePanel->SetVirtualSize( w, h );
235 m_GreyscalePicturePanel->SetVirtualSize( w, h );
236 m_BNPicturePanel->SetVirtualSize( w, h );
237
238 m_Greyscale_Image.Destroy();
239 m_Greyscale_Image = m_Pict_Image.ConvertToGreyscale( );
240
241 if( m_Pict_Bitmap.GetMask() )
242 {
243 for( int x = 0; x < m_Pict_Bitmap.GetWidth(); x++ )
244 {
245 for( int y = 0; y < m_Pict_Bitmap.GetHeight(); y++ )
246 {
247 if( m_Pict_Image.GetRed( x, y ) == m_Pict_Image.GetMaskRed() &&
248 m_Pict_Image.GetGreen( x, y ) == m_Pict_Image.GetMaskGreen() &&
249 m_Pict_Image.GetBlue( x, y ) == m_Pict_Image.GetMaskBlue() )
250 {
251 m_Greyscale_Image.SetRGB( x, y, 255, 255, 255 );
252 }
253 }
254 }
255 }
256
257 if( m_negative )
259
262 Binarize( (double) m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
263
264 m_buttonExportFile->Enable( true );
265 m_buttonExportClipboard->Enable( true );
266
271
272 return true;
273}
274
275
276// return a string giving the output size, according to the selected unit
278{
279 wxString text;
280
281 if( getUnitFromSelection() == EDA_UNITS::MILLIMETRES )
282 text.Printf( wxS( "%.1f" ), aSize );
283 else if( getUnitFromSelection() == EDA_UNITS::INCHES )
284 text.Printf( wxS( "%.2f" ), aSize );
285 else
286 text.Printf( wxT( "%d" ), KiROUND( aSize ) );
287
288 return text;
289}
290
292{
293 // Note: the image resolution text controls are not modified here, to avoid a race between
294 // text change when entered by user and a text change if it is modified here.
295
296 if( m_Pict_Bitmap.IsOk() )
297 {
298 int h = m_Pict_Bitmap.GetHeight();
299 int w = m_Pict_Bitmap.GetWidth();
300 int nb = m_Pict_Bitmap.GetDepth();
301
302 m_SizeXValue->SetLabel( wxString::Format( wxT( "%d" ), w ) );
303 m_SizeYValue->SetLabel( wxString::Format( wxT( "%d" ), h ) );
304 m_BPPValue->SetLabel( wxString::Format( wxT( "%d" ), nb ) );
305 }
306}
307
308
310{
311 // return the EDA_UNITS from the m_PixelUnit choice
312 switch( m_PixelUnit->GetSelection() )
313 {
314 case 1: return EDA_UNITS::INCHES;
315 case 2: return EDA_UNITS::UNSCALED;
316 case 0:
317 default: return EDA_UNITS::MILLIMETRES;
318 }
319}
320
321
322void BITMAP2CMP_PANEL::OnSizeChangeX( wxCommandEvent& event )
323{
324 double new_size;
325
326 if( m_UnitSizeX->GetValue().ToDouble( &new_size ) )
327 {
328 if( m_aspectRatioCheckbox->GetValue() )
329 {
330 double calculatedY = new_size / m_aspectRatio;
331
332 if( getUnitFromSelection() == EDA_UNITS::UNSCALED )
333 {
334 // for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
335 // just re-scale the other dpi
336 double ratio = new_size / m_outputSizeX.GetOutputSize();
337 calculatedY = m_outputSizeY.GetOutputSize() * ratio;
338 }
339
342 }
343
345 }
346
348}
349
350
351void BITMAP2CMP_PANEL::OnSizeChangeY( wxCommandEvent& event )
352{
353 double new_size;
354
355 if( m_UnitSizeY->GetValue().ToDouble( &new_size ) )
356 {
357 if( m_aspectRatioCheckbox->GetValue() )
358 {
359 double calculatedX = new_size * m_aspectRatio;
360
361 if( getUnitFromSelection() == EDA_UNITS::UNSCALED )
362 {
363 // for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
364 // just re-scale the other dpi
365 double ratio = new_size / m_outputSizeX.GetOutputSize();
366 calculatedX = m_outputSizeX.GetOutputSize() * ratio;
367 }
368
371 }
372
374 }
375
377}
378
379
380void BITMAP2CMP_PANEL::OnSizeUnitChange( wxCommandEvent& event )
381{
385
388}
389
390
391void BITMAP2CMP_PANEL::SetOutputSize( const IMAGE_SIZE& aSizeX, const IMAGE_SIZE& aSizeY )
392{
393 m_outputSizeX = aSizeX;
394 m_outputSizeY = aSizeY;
396
399}
400
401
402void BITMAP2CMP_PANEL::ToggleAspectRatioLock( wxCommandEvent& event )
403{
404 if( m_aspectRatioCheckbox->GetValue() )
405 {
406 // Force display update when aspect ratio is locked
407 wxCommandEvent dummy;
409 }
410}
411
412
413void BITMAP2CMP_PANEL::Binarize( double aThreshold )
414{
415 int h = m_Greyscale_Image.GetHeight();
416 int w = m_Greyscale_Image.GetWidth();
417 unsigned char threshold = aThreshold * 255;
418 unsigned char alpha_thresh = 0.7 * threshold;
419
420 for( int y = 0; y < h; y++ )
421 {
422 for( int x = 0; x < w; x++ )
423 {
424 unsigned char pixout;
425 unsigned char pixin = m_Greyscale_Image.GetGreen( x, y );
426 unsigned char alpha = m_Greyscale_Image.HasAlpha() ? m_Greyscale_Image.GetAlpha( x, y )
427 : wxALPHA_OPAQUE;
428
429 if( pixin < threshold && alpha > alpha_thresh )
430 pixout = 0;
431 else
432 pixout = 255;
433
434 m_NB_Image.SetRGB( x, y, pixout, pixout, pixout );
435 }
436 }
437
438 m_BN_Bitmap = wxBitmap( m_NB_Image );
439}
440
441
443{
444 unsigned char pix;
445 int h = m_Greyscale_Image.GetHeight();
446 int w = m_Greyscale_Image.GetWidth();
447
448 for( int y = 0; y < h; y++ )
449 {
450 for( int x = 0; x < w; x++ )
451 {
452 pix = m_Greyscale_Image.GetGreen( x, y );
453 pix = ~pix;
454 m_Greyscale_Image.SetRGB( x, y, pix, pix, pix );
455 }
456 }
457}
458
459
461{
462 if( m_checkNegative->GetValue() != m_negative )
463 {
465
467 Binarize( (double)m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
468 m_negative = m_checkNegative->GetValue();
469
470 Refresh();
471 }
472}
473
474
475void BITMAP2CMP_PANEL::OnThresholdChange( wxScrollEvent& event )
476{
477 Binarize( (double)m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
478 Refresh();
479}
480
481
482void BITMAP2CMP_PANEL::OnExportToFile( wxCommandEvent& event )
483{
484 // choices of m_rbOutputFormat are expected to be in same order as
485 // OUTPUT_FMT_ID. See bitmap2component.h
486 OUTPUT_FMT_ID format = (OUTPUT_FMT_ID) m_rbOutputFormat->GetSelection();
487 exportBitmap( format );
488}
489
490
491void BITMAP2CMP_PANEL::OnExportToClipboard( wxCommandEvent& event )
492{
493 // choices of m_rbOutputFormat are expected to be in same order as
494 // OUTPUT_FMT_ID. See bitmap2component.h
495 OUTPUT_FMT_ID format = (OUTPUT_FMT_ID) m_rbOutputFormat->GetSelection();
496
497 std::string buffer;
498 ExportToBuffer( buffer, format );
499
500 wxLogNull doNotLog; // disable logging of failed clipboard actions
501
502 // Write buffer to the clipboard
503 if( wxTheClipboard->Open() )
504 {
505 // This data objects are held by the clipboard,
506 // so do not delete them in the app.
507 wxTheClipboard->SetData( new wxTextDataObject( buffer.c_str() ) );
508 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
509 wxTheClipboard->Close();
510 }
511 else
512 {
513 wxMessageBox( _( "Unable to export to the Clipboard") );
514 }
515}
516
517
519{
520 switch( aFormat )
521 {
526 }
527}
528
529
530void BITMAP2CMP_PANEL::ExportToBuffer( std::string& aOutput, OUTPUT_FMT_ID aFormat )
531{
532 // Create a potrace bitmap
533 int h = m_NB_Image.GetHeight();
534 int w = m_NB_Image.GetWidth();
535 potrace_bitmap_t* potrace_bitmap = bm_new( w, h );
536
537 if( !potrace_bitmap )
538 {
539 wxString msg;
540 msg.Printf( _( "Error allocating memory for potrace bitmap" ) );
541 wxMessageBox( msg );
542 return;
543 }
544
545 /* fill the bitmap with data */
546 for( int y = 0; y < h; y++ )
547 {
548 for( int x = 0; x < w; x++ )
549 {
550 unsigned char pix = m_NB_Image.GetGreen( x, y );
551 BM_PUT( potrace_bitmap, x, y, pix ? 0 : 1 );
552 }
553 }
554
555 // choices of m_rbPCBLayer are expected to be in same order as
556 // BMP2CMP_MOD_LAYER. See bitmap2component.h
558
559 if( aFormat == PCBNEW_KICAD_MOD )
560 modLayer = (BMP2CMP_MOD_LAYER) m_chPCBLayer->GetSelection();
561
562 BITMAPCONV_INFO converter( aOutput );
563 converter.ConvertBitmap( potrace_bitmap, aFormat, m_outputSizeX.GetOutputDPI(),
564 m_outputSizeY.GetOutputDPI(), modLayer );
565
566 if( !converter.GetErrorMessages().empty() )
567 wxMessageBox( converter.GetErrorMessages().c_str(), _( "Errors" ) );
568}
569
570
571void BITMAP2CMP_PANEL::OnFormatChange( wxCommandEvent& event )
572{
573 bool enable = m_rbOutputFormat->GetSelection() == PCBNEW_KICAD_MOD;
574 m_chPCBLayer->Enable( enable );
575}
#define DEFAULT_DPI
BMP2CMP_MOD_LAYER
@ MOD_LYR_FSILKS
@ MOD_LYR_FINAL
OUTPUT_FMT_ID
@ FINAL_FMT
@ KICAD_WKS_LOGO
@ PCBNEW_KICAD_MOD
@ POSTSCRIPT_FMT
@ EESCHEMA_FMT
void ExportPcbnewFormat()
Generate a footprint in S expr format.
void ExportDrawingSheetFormat()
Generate a file suitable to be copied into a drawing sheet (.kicad_wks) file.
void ExportEeschemaFormat()
Generate a schematic library which contains one component: the logo.
void ExportPostScriptFormat()
Generate a postscript file.
Class BITMAP2CMP_PANEL_BASE.
wxScrolledWindow * m_GreyscalePicturePanel
wxScrolledWindow * m_BNPicturePanel
wxScrolledWindow * m_InitialPicturePanel
void OnPaintGreyscale(wxPaintEvent &event) override
void SetOutputSize(const IMAGE_SIZE &aSizeX, const IMAGE_SIZE &aSizeY)
void OnSizeUnitChange(wxCommandEvent &event) override
void OnSizeChangeX(wxCommandEvent &event) override
void OnPaintInit(wxPaintEvent &event) override
void Binarize(double aThreshold)
void OnExportToClipboard(wxCommandEvent &event) override
void LoadSettings(BITMAP2CMP_SETTINGS *aCfg)
wxString FormatOutputSize(double aSize)
IMAGE_SIZE m_outputSizeX
void ToggleAspectRatioLock(wxCommandEvent &event) override
bool OpenProjectFiles(const std::vector< wxString > &aFilenames, int aCtl=0)
wxWindow * GetCurrentPage()
BITMAP2CMP_PANEL(BITMAP2CMP_FRAME *aParent)
void OnExportToFile(wxCommandEvent &event) override
void SaveSettings(BITMAP2CMP_SETTINGS *aCfg)
void OnThresholdChange(wxScrollEvent &event) override
void ExportToBuffer(std::string &aOutput, OUTPUT_FMT_ID aFormat)
generate a export data of the current bitmap.
BITMAP2CMP_FRAME * m_parentFrame
void OnFormatChange(wxCommandEvent &event) override
void OnLoadFile(wxCommandEvent &event) override
wxBitmap m_Greyscale_Bitmap
EDA_UNITS getUnitFromSelection()
void OnNegativeClicked(wxCommandEvent &event) override
void OnPaintBW(wxPaintEvent &event) override
IMAGE_SIZE m_outputSizeY
void exportBitmap(OUTPUT_FMT_ID aFormat)
void OnSizeChangeY(wxCommandEvent &event) override
std::string & GetErrorMessages()
int ConvertBitmap(potrace_bitmap_t *aPotrace_bitmap, OUTPUT_FMT_ID aFormat, int aDpi_X, int aDpi_Y, BMP2CMP_MOD_LAYER aModLayer)
Run the conversion of the bitmap.
void SetUnit(EDA_UNITS aUnit)
double GetOutputSize()
void SetOriginalDPI(int aDPI)
void SetOutputSizeFromInitialImageSize()
void SetOutputSize(double aSize, EDA_UNITS aUnit)
void SetOriginalSizePixels(int aPixels)
The common library.
#define _(s)
EDA_UNITS
Definition: eda_units.h:46
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
see class PGM_BASE
std::vector< FAB_LAYER_COLOR > dummy
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:118