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 The 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 <common.h>
31#include <math/util.h> // for KiROUND
32#include <potracelib.h>
33#include <vector>
34#include <wx/arrstr.h>
35#include <wx/clipbrd.h>
36#include <wx/dnd.h>
37#include <wx/rawbmp.h>
38#include <wx/msgdlg.h>
39#include <wx/dcclient.h>
40#include <wx/log.h>
41#include <wx/string.h>
42
43#define DEFAULT_DPI 300 // the image DPI used in formats that do not define a DPI
44
45
47 BITMAP2CMP_PANEL_BASE( aParent ),
48 m_parentFrame( aParent ),
49 m_negative( false ),
50 m_aspectRatio( 1.0 )
51{
52 for( const wxString& unit : { _( "mm" ), _( "Inch" ), _( "DPI" ) } )
53 m_PixelUnit->Append( unit );
54
59
62
63 m_buttonExportFile->Enable( false );
64 m_buttonExportClipboard->Enable( false );
65
66 m_InitialPicturePanel->SetDropTarget( new DROP_FILE( this ) );
67 m_GreyscalePicturePanel->SetDropTarget( new DROP_FILE( this ) );
68 m_BNPicturePanel->SetDropTarget( new DROP_FILE( this ) );
69}
70
72{
73 return m_Notebook->GetCurrentPage();
74}
75
76
78{
79 if( cfg->m_Units >= 0 && cfg->m_Units < (int) m_PixelUnit->GetCount() )
80 m_PixelUnit->SetSelection( cfg->m_Units );
81
82 m_sliderThreshold->SetValue( cfg->m_Threshold );
83
85 m_checkNegative->SetValue( cfg->m_Negative );
86
87 m_aspectRatio = 1.0;
88 m_aspectRatioCheckbox->SetValue( true );
89
90 switch( cfg->m_LastFormat )
91 {
92 default:
93 case FOOTPRINT_FMT: m_rbFootprint->SetValue( true ); break;
94 case SYMBOL_FMT:
95 case SYMBOL_PASTE_FMT: m_rbSymbol->SetValue( true ); break;
96 case POSTSCRIPT_FMT: m_rbPostscript->SetValue( true ); break;
97 case DRAWING_SHEET_FMT: m_rbWorksheet->SetValue( true ); break;
98 }
99
100 m_layerLabel->Enable( cfg->m_LastFormat == FOOTPRINT_FMT );
101 m_layerCtrl->Enable( cfg->m_LastFormat == FOOTPRINT_FMT );
102
103 if( cfg->m_LastLayer >= 0 && cfg->m_LastLayer < (int) m_layerCtrl->GetCount() )
104 m_layerCtrl->SetSelection( cfg->m_LastLayer );
105}
106
107
109{
110 cfg->m_Threshold = m_sliderThreshold->GetValue();
111 cfg->m_Negative = m_checkNegative->IsChecked();
113 cfg->m_LastLayer = m_layerCtrl->GetSelection();
114 cfg->m_Units = m_PixelUnit->GetSelection();
115}
116
117
118void BITMAP2CMP_PANEL::OnPaintInit( wxPaintEvent& event )
119{
120#ifdef __WXMAC__
121 // Otherwise fails due: using wxPaintDC without being in a native paint event
122 wxClientDC pict_dc( m_InitialPicturePanel );
123#else
124 wxPaintDC pict_dc( m_InitialPicturePanel );
125#endif
126
127 m_InitialPicturePanel->PrepareDC( pict_dc );
128
129 // OSX crashes with empty bitmaps (on initial refreshes)
130 if( m_Pict_Bitmap.IsOk() )
131 pict_dc.DrawBitmap( m_Pict_Bitmap, 0, 0, !!m_Pict_Bitmap.GetMask() );
132
133 event.Skip();
134}
135
136
137void BITMAP2CMP_PANEL::OnPaintGreyscale( wxPaintEvent& event )
138{
139#ifdef __WXMAC__
140 // Otherwise fails due: using wxPaintDC without being in a native paint event
141 wxClientDC greyscale_dc( m_GreyscalePicturePanel );
142#else
143 wxPaintDC greyscale_dc( m_GreyscalePicturePanel );
144#endif
145
146 m_GreyscalePicturePanel->PrepareDC( greyscale_dc );
147
148 // OSX crashes with empty bitmaps (on initial refreshes)
149 if( m_Greyscale_Bitmap.IsOk() )
150 greyscale_dc.DrawBitmap( m_Greyscale_Bitmap, 0, 0, !!m_Greyscale_Bitmap.GetMask() );
151
152 event.Skip();
153}
154
155
156void BITMAP2CMP_PANEL::OnPaintBW( wxPaintEvent& event )
157{
158#ifdef __WXMAC__
159 // Otherwise fails due: using wxPaintDC without being in a native paint event
160 wxClientDC nb_dc( m_BNPicturePanel );
161#else
162 wxPaintDC nb_dc( m_BNPicturePanel );
163#endif
164
165 m_BNPicturePanel->PrepareDC( nb_dc );
166
167 if( m_BN_Bitmap.IsOk() )
168 nb_dc.DrawBitmap( m_BN_Bitmap, 0, 0, !!m_BN_Bitmap.GetMask() );
169
170 event.Skip();
171}
172
173
174void BITMAP2CMP_PANEL::OnLoadFile( wxCommandEvent& event )
175{
177}
178
179
180bool BITMAP2CMP_PANEL::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
181{
182 m_Pict_Image.Destroy();
183
184 if( !m_Pict_Image.LoadFile( aFileSet[0] ) )
185 {
186 // LoadFile has its own UI, no need for further failure notification here
187 return false;
188 }
189
190 m_Pict_Bitmap = wxBitmap( m_Pict_Image );
191
192 // Determine image resolution in DPI (does not existing in all formats).
193 // the resolution can be given in bit per inches or bit per cm in file
194
195 int imageDPIx = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
196 int imageDPIy = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
197
198 if( imageDPIx > 1 && imageDPIy > 1 )
199 {
200 if( m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
201 {
202 imageDPIx = KiROUND( imageDPIx * 2.54 );
203 imageDPIy = KiROUND( imageDPIy * 2.54 );
204 }
205 }
206 else // fallback to a default value (DEFAULT_DPI)
207 {
208 imageDPIx = imageDPIy = DEFAULT_DPI;
209 }
210
211 m_InputXValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIx ) );
212 m_InputYValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIy ) );
213
214 int h = m_Pict_Bitmap.GetHeight();
215 int w = m_Pict_Bitmap.GetWidth();
216 m_aspectRatio = (double) w / h;
217
218 m_outputSizeX.SetOriginalDPI( imageDPIx );
220 m_outputSizeY.SetOriginalDPI( imageDPIy );
222
223 // Update display to keep aspect ratio
224 wxCommandEvent dummy;
226
228
229 m_InitialPicturePanel->SetVirtualSize( w, h );
230 m_GreyscalePicturePanel->SetVirtualSize( w, h );
231 m_BNPicturePanel->SetVirtualSize( w, h );
232
233 m_Greyscale_Image.Destroy();
234 m_Greyscale_Image = m_Pict_Image.ConvertToGreyscale( );
235
236 if( m_Pict_Bitmap.GetMask() )
237 {
238 for( int x = 0; x < m_Pict_Bitmap.GetWidth(); x++ )
239 {
240 for( int y = 0; y < m_Pict_Bitmap.GetHeight(); y++ )
241 {
242 if( m_Pict_Image.GetRed( x, y ) == m_Pict_Image.GetMaskRed()
243 && m_Pict_Image.GetGreen( x, y ) == m_Pict_Image.GetMaskGreen()
244 && m_Pict_Image.GetBlue( x, y ) == m_Pict_Image.GetMaskBlue() )
245 {
246 m_Greyscale_Image.SetRGB( x, y, 255, 255, 255 );
247 }
248 }
249 }
250 }
251
252 if( m_negative )
254
257 binarize( (double)m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
258
259 m_buttonExportFile->Enable( true );
260 m_buttonExportClipboard->Enable( true );
261
266
267 return true;
268}
269
270
271// return a string giving the output size, according to the selected unit
273{
274 wxString text;
275
276 if( getUnitFromSelection() == EDA_UNITS::MM )
277 text.Printf( wxS( "%.1f" ), aSize );
278 else if( getUnitFromSelection() == EDA_UNITS::INCH )
279 text.Printf( wxS( "%.2f" ), aSize );
280 else
281 text.Printf( wxT( "%d" ), KiROUND( aSize ) );
282
283 return text;
284}
285
287{
288 // Note: the image resolution text controls are not modified here, to avoid a race between
289 // text change when entered by user and a text change if it is modified here.
290
291 if( m_Pict_Bitmap.IsOk() )
292 {
293 m_SizeXValue->SetLabel( wxString::Format( wxT( "%d" ), m_Pict_Bitmap.GetWidth() ) );
294 m_SizeYValue->SetLabel( wxString::Format( wxT( "%d" ), m_Pict_Bitmap.GetHeight() ) );
295 m_BPPValue->SetLabel( wxString::Format( wxT( "%d" ), m_Pict_Bitmap.GetDepth() ) );
296 }
297}
298
299
301{
302 // return the EDA_UNITS from the m_PixelUnit choice
303 switch( m_PixelUnit->GetSelection() )
304 {
305 case 1: return EDA_UNITS::INCH;
306 case 2: return EDA_UNITS::UNSCALED;
307 case 0:
308 default: return EDA_UNITS::MM;
309 }
310}
311
312
313void BITMAP2CMP_PANEL::OnSizeChangeX( wxCommandEvent& event )
314{
315 double new_size;
316
317 if( m_UnitSizeX->GetValue().ToDouble( &new_size ) )
318 {
319 if( m_aspectRatioCheckbox->GetValue() )
320 {
321 double calculatedY = new_size / m_aspectRatio;
322
323 if( getUnitFromSelection() == EDA_UNITS::UNSCALED )
324 {
325 // for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
326 // just re-scale the other dpi
327 double ratio = new_size / m_outputSizeX.GetOutputSize();
328 calculatedY = m_outputSizeY.GetOutputSize() * ratio;
329 }
330
333 }
334
336 }
337
339}
340
341
342void BITMAP2CMP_PANEL::OnSizeChangeY( wxCommandEvent& event )
343{
344 double new_size;
345
346 if( m_UnitSizeY->GetValue().ToDouble( &new_size ) )
347 {
348 if( m_aspectRatioCheckbox->GetValue() )
349 {
350 double calculatedX = new_size * m_aspectRatio;
351
352 if( getUnitFromSelection() == EDA_UNITS::UNSCALED )
353 {
354 // for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
355 // just re-scale the other dpi
356 double ratio = new_size / m_outputSizeX.GetOutputSize();
357 calculatedX = m_outputSizeX.GetOutputSize() * ratio;
358 }
359
362 }
363
365 }
366
368}
369
370
371void BITMAP2CMP_PANEL::OnSizeUnitChange( wxCommandEvent& event )
372{
376
379}
380
381
382void BITMAP2CMP_PANEL::SetOutputSize( const IMAGE_SIZE& aSizeX, const IMAGE_SIZE& aSizeY )
383{
384 m_outputSizeX = aSizeX;
385 m_outputSizeY = aSizeY;
387
390}
391
392
393void BITMAP2CMP_PANEL::ToggleAspectRatioLock( wxCommandEvent& event )
394{
395 if( m_aspectRatioCheckbox->GetValue() )
396 {
397 // Force display update when aspect ratio is locked
398 wxCommandEvent dummy;
400 }
401}
402
403
404void BITMAP2CMP_PANEL::binarize( double aThreshold )
405{
406 unsigned char threshold = aThreshold * 255;
407 unsigned char alpha_thresh = 0.7 * threshold;
408
409 for( int y = 0; y < m_Greyscale_Image.GetHeight(); y++ )
410 {
411 for( int x = 0; x < m_Greyscale_Image.GetWidth(); x++ )
412 {
413 unsigned char pixel = m_Greyscale_Image.GetGreen( x, y );
414 unsigned char alpha = m_Greyscale_Image.HasAlpha() ? m_Greyscale_Image.GetAlpha( x, y )
415 : wxALPHA_OPAQUE;
416
417 if( pixel < threshold && alpha > alpha_thresh )
418 pixel = 0;
419 else
420 pixel = 255;
421
422 m_NB_Image.SetRGB( x, y, pixel, pixel, pixel );
423 }
424 }
425
426 m_BN_Bitmap = wxBitmap( m_NB_Image );
427}
428
429
431{
432 for( int y = 0; y < m_Greyscale_Image.GetHeight(); y++ )
433 {
434 for( int x = 0; x < m_Greyscale_Image.GetWidth(); x++ )
435 {
436 unsigned char pixel = m_Greyscale_Image.GetGreen( x, y );
437 pixel = ~pixel;
438 m_Greyscale_Image.SetRGB( x, y, pixel, pixel, pixel );
439 }
440 }
441}
442
443
445{
446 if( m_checkNegative->GetValue() != m_negative )
447 {
449
451 binarize( (double)m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
452 m_negative = m_checkNegative->GetValue();
453
454 Refresh();
455 }
456}
457
458
459void BITMAP2CMP_PANEL::OnThresholdChange( wxScrollEvent& event )
460{
461 binarize( (double)m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
462 Refresh();
463}
464
465
466void BITMAP2CMP_PANEL::OnExportToFile( wxCommandEvent& event )
467{
468 switch( getOutputFormat() )
469 {
470 case SYMBOL_FMT:
475 }
476}
477
478
480{
481 if( m_rbSymbol->GetValue() )
482 return SYMBOL_FMT;
483 else if( m_rbPostscript->GetValue() )
484 return POSTSCRIPT_FMT;
485 else if( m_rbWorksheet->GetValue() )
486 return DRAWING_SHEET_FMT;
487 else
488 return FOOTPRINT_FMT;
489}
490
491
492void BITMAP2CMP_PANEL::OnExportToClipboard( wxCommandEvent& event )
493{
494 std::string buffer;
496 ExportToBuffer( buffer, format );
497
498 wxLogNull doNotLog; // disable logging of failed clipboard actions
499
500 // Write buffer to the clipboard
501 if( wxTheClipboard->Open() )
502 {
503 // This data objects are held by the clipboard,
504 // so do not delete them in the app.
505 wxTheClipboard->SetData( new wxTextDataObject( buffer.c_str() ) );
506 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
507 wxTheClipboard->Close();
508 }
509 else
510 {
511 wxMessageBox( _( "Unable to export to the Clipboard") );
512 }
513}
514
515
516void BITMAP2CMP_PANEL::ExportToBuffer( std::string& aOutput, OUTPUT_FMT_ID aFormat )
517{
518 // Create a potrace bitmap
519 potrace_bitmap_t* potrace_bitmap = bm_new( m_NB_Image.GetWidth(), m_NB_Image.GetHeight() );
520
521 if( !potrace_bitmap )
522 {
523 wxMessageBox( _( "Error allocating memory for potrace bitmap" ) );
524 return;
525 }
526
527 /* fill the bitmap with data */
528 for( int y = 0; y < m_NB_Image.GetHeight(); y++ )
529 {
530 for( int x = 0; x < m_NB_Image.GetWidth(); x++ )
531 {
532 unsigned char pixel = m_NB_Image.GetGreen( x, y );
533 BM_PUT( potrace_bitmap, x, y, pixel ? 0 : 1 );
534 }
535 }
536
537 wxString layer = wxT( "F.SilkS" );
538
539 if( aFormat == FOOTPRINT_FMT )
540 {
541 switch( m_layerCtrl->GetSelection() )
542 {
543 case 0: layer = wxT( "F.Cu" ); break;
544 case 1: layer = wxT( "F.SilkS" ); break;
545 case 2: layer = wxT( "F.Mask" ); break;
546 case 3: layer = wxT( "Dwgs.User" ); break;
547 case 4: layer = wxT( "Cmts.User" ); break;
548 case 5: layer = wxT( "Eco1.User" ); break;
549 case 6: layer = wxT( "Eco2.User" ); break;
550 case 7: layer = wxT( "F.Fab" ); break;
551 }
552 }
553
554
555
556 WX_STRING_REPORTER reporter;
557 BITMAPCONV_INFO converter( aOutput, reporter );
558
559 converter.ConvertBitmap( potrace_bitmap, aFormat, m_outputSizeX.GetOutputDPI(),
560 m_outputSizeY.GetOutputDPI(), layer );
561
562 if( reporter.HasMessage() )
563 wxMessageBox( reporter.GetMessages(), _( "Errors" ) );
564}
565
566
567void BITMAP2CMP_PANEL::OnFormatChange( wxCommandEvent& event )
568{
569 m_layerLabel->Enable( m_rbFootprint->GetValue() );
570 m_layerCtrl->Enable( m_rbFootprint->GetValue() );
571}
572
573
575 m_panel( panel )
576{
577}
578
579
580bool DROP_FILE::OnDropFiles( wxCoord x, wxCoord y, const wxArrayString& filenames )
581{
582 m_panel->SetFocus();
583
584 // If a file is already loaded
586 {
587 wxString cap = _( "Replace Loaded File?" );
588 wxString msg = _( "There is already a file loaded. Do you want to replace it?" );
589 wxMessageDialog acceptFileDlg( m_panel, msg, cap, wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT );
590 int replace = acceptFileDlg.ShowModal();
591
592 if( replace == wxID_NO )
593 return false;
594 }
595
596 std::vector<wxString> fNameVec;
597 fNameVec.insert( fNameVec.begin(), filenames.begin(), filenames.end() );
598 m_panel->OpenProjectFiles( fNameVec );
599
600 return true;
601}
#define DEFAULT_DPI
OUTPUT_FMT_ID
@ DRAWING_SHEET_FMT
@ SYMBOL_FMT
@ SYMBOL_PASTE_FMT
@ POSTSCRIPT_FMT
@ FOOTPRINT_FMT
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
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
wxRadioButton * m_rbPostscript
wxScrolledWindow * m_InitialPicturePanel
void OnPaintGreyscale(wxPaintEvent &event) override
void binarize(double aThreshold)
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 OnExportToClipboard(wxCommandEvent &event) override
void LoadSettings(BITMAP2CMP_SETTINGS *aCfg)
IMAGE_SIZE m_outputSizeX
IMAGE_SIZE GetOutputSizeX() const
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)
wxString formatOutputSize(double aSize)
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
OUTPUT_FMT_ID getOutputFormat()
void OnSizeChangeY(wxCommandEvent &event) override
int ConvertBitmap(potrace_bitmap_t *aPotrace_bitmap, OUTPUT_FMT_ID aFormat, int aDpi_X, int aDpi_Y, const wxString &aLayer)
Run the conversion of the bitmap.
BITMAP2CMP_PANEL * m_panel
DROP_FILE()=delete
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) override
void SetUnit(EDA_UNITS aUnit)
double GetOutputSize()
void SetOriginalDPI(int aDPI)
int GetOriginalSizePixels()
void SetOutputSizeFromInitialImageSize()
void SetOutputSize(double aSize, EDA_UNITS aUnit)
void SetOriginalSizePixels(int aPixels)
A wrapper for reporting to a wxString object.
Definition: reporter.h:190
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:96
const wxString & GetMessages() const
Definition: reporter.cpp:83
The common library.
#define _(s)
EDA_UNITS
Definition: eda_units.h:48
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
std::vector< FAB_LAYER_COLOR > dummy