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