2019-06-19 07:18:30 -07:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992-2010 jean-pierre.charras
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2019-06-19 07:18:30 -07:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
#include <bitmap2cmp_frame.h>
|
|
|
|
#include <bitmap2component.h>
|
|
|
|
#include <bitmap2cmp_panel.h>
|
2020-01-12 20:44:19 -05:00
|
|
|
#include <bitmap2cmp_settings.h>
|
2019-06-19 07:18:30 -07:00
|
|
|
#include <bitmap_io.h>
|
2023-03-12 14:49:57 +00:00
|
|
|
#include <common.h>
|
2020-01-07 17:12:59 +00:00
|
|
|
#include <math/util.h> // for KiROUND
|
2019-06-19 07:18:30 -07:00
|
|
|
#include <potracelib.h>
|
|
|
|
#include <wx/clipbrd.h>
|
|
|
|
#include <wx/rawbmp.h>
|
2021-05-01 09:50:29 +02:00
|
|
|
#include <wx/msgdlg.h>
|
|
|
|
#include <wx/dcclient.h>
|
2021-06-03 08:11:15 -04:00
|
|
|
#include <wx/log.h>
|
2021-05-01 09:50:29 +02:00
|
|
|
|
2019-06-19 07:18:30 -07:00
|
|
|
#define DEFAULT_DPI 300 // the image DPI used in formats that do not define a DPI
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
BITMAP2CMP_PANEL::BITMAP2CMP_PANEL( BITMAP2CMP_FRAME* aParent ) :
|
|
|
|
BITMAP2CMP_PANEL_BASE( aParent ),
|
2025-01-18 12:01:28 +00:00
|
|
|
m_parentFrame( aParent ),
|
|
|
|
m_negative( false ),
|
2023-06-16 15:29:31 +01:00
|
|
|
m_aspectRatio( 1.0 )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
for( const wxString& unit : { _( "mm" ), _( "Inch" ), _( "DPI" ) } )
|
2020-03-24 01:01:23 +00:00
|
|
|
m_PixelUnit->Append( unit );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_outputSizeX.SetUnit( getUnitFromSelection() );
|
|
|
|
m_outputSizeY.SetUnit( getUnitFromSelection() );
|
|
|
|
m_outputSizeX.SetOutputSize( 0, getUnitFromSelection() );
|
|
|
|
m_outputSizeY.SetOutputSize( 0, getUnitFromSelection() );
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeX->ChangeValue( formatOutputSize( m_outputSizeX.GetOutputSize() ) );
|
|
|
|
m_UnitSizeY->ChangeValue( formatOutputSize( m_outputSizeY.GetOutputSize() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_buttonExportFile->Enable( false );
|
|
|
|
m_buttonExportClipboard->Enable( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
wxWindow* BITMAP2CMP_PANEL::GetCurrentPage()
|
2020-03-24 01:01:23 +00:00
|
|
|
{
|
|
|
|
return m_Notebook->GetCurrentPage();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::LoadSettings( BITMAP2CMP_SETTINGS* cfg )
|
2022-11-19 19:39:32 +00:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
if( cfg->m_Units >= 0 && cfg->m_Units < (int) m_PixelUnit->GetCount() )
|
|
|
|
m_PixelUnit->SetSelection( cfg->m_Units );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2020-01-12 20:44:19 -05:00
|
|
|
m_sliderThreshold->SetValue( cfg->m_Threshold );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2023-06-16 15:29:31 +01:00
|
|
|
m_negative = cfg->m_Negative;
|
2020-01-12 20:44:19 -05:00
|
|
|
m_checkNegative->SetValue( cfg->m_Negative );
|
2022-11-19 19:39:32 +00:00
|
|
|
|
2023-06-16 15:29:31 +01:00
|
|
|
m_aspectRatio = 1.0;
|
2022-11-19 19:39:32 +00:00
|
|
|
m_aspectRatioCheckbox->SetValue( true );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
switch( cfg->m_LastFormat )
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case FOOTPRINT_FMT: m_rbFootprint->SetValue( true ); break;
|
2025-01-19 17:45:40 -08:00
|
|
|
case SYMBOL_FMT:
|
|
|
|
case SYMBOL_PASTE_FMT: m_rbSymbol->SetValue( true ); break;
|
2025-01-18 12:01:28 +00:00
|
|
|
case POSTSCRIPT_FMT: m_rbPostscript->SetValue( true ); break;
|
|
|
|
case DRAWING_SHEET_FMT: m_rbWorksheet->SetValue( true ); break;
|
|
|
|
}
|
2020-01-12 20:44:19 -05:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
m_layerLabel->Enable( cfg->m_LastFormat == FOOTPRINT_FMT );
|
|
|
|
m_layerCtrl->Enable( cfg->m_LastFormat == FOOTPRINT_FMT );
|
2023-05-13 11:45:25 +02:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
if( cfg->m_LastLayer >= 0 && cfg->m_LastLayer < (int) m_layerCtrl->GetCount() )
|
|
|
|
m_layerCtrl->SetSelection( cfg->m_LastLayer );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::SaveSettings( BITMAP2CMP_SETTINGS* cfg )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
cfg->m_Threshold = m_sliderThreshold->GetValue();
|
|
|
|
cfg->m_Negative = m_checkNegative->IsChecked();
|
|
|
|
cfg->m_LastFormat = getOutputFormat();
|
|
|
|
cfg->m_LastLayer = m_layerCtrl->GetSelection();
|
|
|
|
cfg->m_Units = m_PixelUnit->GetSelection();
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnPaintInit( wxPaintEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
#ifdef __WXMAC__
|
|
|
|
// Otherwise fails due: using wxPaintDC without being in a native paint event
|
|
|
|
wxClientDC pict_dc( m_InitialPicturePanel );
|
|
|
|
#else
|
|
|
|
wxPaintDC pict_dc( m_InitialPicturePanel );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_InitialPicturePanel->PrepareDC( pict_dc );
|
|
|
|
|
|
|
|
// OSX crashes with empty bitmaps (on initial refreshes)
|
|
|
|
if( m_Pict_Bitmap.IsOk() )
|
|
|
|
pict_dc.DrawBitmap( m_Pict_Bitmap, 0, 0, !!m_Pict_Bitmap.GetMask() );
|
|
|
|
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnPaintGreyscale( wxPaintEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
#ifdef __WXMAC__
|
|
|
|
// Otherwise fails due: using wxPaintDC without being in a native paint event
|
|
|
|
wxClientDC greyscale_dc( m_GreyscalePicturePanel );
|
|
|
|
#else
|
|
|
|
wxPaintDC greyscale_dc( m_GreyscalePicturePanel );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_GreyscalePicturePanel->PrepareDC( greyscale_dc );
|
|
|
|
|
|
|
|
// OSX crashes with empty bitmaps (on initial refreshes)
|
|
|
|
if( m_Greyscale_Bitmap.IsOk() )
|
|
|
|
greyscale_dc.DrawBitmap( m_Greyscale_Bitmap, 0, 0, !!m_Greyscale_Bitmap.GetMask() );
|
|
|
|
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnPaintBW( wxPaintEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
#ifdef __WXMAC__
|
|
|
|
// Otherwise fails due: using wxPaintDC without being in a native paint event
|
|
|
|
wxClientDC nb_dc( m_BNPicturePanel );
|
|
|
|
#else
|
|
|
|
wxPaintDC nb_dc( m_BNPicturePanel );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_BNPicturePanel->PrepareDC( nb_dc );
|
|
|
|
|
|
|
|
if( m_BN_Bitmap.IsOk() )
|
|
|
|
nb_dc.DrawBitmap( m_BN_Bitmap, 0, 0, !!m_BN_Bitmap.GetMask() );
|
|
|
|
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnLoadFile( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2023-06-16 15:29:31 +01:00
|
|
|
m_parentFrame->OnLoadFile();
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
bool BITMAP2CMP_PANEL::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
m_Pict_Image.Destroy();
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
if( !m_Pict_Image.LoadFile( aFileSet[0] ) )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
// LoadFile has its own UI, no need for further failure notification here
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_Pict_Bitmap = wxBitmap( m_Pict_Image );
|
|
|
|
|
|
|
|
// Determine image resolution in DPI (does not existing in all formats).
|
|
|
|
// the resolution can be given in bit per inches or bit per cm in file
|
|
|
|
|
|
|
|
int imageDPIx = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
|
|
|
|
int imageDPIy = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
|
|
|
|
|
|
|
|
if( imageDPIx > 1 && imageDPIy > 1 )
|
|
|
|
{
|
|
|
|
if( m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
|
|
|
|
{
|
|
|
|
imageDPIx = KiROUND( imageDPIx * 2.54 );
|
|
|
|
imageDPIy = KiROUND( imageDPIy * 2.54 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // fallback to a default value (DEFAULT_DPI)
|
|
|
|
{
|
|
|
|
imageDPIx = imageDPIy = DEFAULT_DPI;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_InputXValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIx ) );
|
|
|
|
m_InputYValueDPI->SetLabel( wxString::Format( wxT( "%d" ), imageDPIy ) );
|
|
|
|
|
|
|
|
int h = m_Pict_Bitmap.GetHeight();
|
|
|
|
int w = m_Pict_Bitmap.GetWidth();
|
2023-06-16 15:29:31 +01:00
|
|
|
m_aspectRatio = (double) w / h;
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_outputSizeX.SetOriginalDPI( imageDPIx );
|
|
|
|
m_outputSizeX.SetOriginalSizePixels( w );
|
|
|
|
m_outputSizeY.SetOriginalDPI( imageDPIy );
|
|
|
|
m_outputSizeY.SetOriginalSizePixels( h );
|
|
|
|
|
|
|
|
// Update display to keep aspect ratio
|
2022-10-16 17:45:10 +01:00
|
|
|
wxCommandEvent dummy;
|
|
|
|
OnSizeChangeX( dummy );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
updateImageInfo();
|
|
|
|
|
|
|
|
m_InitialPicturePanel->SetVirtualSize( w, h );
|
|
|
|
m_GreyscalePicturePanel->SetVirtualSize( w, h );
|
|
|
|
m_BNPicturePanel->SetVirtualSize( w, h );
|
|
|
|
|
|
|
|
m_Greyscale_Image.Destroy();
|
|
|
|
m_Greyscale_Image = m_Pict_Image.ConvertToGreyscale( );
|
|
|
|
|
|
|
|
if( m_Pict_Bitmap.GetMask() )
|
|
|
|
{
|
|
|
|
for( int x = 0; x < m_Pict_Bitmap.GetWidth(); x++ )
|
|
|
|
{
|
|
|
|
for( int y = 0; y < m_Pict_Bitmap.GetHeight(); y++ )
|
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
if( m_Pict_Image.GetRed( x, y ) == m_Pict_Image.GetMaskRed()
|
|
|
|
&& m_Pict_Image.GetGreen( x, y ) == m_Pict_Image.GetMaskGreen()
|
|
|
|
&& m_Pict_Image.GetBlue( x, y ) == m_Pict_Image.GetMaskBlue() )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
m_Greyscale_Image.SetRGB( x, y, 255, 255, 255 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-16 15:29:31 +01:00
|
|
|
if( m_negative )
|
2025-01-18 12:01:28 +00:00
|
|
|
negateGreyscaleImage();
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_Greyscale_Bitmap = wxBitmap( m_Greyscale_Image );
|
|
|
|
m_NB_Image = m_Greyscale_Image;
|
2025-01-18 12:01:28 +00:00
|
|
|
binarize( (double)m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_buttonExportFile->Enable( true );
|
|
|
|
m_buttonExportClipboard->Enable( true );
|
|
|
|
|
|
|
|
m_outputSizeX.SetOutputSizeFromInitialImageSize();
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeX->ChangeValue( formatOutputSize( m_outputSizeX.GetOutputSize() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
m_outputSizeY.SetOutputSizeFromInitialImageSize();
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeY->ChangeValue( formatOutputSize( m_outputSizeY.GetOutputSize() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// return a string giving the output size, according to the selected unit
|
2025-01-18 12:01:28 +00:00
|
|
|
wxString BITMAP2CMP_PANEL::formatOutputSize( double aSize )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
wxString text;
|
|
|
|
|
2025-03-01 20:24:37 +00:00
|
|
|
if( getUnitFromSelection() == EDA_UNITS::MM )
|
2023-01-16 19:07:41 -05:00
|
|
|
text.Printf( wxS( "%.1f" ), aSize );
|
2025-03-02 11:34:30 +01:00
|
|
|
else if( getUnitFromSelection() == EDA_UNITS::INCH )
|
2023-01-16 19:07:41 -05:00
|
|
|
text.Printf( wxS( "%.2f" ), aSize );
|
2019-06-19 07:18:30 -07:00
|
|
|
else
|
2022-02-09 09:01:30 -05:00
|
|
|
text.Printf( wxT( "%d" ), KiROUND( aSize ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::updateImageInfo()
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2022-11-19 19:39:32 +00:00
|
|
|
// Note: the image resolution text controls are not modified here, to avoid a race between
|
|
|
|
// text change when entered by user and a text change if it is modified here.
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
if( m_Pict_Bitmap.IsOk() )
|
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
m_SizeXValue->SetLabel( wxString::Format( wxT( "%d" ), m_Pict_Bitmap.GetWidth() ) );
|
|
|
|
m_SizeYValue->SetLabel( wxString::Format( wxT( "%d" ), m_Pict_Bitmap.GetHeight() ) );
|
|
|
|
m_BPPValue->SetLabel( wxString::Format( wxT( "%d" ), m_Pict_Bitmap.GetDepth() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
EDA_UNITS BITMAP2CMP_PANEL::getUnitFromSelection()
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2019-12-20 14:11:39 +00:00
|
|
|
// return the EDA_UNITS from the m_PixelUnit choice
|
2019-06-19 07:18:30 -07:00
|
|
|
switch( m_PixelUnit->GetSelection() )
|
|
|
|
{
|
2025-03-02 11:34:30 +01:00
|
|
|
case 1: return EDA_UNITS::INCH;
|
2022-11-19 19:39:32 +00:00
|
|
|
case 2: return EDA_UNITS::UNSCALED;
|
2019-06-19 07:18:30 -07:00
|
|
|
case 0:
|
2025-03-01 20:24:37 +00:00
|
|
|
default: return EDA_UNITS::MM;
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnSizeChangeX( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
double new_size;
|
|
|
|
|
|
|
|
if( m_UnitSizeX->GetValue().ToDouble( &new_size ) )
|
|
|
|
{
|
2022-11-19 19:39:32 +00:00
|
|
|
if( m_aspectRatioCheckbox->GetValue() )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2023-06-16 15:29:31 +01:00
|
|
|
double calculatedY = new_size / m_aspectRatio;
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2019-12-20 14:11:39 +00:00
|
|
|
if( getUnitFromSelection() == EDA_UNITS::UNSCALED )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
// for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
|
2021-07-16 16:13:26 -04:00
|
|
|
// just re-scale the other dpi
|
2019-06-19 07:18:30 -07:00
|
|
|
double ratio = new_size / m_outputSizeX.GetOutputSize();
|
|
|
|
calculatedY = m_outputSizeY.GetOutputSize() * ratio;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_outputSizeY.SetOutputSize( calculatedY, getUnitFromSelection() );
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeY->ChangeValue( formatOutputSize( m_outputSizeY.GetOutputSize() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
m_outputSizeX.SetOutputSize( new_size, getUnitFromSelection() );
|
|
|
|
}
|
|
|
|
|
|
|
|
updateImageInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnSizeChangeY( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
double new_size;
|
|
|
|
|
|
|
|
if( m_UnitSizeY->GetValue().ToDouble( &new_size ) )
|
|
|
|
{
|
2022-11-19 19:39:32 +00:00
|
|
|
if( m_aspectRatioCheckbox->GetValue() )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2023-06-16 15:29:31 +01:00
|
|
|
double calculatedX = new_size * m_aspectRatio;
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2019-12-20 14:11:39 +00:00
|
|
|
if( getUnitFromSelection() == EDA_UNITS::UNSCALED )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
// for units in DPI, keeping aspect ratio cannot use m_AspectRatioLocked.
|
2021-07-16 16:13:26 -04:00
|
|
|
// just re-scale the other dpi
|
2019-06-19 07:18:30 -07:00
|
|
|
double ratio = new_size / m_outputSizeX.GetOutputSize();
|
|
|
|
calculatedX = m_outputSizeX.GetOutputSize() * ratio;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_outputSizeX.SetOutputSize( calculatedX, getUnitFromSelection() );
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeX->ChangeValue( formatOutputSize( m_outputSizeX.GetOutputSize() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
m_outputSizeY.SetOutputSize( new_size, getUnitFromSelection() );
|
|
|
|
}
|
|
|
|
|
|
|
|
updateImageInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnSizeUnitChange( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
m_outputSizeX.SetUnit( getUnitFromSelection() );
|
|
|
|
m_outputSizeY.SetUnit( getUnitFromSelection() );
|
|
|
|
updateImageInfo();
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeX->ChangeValue( formatOutputSize( m_outputSizeX.GetOutputSize() ) );
|
|
|
|
m_UnitSizeY->ChangeValue( formatOutputSize( m_outputSizeY.GetOutputSize() ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-16 15:29:31 +01:00
|
|
|
void BITMAP2CMP_PANEL::SetOutputSize( const IMAGE_SIZE& aSizeX, const IMAGE_SIZE& aSizeY )
|
|
|
|
{
|
|
|
|
m_outputSizeX = aSizeX;
|
|
|
|
m_outputSizeY = aSizeY;
|
|
|
|
updateImageInfo();
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
m_UnitSizeX->ChangeValue( formatOutputSize( m_outputSizeX.GetOutputSize() ) );
|
|
|
|
m_UnitSizeY->ChangeValue( formatOutputSize( m_outputSizeY.GetOutputSize() ) );
|
2023-06-16 15:29:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::ToggleAspectRatioLock( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2022-11-19 19:39:32 +00:00
|
|
|
if( m_aspectRatioCheckbox->GetValue() )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2022-11-19 19:39:32 +00:00
|
|
|
// Force display update when aspect ratio is locked
|
2022-10-16 17:45:10 +01:00
|
|
|
wxCommandEvent dummy;
|
|
|
|
OnSizeChangeX( dummy );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
void BITMAP2CMP_PANEL::binarize( double aThreshold )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
unsigned char threshold = aThreshold * 255;
|
|
|
|
unsigned char alpha_thresh = 0.7 * threshold;
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
for( int y = 0; y < m_Greyscale_Image.GetHeight(); y++ )
|
2022-10-16 17:45:10 +01:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
for( int x = 0; x < m_Greyscale_Image.GetWidth(); x++ )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
unsigned char pixel = m_Greyscale_Image.GetGreen( x, y );
|
2022-10-16 17:45:10 +01:00
|
|
|
unsigned char alpha = m_Greyscale_Image.HasAlpha() ? m_Greyscale_Image.GetAlpha( x, y )
|
|
|
|
: wxALPHA_OPAQUE;
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
if( pixel < threshold && alpha > alpha_thresh )
|
|
|
|
pixel = 0;
|
2019-06-19 07:18:30 -07:00
|
|
|
else
|
2025-01-18 12:01:28 +00:00
|
|
|
pixel = 255;
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
m_NB_Image.SetRGB( x, y, pixel, pixel, pixel );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
2022-10-16 17:45:10 +01:00
|
|
|
}
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_BN_Bitmap = wxBitmap( m_NB_Image );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
void BITMAP2CMP_PANEL::negateGreyscaleImage( )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
for( int y = 0; y < m_Greyscale_Image.GetHeight(); y++ )
|
2022-10-16 17:45:10 +01:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
for( int x = 0; x < m_Greyscale_Image.GetWidth(); x++ )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
unsigned char pixel = m_Greyscale_Image.GetGreen( x, y );
|
|
|
|
pixel = ~pixel;
|
|
|
|
m_Greyscale_Image.SetRGB( x, y, pixel, pixel, pixel );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
2022-10-16 17:45:10 +01:00
|
|
|
}
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnNegativeClicked( wxCommandEvent& )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2023-06-16 15:29:31 +01:00
|
|
|
if( m_checkNegative->GetValue() != m_negative )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
negateGreyscaleImage();
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
m_Greyscale_Bitmap = wxBitmap( m_Greyscale_Image );
|
2025-01-18 12:01:28 +00:00
|
|
|
binarize( (double)m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
|
2023-06-16 15:29:31 +01:00
|
|
|
m_negative = m_checkNegative->GetValue();
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnThresholdChange( wxScrollEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
binarize( (double)m_sliderThreshold->GetValue() / m_sliderThreshold->GetMax() );
|
2019-06-19 07:18:30 -07:00
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnExportToFile( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
switch( getOutputFormat() )
|
|
|
|
{
|
2025-01-19 17:45:40 -08:00
|
|
|
case SYMBOL_FMT:
|
|
|
|
case SYMBOL_PASTE_FMT: m_parentFrame->ExportEeschemaFormat(); break;
|
2025-01-18 12:01:28 +00:00
|
|
|
case FOOTPRINT_FMT: m_parentFrame->ExportPcbnewFormat(); break;
|
|
|
|
case POSTSCRIPT_FMT: m_parentFrame->ExportPostScriptFormat(); break;
|
|
|
|
case DRAWING_SHEET_FMT: m_parentFrame->ExportDrawingSheetFormat(); break;
|
|
|
|
}
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
OUTPUT_FMT_ID BITMAP2CMP_PANEL::getOutputFormat()
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
if( m_rbSymbol->GetValue() )
|
|
|
|
return SYMBOL_FMT;
|
|
|
|
else if( m_rbPostscript->GetValue() )
|
|
|
|
return POSTSCRIPT_FMT;
|
|
|
|
else if( m_rbWorksheet->GetValue() )
|
|
|
|
return DRAWING_SHEET_FMT;
|
|
|
|
else
|
|
|
|
return FOOTPRINT_FMT;
|
|
|
|
}
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
|
|
|
|
void BITMAP2CMP_PANEL::OnExportToClipboard( wxCommandEvent& event )
|
|
|
|
{
|
2019-06-19 07:18:30 -07:00
|
|
|
std::string buffer;
|
2025-01-19 17:45:40 -08:00
|
|
|
OUTPUT_FMT_ID format = getOutputFormat() == SYMBOL_FMT ? SYMBOL_PASTE_FMT : getOutputFormat();
|
|
|
|
ExportToBuffer( buffer, format );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2021-01-29 19:13:12 +00:00
|
|
|
wxLogNull doNotLog; // disable logging of failed clipboard actions
|
|
|
|
|
2019-06-19 07:18:30 -07:00
|
|
|
// Write buffer to the clipboard
|
2021-07-16 16:13:26 -04:00
|
|
|
if( wxTheClipboard->Open() )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
// This data objects are held by the clipboard,
|
|
|
|
// so do not delete them in the app.
|
|
|
|
wxTheClipboard->SetData( new wxTextDataObject( buffer.c_str() ) );
|
2021-05-01 23:00:08 +01:00
|
|
|
wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
|
2019-06-19 07:18:30 -07:00
|
|
|
wxTheClipboard->Close();
|
|
|
|
}
|
|
|
|
else
|
2021-07-16 16:13:26 -04:00
|
|
|
{
|
2019-06-19 07:18:30 -07:00
|
|
|
wxMessageBox( _( "Unable to export to the Clipboard") );
|
2021-07-16 16:13:26 -04:00
|
|
|
}
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::ExportToBuffer( std::string& aOutput, OUTPUT_FMT_ID aFormat )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
|
|
|
// Create a potrace bitmap
|
2025-01-18 12:01:28 +00:00
|
|
|
potrace_bitmap_t* potrace_bitmap = bm_new( m_NB_Image.GetWidth(), m_NB_Image.GetHeight() );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
if( !potrace_bitmap )
|
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
wxMessageBox( _( "Error allocating memory for potrace bitmap" ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill the bitmap with data */
|
2025-01-18 12:01:28 +00:00
|
|
|
for( int y = 0; y < m_NB_Image.GetHeight(); y++ )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
for( int x = 0; x < m_NB_Image.GetWidth(); x++ )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
unsigned char pixel = m_NB_Image.GetGreen( x, y );
|
|
|
|
BM_PUT( potrace_bitmap, x, y, pixel ? 0 : 1 );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
wxString layer = wxT( "F.SilkS" );
|
|
|
|
|
|
|
|
if( aFormat == FOOTPRINT_FMT )
|
|
|
|
{
|
|
|
|
switch( m_layerCtrl->GetSelection() )
|
|
|
|
{
|
|
|
|
case 0: layer = wxT( "F.Cu" ); break;
|
|
|
|
case 1: layer = wxT( "F.SilkS" ); break;
|
|
|
|
case 2: layer = wxT( "F.Mask" ); break;
|
|
|
|
case 3: layer = wxT( "Dwgs.User" ); break;
|
|
|
|
case 4: layer = wxT( "Cmts.User" ); break;
|
|
|
|
case 5: layer = wxT( "Eco1.User" ); break;
|
|
|
|
case 6: layer = wxT( "Eco2.User" ); break;
|
|
|
|
case 7: layer = wxT( "F.Fab" ); break;
|
|
|
|
}
|
|
|
|
}
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2025-01-19 17:45:40 -08:00
|
|
|
|
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
WX_STRING_REPORTER reporter;
|
|
|
|
BITMAPCONV_INFO converter( aOutput, reporter );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
|
|
|
converter.ConvertBitmap( potrace_bitmap, aFormat, m_outputSizeX.GetOutputDPI(),
|
2025-01-18 12:01:28 +00:00
|
|
|
m_outputSizeY.GetOutputDPI(), layer );
|
2019-06-19 07:18:30 -07:00
|
|
|
|
2025-01-18 12:01:28 +00:00
|
|
|
if( reporter.HasMessage() )
|
|
|
|
wxMessageBox( reporter.GetMessages(), _( "Errors" ) );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 17:38:43 +01:00
|
|
|
void BITMAP2CMP_PANEL::OnFormatChange( wxCommandEvent& event )
|
2019-06-19 07:18:30 -07:00
|
|
|
{
|
2025-01-18 12:01:28 +00:00
|
|
|
m_layerLabel->Enable( m_rbFootprint->GetValue() );
|
|
|
|
m_layerCtrl->Enable( m_rbFootprint->GetValue() );
|
2019-06-19 07:18:30 -07:00
|
|
|
}
|