You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

944 lines
26 KiB

  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-2019 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. #include "bitmap2cmp_gui.h"
  25. #include "bitmap2component.h"
  26. #include <bitmap_io.h>
  27. #include <bitmaps.h>
  28. #include <build_version.h>
  29. #include <confirm.h>
  30. #include <fctsys.h>
  31. #include <gestfich.h>
  32. #include <kiface_i.h>
  33. #include <kiway.h>
  34. #include <macros.h>
  35. #include <pgm_base.h>
  36. #include <potracelib.h>
  37. #include <wildcards_and_files_ext.h>
  38. #include <wx/clipbrd.h>
  39. #include <wx/rawbmp.h>
  40. #include "bitmap2cmp_gui_base.h"
  41. #define KEYWORD_FRAME_POSX wxT( "Bmconverter_Pos_x" )
  42. #define KEYWORD_FRAME_POSY wxT( "Bmconverter_Pos_y" )
  43. #define KEYWORD_FRAME_SIZEX wxT( "Bmconverter_Size_x" )
  44. #define KEYWORD_FRAME_SIZEY wxT( "Bmconverter_Size_y" )
  45. #define KEYWORD_LAST_INPUT_FILE wxT( "Last_input" )
  46. #define KEYWORD_LAST_OUTPUT_FILE wxT( "Last_output" )
  47. #define KEYWORD_LAST_FORMAT wxT( "Last_format" )
  48. #define KEYWORD_LAST_MODLAYER wxT( "Last_modlayer" )
  49. #define KEYWORD_BINARY_THRESHOLD wxT( "Threshold" )
  50. #define KEYWORD_BW_NEGATIVE wxT( "Negative_choice" )
  51. #define KEYWORD_UNIT_SELECTION wxT( "Unit_selection" )
  52. #define DEFAULT_DPI 300 // the image DPI used in formats that do not define a DPI
  53. IMAGE_SIZE::IMAGE_SIZE()
  54. {
  55. m_outputSize = 0.0;
  56. m_originalDPI = DEFAULT_DPI;
  57. m_originalSizePixels = 0;
  58. m_unit = MILLIMETRES;
  59. }
  60. void IMAGE_SIZE::SetOutputSizeFromInitialImageSize()
  61. {
  62. // Set the m_outputSize value from the m_originalSizePixels and the selected unit
  63. if( m_unit == MILLIMETRES )
  64. {
  65. m_outputSize = (double)GetOriginalSizePixels() / m_originalDPI * 25.4;
  66. }
  67. else if( m_unit == INCHES )
  68. {
  69. m_outputSize = (double)GetOriginalSizePixels() / m_originalDPI;
  70. }
  71. else
  72. {
  73. m_outputSize = m_originalDPI;
  74. }
  75. }
  76. int IMAGE_SIZE::GetOutputDPI()
  77. {
  78. int outputDPI;
  79. if( m_unit == MILLIMETRES )
  80. {
  81. outputDPI = GetOriginalSizePixels() / ( m_outputSize / 25.4 );
  82. }
  83. else if( m_unit == INCHES )
  84. {
  85. outputDPI = GetOriginalSizePixels() / m_outputSize;
  86. }
  87. else
  88. {
  89. outputDPI = KiROUND( m_outputSize );
  90. }
  91. return outputDPI;
  92. }
  93. void IMAGE_SIZE::SetUnit( EDA_UNITS_T aUnit )
  94. {
  95. // Set the unit used for m_outputSize, and convert the old m_outputSize value
  96. // to the value in new unit
  97. if( aUnit == m_unit )
  98. return;
  99. // Convert m_outputSize to mm:
  100. double size_mm;
  101. if( m_unit == MILLIMETRES )
  102. {
  103. size_mm = m_outputSize;
  104. }
  105. else if( m_unit == INCHES )
  106. {
  107. size_mm = m_outputSize * 25.4;
  108. }
  109. else
  110. {
  111. // m_outputSize is the DPI, not an image size
  112. // the image size is m_originalSizePixels / m_outputSize (in inches)
  113. if( m_outputSize )
  114. size_mm = m_originalSizePixels / m_outputSize * 25.4;
  115. else
  116. size_mm = 0;
  117. }
  118. // Convert m_outputSize to new value:
  119. if( aUnit == MILLIMETRES )
  120. {
  121. m_outputSize = size_mm;
  122. }
  123. else if( aUnit == INCHES )
  124. {
  125. m_outputSize = size_mm / 25.4;
  126. }
  127. else
  128. {
  129. if( size_mm )
  130. m_outputSize = m_originalSizePixels / size_mm * 25.4;
  131. else
  132. m_outputSize = 0;
  133. }
  134. m_unit = aUnit;
  135. }
  136. BM2CMP_FRAME::BM2CMP_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
  137. BM2CMP_FRAME_BASE( aParent )
  138. {
  139. SetKiway( this, aKiway );
  140. m_config = GetNewConfig( Pgm().App().GetAppName() );
  141. wxString unitList[] =
  142. {
  143. _("mm"), _("Inch"), _("DPI")
  144. };
  145. for( int ii = 0; ii < 3; ii++ )
  146. m_PixelUnit->Append( unitList[ii] );
  147. LoadSettings( m_config.get() );
  148. m_outputSizeX.SetUnit( getUnitFromSelection() );
  149. m_outputSizeY.SetUnit( getUnitFromSelection() );
  150. m_outputSizeX.SetOutputSize( 0, getUnitFromSelection() );
  151. m_outputSizeY.SetOutputSize( 0, getUnitFromSelection() );
  152. m_UnitSizeX->ChangeValue( FormatOutputSize( m_outputSizeX.GetOutputSize() ) );
  153. m_UnitSizeY->ChangeValue( FormatOutputSize( m_outputSizeY.GetOutputSize() ) );
  154. //Set icon for aspect ratio
  155. m_AspectRatioLocked = true;
  156. m_AspectRatio = 1;
  157. m_AspectRatioLockButton->SetBitmap( KiBitmap( locked_xpm ) );
  158. // Give an icon
  159. wxIcon icon;
  160. icon.CopyFromBitmap( KiBitmap( icon_bitmap2component_xpm ) );
  161. SetIcon( icon );
  162. GetSizer()->SetSizeHints( this );
  163. SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
  164. m_buttonExportFile->Enable( false );
  165. m_buttonExportClipboard->Enable( false );
  166. if ( m_framePos == wxDefaultPosition )
  167. Centre();
  168. }
  169. BM2CMP_FRAME::~BM2CMP_FRAME()
  170. {
  171. SaveSettings( m_config.get() );
  172. /*
  173. * This needed for OSX: avoids further OnDraw processing after this
  174. * destructor and before the native window is destroyed
  175. */
  176. this->Freeze( );
  177. }
  178. void BM2CMP_FRAME::LoadSettings( wxConfigBase* aCfg )
  179. {
  180. int tmp;
  181. aCfg->Read( KEYWORD_FRAME_POSX, & m_framePos.x, -1 );
  182. aCfg->Read( KEYWORD_FRAME_POSY, & m_framePos.y, -1 );
  183. aCfg->Read( KEYWORD_FRAME_SIZEX, & m_frameSize.x, -1 );
  184. aCfg->Read( KEYWORD_FRAME_SIZEY, & m_frameSize.y, -1 );
  185. aCfg->Read( KEYWORD_LAST_INPUT_FILE, &m_BitmapFileName );
  186. aCfg->Read( KEYWORD_LAST_OUTPUT_FILE, &m_ConvertedFileName );
  187. int u_select = 0;
  188. aCfg->Read( KEYWORD_UNIT_SELECTION, &u_select, 0 );
  189. if( u_select < 0 || u_select > 2 ) // Validity control
  190. u_select = 0;
  191. m_PixelUnit->SetSelection( u_select );
  192. if( aCfg->Read( KEYWORD_BINARY_THRESHOLD, &tmp ) )
  193. m_sliderThreshold->SetValue( tmp );
  194. aCfg->Read( KEYWORD_BW_NEGATIVE, &tmp, 0 );
  195. m_Negative = tmp != 0;
  196. m_checkNegative->SetValue( m_Negative );
  197. m_exportToClipboard = false;
  198. m_AspectRatioLocked = false;
  199. if( aCfg->Read( KEYWORD_LAST_FORMAT, &tmp ) )
  200. {
  201. if( tmp < 0 || tmp > FINAL_FMT )
  202. tmp = PCBNEW_KICAD_MOD;
  203. m_rbOutputFormat->SetSelection( tmp );
  204. }
  205. if( tmp == PCBNEW_KICAD_MOD )
  206. m_rbPCBLayer->Enable( true );
  207. else
  208. m_rbPCBLayer->Enable( false );
  209. if( aCfg->Read( KEYWORD_LAST_MODLAYER, &tmp ) )
  210. {
  211. if( (unsigned) tmp > MOD_LYR_FINAL ) // Out of range
  212. m_rbPCBLayer->SetSelection( MOD_LYR_FSILKS );
  213. else
  214. m_rbPCBLayer->SetSelection( tmp );
  215. }
  216. }
  217. void BM2CMP_FRAME::SaveSettings( wxConfigBase* aCfg )
  218. {
  219. if( !aCfg )
  220. return;
  221. m_frameSize = GetSize();
  222. m_framePos = GetPosition();
  223. if( !IsIconized() )
  224. {
  225. aCfg->Write( KEYWORD_FRAME_POSX, (long) m_framePos.x );
  226. aCfg->Write( KEYWORD_FRAME_POSY, (long) m_framePos.y );
  227. aCfg->Write( KEYWORD_FRAME_SIZEX, (long) m_frameSize.x );
  228. aCfg->Write( KEYWORD_FRAME_SIZEY, (long) m_frameSize.y );
  229. }
  230. aCfg->Write( KEYWORD_LAST_INPUT_FILE, m_BitmapFileName );
  231. aCfg->Write( KEYWORD_LAST_OUTPUT_FILE, m_ConvertedFileName );
  232. aCfg->Write( KEYWORD_BINARY_THRESHOLD, m_sliderThreshold->GetValue() );
  233. aCfg->Write( KEYWORD_BW_NEGATIVE, m_checkNegative->IsChecked() ? 1 : 0 );
  234. aCfg->Write( KEYWORD_LAST_FORMAT, m_rbOutputFormat->GetSelection() );
  235. aCfg->Write( KEYWORD_LAST_MODLAYER, m_rbPCBLayer->GetSelection() );
  236. aCfg->Write( KEYWORD_UNIT_SELECTION, m_PixelUnit->GetSelection() );
  237. }
  238. void BM2CMP_FRAME::OnPaintInit( wxPaintEvent& event )
  239. {
  240. #ifdef __WXMAC__
  241. // Otherwise fails due: using wxPaintDC without being in a native paint event
  242. wxClientDC pict_dc( m_InitialPicturePanel );
  243. #else
  244. wxPaintDC pict_dc( m_InitialPicturePanel );
  245. #endif
  246. m_InitialPicturePanel->PrepareDC( pict_dc );
  247. // OSX crashes with empty bitmaps (on initial refreshes)
  248. if( m_Pict_Bitmap.IsOk() )
  249. pict_dc.DrawBitmap( m_Pict_Bitmap, 0, 0, !!m_Pict_Bitmap.GetMask() );
  250. event.Skip();
  251. }
  252. void BM2CMP_FRAME::OnPaintGreyscale( wxPaintEvent& event )
  253. {
  254. #ifdef __WXMAC__
  255. // Otherwise fails due: using wxPaintDC without being in a native paint event
  256. wxClientDC greyscale_dc( m_GreyscalePicturePanel );
  257. #else
  258. wxPaintDC greyscale_dc( m_GreyscalePicturePanel );
  259. #endif
  260. m_GreyscalePicturePanel->PrepareDC( greyscale_dc );
  261. // OSX crashes with empty bitmaps (on initial refreshes)
  262. if( m_Greyscale_Bitmap.IsOk() )
  263. greyscale_dc.DrawBitmap( m_Greyscale_Bitmap, 0, 0, !!m_Greyscale_Bitmap.GetMask() );
  264. event.Skip();
  265. }
  266. void BM2CMP_FRAME::OnPaintBW( wxPaintEvent& event )
  267. {
  268. #ifdef __WXMAC__
  269. // Otherwise fails due: using wxPaintDC without being in a native paint event
  270. wxClientDC nb_dc( m_BNPicturePanel );
  271. #else
  272. wxPaintDC nb_dc( m_BNPicturePanel );
  273. #endif
  274. m_BNPicturePanel->PrepareDC( nb_dc );
  275. if( m_BN_Bitmap.IsOk() )
  276. nb_dc.DrawBitmap( m_BN_Bitmap, 0, 0, !!m_BN_Bitmap.GetMask() );
  277. event.Skip();
  278. }
  279. void BM2CMP_FRAME::OnLoadFile( wxCommandEvent& event )
  280. {
  281. wxFileName fn( m_BitmapFileName );
  282. wxString path = fn.GetPath();
  283. if( path.IsEmpty() || !wxDirExists( path ) )
  284. path = m_mruPath;
  285. wxFileDialog fileDlg( this, _( "Choose Image" ), path, wxEmptyString,
  286. _( "Image Files " ) + wxImage::GetImageExtWildcard(),
  287. wxFD_OPEN | wxFD_FILE_MUST_EXIST );
  288. int diag = fileDlg.ShowModal();
  289. if( diag != wxID_OK )
  290. return;
  291. wxString fullFilename = fileDlg.GetPath();
  292. if( !OpenProjectFiles( std::vector<wxString>( 1, fullFilename ) ) )
  293. return;
  294. fn = fullFilename;
  295. m_mruPath = fn.GetPath();
  296. SetStatusText( fullFilename );
  297. Refresh();
  298. }
  299. bool BM2CMP_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
  300. {
  301. m_Pict_Image.Destroy();
  302. m_BitmapFileName = aFileSet[0];
  303. if( !m_Pict_Image.LoadFile( m_BitmapFileName ) )
  304. {
  305. // LoadFile has its own UI, no need for further failure notification here
  306. return false;
  307. }
  308. m_Pict_Bitmap = wxBitmap( m_Pict_Image );
  309. // Determine image resolution in DPI (does not existing in all formats).
  310. // the resolution can be given in bit per inches or bit per cm in file
  311. int imageDPIx = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
  312. int imageDPIy = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
  313. if( imageDPIx > 1 && imageDPIy > 1 )
  314. {
  315. if( m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
  316. {
  317. imageDPIx = KiROUND( imageDPIx * 2.54 );
  318. imageDPIy = KiROUND( imageDPIy * 2.54 );
  319. }
  320. }
  321. else // fallback to a default value (DEFAULT_DPI)
  322. {
  323. imageDPIx = imageDPIy = DEFAULT_DPI;
  324. }
  325. m_InputXValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIx ) );
  326. m_InputYValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIy ) );
  327. int h = m_Pict_Bitmap.GetHeight();
  328. int w = m_Pict_Bitmap.GetWidth();
  329. m_AspectRatio = (double) w / h;
  330. m_outputSizeX.SetOriginalDPI( imageDPIx );
  331. m_outputSizeX.SetOriginalSizePixels( w );
  332. m_outputSizeY.SetOriginalDPI( imageDPIy );
  333. m_outputSizeY.SetOriginalSizePixels( h );
  334. // Update display to keep aspect ratio
  335. auto fakeEvent = wxCommandEvent();
  336. OnSizeChangeX( fakeEvent );
  337. updateImageInfo();
  338. m_InitialPicturePanel->SetVirtualSize( w, h );
  339. m_GreyscalePicturePanel->SetVirtualSize( w, h );
  340. m_BNPicturePanel->SetVirtualSize( w, h );
  341. m_Greyscale_Image.Destroy();
  342. m_Greyscale_Image = m_Pict_Image.ConvertToGreyscale( );
  343. if( m_Pict_Bitmap.GetMask() )
  344. {
  345. for( int x = 0; x < m_Pict_Bitmap.GetWidth(); x++ )
  346. {
  347. for( int y = 0; y < m_Pict_Bitmap.GetHeight(); y++ )
  348. {
  349. if( m_Pict_Image.GetRed( x, y ) == m_Pict_Image.GetMaskRed() &&
  350. m_Pict_Image.GetGreen( x, y ) == m_Pict_Image.GetMaskGreen() &&
  351. m_Pict_Image.GetBlue( x, y ) == m_Pict_Image.GetMaskBlue() )
  352. {
  353. m_Greyscale_Image.SetRGB( x, y, 255, 255, 255 );
  354. }
  355. }
  356. }
  357. }
  358. if( m_Negative )
  359. NegateGreyscaleImage( );
  360. m_Greyscale_Bitmap = wxBitmap( m_Greyscale_Image );
  361. m_NB_Image = m_Greyscale_Image;
  362. Binarize( (double) m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
  363. m_buttonExportFile->Enable( true );
  364. m_buttonExportClipboard->Enable( true );
  365. m_outputSizeX.SetOutputSizeFromInitialImageSize();
  366. m_UnitSizeX->ChangeValue( FormatOutputSize( m_outputSizeX.GetOutputSize() ) );
  367. m_outputSizeY.SetOutputSizeFromInitialImageSize();
  368. m_UnitSizeY->ChangeValue( FormatOutputSize( m_outputSizeY.GetOutputSize() ) );
  369. return true;
  370. }
  371. // return a string giving the output size, according to the selected unit
  372. wxString BM2CMP_FRAME::FormatOutputSize( double aSize )
  373. {
  374. wxString text;
  375. if( getUnitFromSelection() == MILLIMETRES )
  376. {
  377. text.Printf( "%.1f", aSize );
  378. }
  379. else if( getUnitFromSelection() == INCHES )
  380. {
  381. text.Printf( "%.2f", aSize );
  382. }
  383. else
  384. {
  385. text.Printf( "%d", KiROUND( aSize ) );
  386. }
  387. return text;
  388. }
  389. void BM2CMP_FRAME::updateImageInfo()
  390. {
  391. // Note: the image resolution text controls are not modified
  392. // here, to avoid a race between text change when entered by user and
  393. // a text change if it is modified here.
  394. if( m_Pict_Bitmap.IsOk() )
  395. {
  396. int h = m_Pict_Bitmap.GetHeight();
  397. int w = m_Pict_Bitmap.GetWidth();
  398. int nb = m_Pict_Bitmap.GetDepth();
  399. m_SizeXValue->SetLabel( wxString::Format( wxT( "%d" ), w ) );
  400. m_SizeYValue->SetLabel( wxString::Format( wxT( "%d" ), h ) );
  401. m_BPPValue->SetLabel( wxString::Format( wxT( "%d" ), nb ) );
  402. }
  403. }
  404. EDA_UNITS_T BM2CMP_FRAME::getUnitFromSelection()
  405. {
  406. // return the EDA_UNITS_T from the m_PixelUnit choice
  407. switch( m_PixelUnit->GetSelection() )
  408. {
  409. case 1:
  410. return INCHES;
  411. case 2:
  412. return UNSCALED_UNITS;
  413. case 0:
  414. default:
  415. break;
  416. }
  417. return MILLIMETRES;
  418. }
  419. void BM2CMP_FRAME::OnSizeChangeX( wxCommandEvent& event )
  420. {
  421. double new_size;
  422. if( m_UnitSizeX->GetValue().ToDouble( &new_size ) )
  423. {
  424. if( m_AspectRatioLocked )
  425. {
  426. double calculatedY = new_size / m_AspectRatio;
  427. if( getUnitFromSelection() == UNSCALED_UNITS )
  428. {
  429. // for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
  430. // just rescale the other dpi
  431. double ratio = new_size / m_outputSizeX.GetOutputSize();
  432. calculatedY = m_outputSizeY.GetOutputSize() * ratio;
  433. }
  434. m_outputSizeY.SetOutputSize( calculatedY, getUnitFromSelection() );
  435. m_UnitSizeY->ChangeValue( FormatOutputSize( m_outputSizeY.GetOutputSize() ) );
  436. }
  437. m_outputSizeX.SetOutputSize( new_size, getUnitFromSelection() );
  438. }
  439. updateImageInfo();
  440. }
  441. void BM2CMP_FRAME::OnSizeChangeY( wxCommandEvent& event )
  442. {
  443. double new_size;
  444. if( m_UnitSizeY->GetValue().ToDouble( &new_size ) )
  445. {
  446. if( m_AspectRatioLocked )
  447. {
  448. double calculatedX = new_size * m_AspectRatio;
  449. if( getUnitFromSelection() == UNSCALED_UNITS )
  450. {
  451. // for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
  452. // just rescale the other dpi
  453. double ratio = new_size / m_outputSizeX.GetOutputSize();
  454. calculatedX = m_outputSizeX.GetOutputSize() * ratio;
  455. }
  456. m_outputSizeX.SetOutputSize( calculatedX, getUnitFromSelection() );
  457. m_UnitSizeX->ChangeValue( FormatOutputSize( m_outputSizeX.GetOutputSize() ) );
  458. }
  459. m_outputSizeY.SetOutputSize( new_size, getUnitFromSelection() );
  460. }
  461. updateImageInfo();
  462. }
  463. void BM2CMP_FRAME::OnSizeUnitChange( wxCommandEvent& event )
  464. {
  465. m_outputSizeX.SetUnit( getUnitFromSelection() );
  466. m_outputSizeY.SetUnit( getUnitFromSelection() );
  467. updateImageInfo();
  468. m_UnitSizeX->ChangeValue( FormatOutputSize( m_outputSizeX.GetOutputSize() ) );
  469. m_UnitSizeY->ChangeValue( FormatOutputSize( m_outputSizeY.GetOutputSize() ) );
  470. }
  471. void BM2CMP_FRAME::ToggleAspectRatioLock( wxCommandEvent& event )
  472. {
  473. m_AspectRatioLocked = !m_AspectRatioLocked;
  474. if( m_AspectRatioLocked )
  475. {
  476. m_AspectRatioLockButton->SetBitmap( KiBitmap( locked_xpm ) );
  477. //Force display update when aspect ratio is locked
  478. auto fakeEvent = wxCommandEvent();
  479. OnSizeChangeX( fakeEvent );
  480. }
  481. else
  482. {
  483. m_AspectRatioLockButton->SetBitmap( KiBitmap( unlocked_xpm ) );
  484. }
  485. }
  486. void BM2CMP_FRAME::Binarize( double aThreshold )
  487. {
  488. int h = m_Greyscale_Image.GetHeight();
  489. int w = m_Greyscale_Image.GetWidth();
  490. unsigned char threshold = aThreshold * 255;
  491. unsigned char alpha_thresh = 0.7 * threshold;
  492. for( int y = 0; y < h; y++ )
  493. for( int x = 0; x < w; x++ )
  494. {
  495. unsigned char pixout;
  496. auto pixin = m_Greyscale_Image.GetGreen( x, y );
  497. auto alpha = m_Greyscale_Image.HasAlpha() ?
  498. m_Greyscale_Image.GetAlpha( x, y ) : wxALPHA_OPAQUE;
  499. if( pixin < threshold && alpha > alpha_thresh )
  500. pixout = 0;
  501. else
  502. pixout = 255;
  503. m_NB_Image.SetRGB( x, y, pixout, pixout, pixout );
  504. }
  505. m_BN_Bitmap = wxBitmap( m_NB_Image );
  506. }
  507. void BM2CMP_FRAME::NegateGreyscaleImage( )
  508. {
  509. unsigned char pix;
  510. int h = m_Greyscale_Image.GetHeight();
  511. int w = m_Greyscale_Image.GetWidth();
  512. for( int y = 0; y < h; y++ )
  513. for( int x = 0; x < w; x++ )
  514. {
  515. pix = m_Greyscale_Image.GetGreen( x, y );
  516. pix = ~pix;
  517. m_Greyscale_Image.SetRGB( x, y, pix, pix, pix );
  518. }
  519. }
  520. void BM2CMP_FRAME::OnNegativeClicked( wxCommandEvent& )
  521. {
  522. if( m_checkNegative->GetValue() != m_Negative )
  523. {
  524. NegateGreyscaleImage();
  525. m_Greyscale_Bitmap = wxBitmap( m_Greyscale_Image );
  526. Binarize( (double)m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
  527. m_Negative = m_checkNegative->GetValue();
  528. Refresh();
  529. }
  530. }
  531. void BM2CMP_FRAME::OnThresholdChange( wxScrollEvent& event )
  532. {
  533. Binarize( (double)m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
  534. Refresh();
  535. }
  536. void BM2CMP_FRAME::OnExportToFile( wxCommandEvent& event )
  537. {
  538. m_exportToClipboard = false;
  539. // choices of m_rbOutputFormat are expected to be in same order as
  540. // OUTPUT_FMT_ID. See bitmap2component.h
  541. OUTPUT_FMT_ID format = (OUTPUT_FMT_ID) m_rbOutputFormat->GetSelection();
  542. exportBitmap( format );
  543. }
  544. void BM2CMP_FRAME::OnExportToClipboard( wxCommandEvent& event )
  545. {
  546. m_exportToClipboard = true;
  547. // choices of m_rbOutputFormat are expected to be in same order as
  548. // OUTPUT_FMT_ID. See bitmap2component.h
  549. OUTPUT_FMT_ID format = (OUTPUT_FMT_ID) m_rbOutputFormat->GetSelection();
  550. std::string buffer;
  551. ExportToBuffer( buffer, format );
  552. // Write buffer to the clipboard
  553. if (wxTheClipboard->Open())
  554. {
  555. // This data objects are held by the clipboard,
  556. // so do not delete them in the app.
  557. wxTheClipboard->SetData( new wxTextDataObject( buffer.c_str() ) );
  558. wxTheClipboard->Close();
  559. }
  560. else
  561. wxMessageBox( _( "Unable to export to the Clipboard") );
  562. }
  563. void BM2CMP_FRAME::exportBitmap( OUTPUT_FMT_ID aFormat )
  564. {
  565. switch( aFormat )
  566. {
  567. case EESCHEMA_FMT:
  568. exportEeschemaFormat();
  569. break;
  570. case PCBNEW_KICAD_MOD:
  571. exportPcbnewFormat();
  572. break;
  573. case POSTSCRIPT_FMT:
  574. exportPostScriptFormat();
  575. break;
  576. case KICAD_LOGO:
  577. OnExportLogo();
  578. break;
  579. }
  580. }
  581. void BM2CMP_FRAME::OnExportLogo()
  582. {
  583. wxFileName fn( m_ConvertedFileName );
  584. wxString path = fn.GetPath();
  585. if( path.IsEmpty() || !wxDirExists(path) )
  586. path = ::wxGetCwd();
  587. wxFileDialog fileDlg( this, _( "Create Logo File" ), path, wxEmptyString,
  588. PageLayoutDescrFileWildcard(),
  589. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  590. int diag = fileDlg.ShowModal();
  591. if( diag != wxID_OK )
  592. return;
  593. fn = fileDlg.GetPath();
  594. fn.SetExt( PageLayoutDescrFileExtension );
  595. m_ConvertedFileName = fn.GetFullPath();
  596. FILE* outfile;
  597. outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );
  598. if( outfile == NULL )
  599. {
  600. wxString msg;
  601. msg.Printf( _( "File \"%s\" could not be created." ), m_ConvertedFileName );
  602. wxMessageBox( msg );
  603. return;
  604. }
  605. std::string buffer;
  606. ExportToBuffer( buffer, KICAD_LOGO );
  607. fputs( buffer.c_str(), outfile );
  608. fclose( outfile );
  609. }
  610. void BM2CMP_FRAME::exportPostScriptFormat()
  611. {
  612. wxFileName fn( m_ConvertedFileName );
  613. wxString path = fn.GetPath();
  614. if( path.IsEmpty() || !wxDirExists( path ) )
  615. path = ::wxGetCwd();
  616. wxFileDialog fileDlg( this, _( "Create Postscript File" ),
  617. path, wxEmptyString,
  618. PSFileWildcard(),
  619. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  620. int diag = fileDlg.ShowModal();
  621. if( diag != wxID_OK )
  622. return;
  623. fn = fileDlg.GetPath();
  624. fn.SetExt( wxT( "ps" ) );
  625. m_ConvertedFileName = fn.GetFullPath();
  626. FILE* outfile;
  627. outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );
  628. if( outfile == NULL )
  629. {
  630. wxString msg;
  631. msg.Printf( _( "File \"%s\" could not be created." ), m_ConvertedFileName );
  632. wxMessageBox( msg );
  633. return;
  634. }
  635. std::string buffer;
  636. ExportToBuffer( buffer, POSTSCRIPT_FMT );
  637. fputs( buffer.c_str(), outfile );
  638. fclose( outfile );
  639. }
  640. void BM2CMP_FRAME::exportEeschemaFormat()
  641. {
  642. wxFileName fn( m_ConvertedFileName );
  643. wxString path = fn.GetPath();
  644. if( path.IsEmpty() || !wxDirExists(path) )
  645. path = ::wxGetCwd();
  646. wxFileDialog fileDlg( this, _( "Create Symbol Library" ),
  647. path, wxEmptyString,
  648. SchematicLibraryFileWildcard(),
  649. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  650. int diag = fileDlg.ShowModal();
  651. if( diag != wxID_OK )
  652. return;
  653. fn = fileDlg.GetPath();
  654. fn.SetExt( SchematicLibraryFileExtension );
  655. m_ConvertedFileName = fn.GetFullPath();
  656. FILE* outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );
  657. if( outfile == NULL )
  658. {
  659. wxString msg;
  660. msg.Printf( _( "File \"%s\" could not be created." ), m_ConvertedFileName );
  661. wxMessageBox( msg );
  662. return;
  663. }
  664. std::string buffer;
  665. ExportToBuffer( buffer, EESCHEMA_FMT );
  666. fputs( buffer.c_str(), outfile );
  667. fclose( outfile );
  668. }
  669. void BM2CMP_FRAME::exportPcbnewFormat()
  670. {
  671. wxFileName fn( m_ConvertedFileName );
  672. wxString path = fn.GetPath();
  673. if( path.IsEmpty() || !wxDirExists( path ) )
  674. path = m_mruPath;
  675. wxFileDialog fileDlg( this, _( "Create Footprint Library" ),
  676. path, wxEmptyString,
  677. KiCadFootprintLibFileWildcard(),
  678. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  679. int diag = fileDlg.ShowModal();
  680. if( diag != wxID_OK )
  681. return;
  682. fn = fileDlg.GetPath();
  683. fn.SetExt( KiCadFootprintFileExtension );
  684. m_ConvertedFileName = fn.GetFullPath();
  685. FILE* outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );
  686. if( outfile == NULL )
  687. {
  688. wxString msg;
  689. msg.Printf( _( "File \"%s\" could not be created." ), m_ConvertedFileName );
  690. wxMessageBox( msg );
  691. return;
  692. }
  693. std::string buffer;
  694. ExportToBuffer( buffer, PCBNEW_KICAD_MOD );
  695. fputs( buffer.c_str(), outfile );
  696. fclose( outfile );
  697. m_mruPath = fn.GetPath();
  698. }
  699. void BM2CMP_FRAME::ExportToBuffer( std::string& aOutput, OUTPUT_FMT_ID aFormat )
  700. {
  701. // Create a potrace bitmap
  702. int h = m_NB_Image.GetHeight();
  703. int w = m_NB_Image.GetWidth();
  704. potrace_bitmap_t* potrace_bitmap = bm_new( w, h );
  705. if( !potrace_bitmap )
  706. {
  707. wxString msg;
  708. msg.Printf( _( "Error allocating memory for potrace bitmap" ) );
  709. wxMessageBox( msg );
  710. return;
  711. }
  712. /* fill the bitmap with data */
  713. for( int y = 0; y < h; y++ )
  714. {
  715. for( int x = 0; x < w; x++ )
  716. {
  717. auto pix = m_NB_Image.GetGreen( x, y );
  718. BM_PUT( potrace_bitmap, x, y, pix ? 0 : 1 );
  719. }
  720. }
  721. // choices of m_rbPCBLayer are expected to be in same order as
  722. // BMP2CMP_MOD_LAYER. See bitmap2component.h
  723. BMP2CMP_MOD_LAYER modLayer = MOD_LYR_FSILKS;
  724. if( aFormat == PCBNEW_KICAD_MOD )
  725. modLayer = (BMP2CMP_MOD_LAYER) m_rbPCBLayer->GetSelection();
  726. BITMAPCONV_INFO converter( aOutput );
  727. converter.ConvertBitmap( potrace_bitmap, aFormat, m_outputSizeX.GetOutputDPI(),
  728. m_outputSizeY.GetOutputDPI(), modLayer );
  729. if( !converter.GetErrorMessages().empty() )
  730. wxMessageBox( converter.GetErrorMessages().c_str(), _( "Errors" ) );
  731. }
  732. void BM2CMP_FRAME::OnFormatChange( wxCommandEvent& event )
  733. {
  734. if( m_rbOutputFormat->GetSelection() == PCBNEW_KICAD_MOD )
  735. m_rbPCBLayer->Enable( true );
  736. else
  737. m_rbPCBLayer->Enable( false );
  738. }