bitmap2cmp_gui.cpp 20.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * This program source code file is part of KICAD, a free EDA CAD application.
 *
 * Copyright (C) 1992-2010 jean-pierre.charras
 * Copyright (C) 1992-2010 Kicad Developers, see change_log.txt for contributors.
 *
 * 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
 */
24

25
#include <fctsys.h>
26 27 28
#include <macros.h>

#include <pgm_base.h>
29 30 31
#include <wxstruct.h>
#include <confirm.h>
#include <gestfich.h>
32
#include <wildcards_and_files_ext.h>
33

34
#include <bitmap2cmp_gui_base.h>
35
#include <bitmap2component.h>
36

37 38
#include <potracelib.h>
#include <bitmap_io.h>
39

40 41
#include <colors_selection.h>
#include <build_version.h>
42
#include <menus_helpers.h>
43 44 45 46 47 48 49 50 51 52 53 54
#include <kiway.h>
#include <kiface_i.h>

#define KEYWORD_FRAME_POSX          wxT( "Bmconverter_Pos_x" )
#define KEYWORD_FRAME_POSY          wxT( "Bmconverter_Pos_y" )
#define KEYWORD_FRAME_SIZEX         wxT( "Bmconverter_Size_x" )
#define KEYWORD_FRAME_SIZEY         wxT( "Bmconverter_Size_y" )
#define KEYWORD_LAST_INPUT_FILE     wxT( "Last_input" )
#define KEYWORD_LAST_OUTPUT_FILE    wxT( "Last_output" )
#define KEYWORD_LAST_FORMAT         wxT( "Last_format" )
#define KEYWORD_BINARY_THRESHOLD    wxT( "Threshold" )
#define KEYWORD_BW_NEGATIVE         wxT( "Negative_choice" )
55

56
#define DEFAULT_DPI 300     // Default resolution in Bit per inches
57

58
extern int bitmap2component( potrace_bitmap_t* aPotrace_bitmap, FILE* aOutfile,
59
                             OUTPUT_FMT_ID aFormat, int aDpi_X, int aDpi_Y );
60

61 62 63 64
/**
 * Class BM2CMP_FRAME_BASE
 * is the main frame for this application
 */
65 66 67
class BM2CMP_FRAME : public BM2CMP_FRAME_BASE
{
private:
68 69 70 71 72 73
    wxImage     m_Pict_Image;
    wxBitmap    m_Pict_Bitmap;
    wxImage     m_Greyscale_Image;
    wxBitmap    m_Greyscale_Bitmap;
    wxImage     m_NB_Image;
    wxBitmap    m_BN_Bitmap;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
74
    wxSize      m_imageDPI;         // The initial image resolution. When unknown,
75
                                    // set to DEFAULT_DPI x DEFAULT_DPI per Inch
76 77
    wxString    m_BitmapFileName;
    wxString    m_ConvertedFileName;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
78 79 80
    wxSize      m_frameSize;
    wxPoint     m_framePos;
    wxConfig*   m_config;
81 82

public:
83
    BM2CMP_FRAME( KIWAY* aKiway, wxWindow* aParent );
84 85
    ~BM2CMP_FRAME();

86 87 88
    // overload KIWAY_PLAYER virtual
    bool OpenProjectFiles( const std::vector<wxString>& aFilenames, int aCtl=0 );

89 90 91 92 93
private:

    // Event handlers
    void OnPaint( wxPaintEvent& event );
    void OnLoadFile( wxCommandEvent& event );
94 95 96 97 98 99 100 101 102
    void OnExport( wxCommandEvent& event );

    /**
     * Generate a schematic library which comtains one component:
     * the logo
     */
    void OnExportEeschema();

    /**
103
     * Generate a module in S expr format
104
     */
105
    void OnExportPcbnew();
106 107 108 109 110 111 112 113 114 115 116 117

    /**
     * Generate a postscript file
     */
    void OnExportPostScript();

    /**
     * Generate a file suitable to be copied into a page layout
     * description file (.kicad_wks file
     */
    void OnExportLogo();

118
    void Binarize( double aThreshold );     // aThreshold = 0.0 (black level) to 1.0 (white level)
119 120
    void OnOptionsSelection( wxCommandEvent& event );
    void OnThresholdChange( wxScrollEvent& event );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
121
    void OnResolutionChange( wxCommandEvent& event );
122 123 124 125 126 127

    // called when texts controls which handle the image resolution
    // lose the focus, to ensure the rigyht vaules are displayed
    // because the m_imageDPI are clipped to acceptable values, and
    // the text displayed could be differ duringa text edition
    // We are using ChangeValue here to avoid generating a wxEVT_TEXT event.
Dick Hollenbeck's avatar
Dick Hollenbeck committed
128
    void UpdateDPITextValueX( wxMouseEvent& event )
129 130 131
    {
        m_DPIValueX->ChangeValue( wxString::Format( wxT( "%d" ), m_imageDPI.x ) );
    }
132

Dick Hollenbeck's avatar
Dick Hollenbeck committed
133
    void UpdateDPITextValueY( wxMouseEvent& event )
134 135 136 137
    {
        m_DPIValueY->ChangeValue( wxString::Format( wxT( "%d" ), m_imageDPI.y ) );
    }

138
    void NegateGreyscaleImage( );
139
    void ExportFile( FILE* aOutfile, OUTPUT_FMT_ID aFormat );
140
    void updateImageInfo();
141 142
};

143

144 145
BM2CMP_FRAME::BM2CMP_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
    BM2CMP_FRAME_BASE( aParent )
146
{
147 148
    SetKiway( this, aKiway );

149
    int tmp;
150 151 152 153 154 155 156
    m_config = new wxConfig();
    m_config->Read( KEYWORD_FRAME_POSX, & m_framePos.x, -1 );
    m_config->Read( KEYWORD_FRAME_POSY, & m_framePos.y, -1 );
    m_config->Read( KEYWORD_FRAME_SIZEX, & m_frameSize.x, -1 );
    m_config->Read( KEYWORD_FRAME_SIZEY, & m_frameSize.y, -1 );
    m_config->Read( KEYWORD_LAST_INPUT_FILE, &m_BitmapFileName );
    m_config->Read( KEYWORD_LAST_OUTPUT_FILE, &m_ConvertedFileName );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
157

158
    if( m_config->Read( KEYWORD_BINARY_THRESHOLD, &tmp ) )
159
        m_sliderThreshold->SetValue( tmp );
160

161
    if( m_config->Read( KEYWORD_BW_NEGATIVE, &tmp ) )
162 163
        m_rbOptions->SetSelection( tmp  ? 1 : 0 );

164 165 166 167 168 169 170
    if( m_config->Read( KEYWORD_LAST_FORMAT, &tmp ) )
    {
        if( tmp < 0 || tmp > FINAL_FMT )
            tmp = PCBNEW_KICAD_MOD;

        m_radioBoxFormat->SetSelection( tmp );
    }
171

172 173 174 175
    // Give an icon
    wxIcon icon;
    icon.CopyFromBitmap( KiBitmap( icon_bitmap2component_xpm ) );
    SetIcon( icon );
176

177
    GetSizer()->SetSizeHints( this );
178

179
    SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
180

181
    m_buttonExport->Enable( false );
182

183 184 185
    m_imageDPI.x = m_imageDPI.y = DEFAULT_DPI;  // Default resolution in Bit per inches

    if ( m_framePos == wxDefaultPosition )
186 187 188
        Centre();
}

189

190 191
BM2CMP_FRAME::~BM2CMP_FRAME()
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
192
    if( !m_config || IsIconized() )
193 194
        return;

195 196
    m_frameSize = GetSize();
    m_framePos  = GetPosition();
197

198 199 200 201 202 203 204 205 206
    m_config->Write( KEYWORD_FRAME_POSX, (long) m_framePos.x );
    m_config->Write( KEYWORD_FRAME_POSY, (long) m_framePos.y );
    m_config->Write( KEYWORD_FRAME_SIZEX, (long) m_frameSize.x );
    m_config->Write( KEYWORD_FRAME_SIZEY, (long) m_frameSize.y );
    m_config->Write( KEYWORD_LAST_INPUT_FILE, m_BitmapFileName );
    m_config->Write( KEYWORD_LAST_OUTPUT_FILE, m_ConvertedFileName );
    m_config->Write( KEYWORD_BINARY_THRESHOLD, m_sliderThreshold->GetValue() );
    m_config->Write( KEYWORD_BW_NEGATIVE, m_rbOptions->GetSelection() );
    m_config->Write( KEYWORD_LAST_FORMAT,  m_radioBoxFormat->GetSelection() );
207

208
    delete m_config;
209

210
    /* This needed for OSX: avoids further OnDraw processing after this
211 212 213 214 215 216 217 218
     * destructor and before the native window is destroyed
     */
    this->Freeze( );
}


void BM2CMP_FRAME::OnPaint( wxPaintEvent& event )
{
219 220 221 222 223 224
#ifdef __WXMAC__
    // Otherwise fails due: using wxPaintDC without being in a native paint event
    wxClientDC pict_dc( m_InitialPicturePanel );
    wxClientDC greyscale_dc( m_GreyscalePicturePanel );
    wxClientDC nb_dc( m_BNPicturePanel );
#else
225 226 227
    wxPaintDC pict_dc( m_InitialPicturePanel );
    wxPaintDC greyscale_dc( m_GreyscalePicturePanel );
    wxPaintDC nb_dc( m_BNPicturePanel );
228
#endif
229 230 231 232

    m_InitialPicturePanel->PrepareDC( pict_dc );
    m_GreyscalePicturePanel->PrepareDC( greyscale_dc );
    m_BNPicturePanel->PrepareDC( nb_dc );
233

234 235 236 237 238 239 240
    // OSX crashes with empty bitmaps (on initial refreshes)
    if(m_Pict_Bitmap.IsOk() && m_Greyscale_Bitmap.IsOk() && m_BN_Bitmap.IsOk())
    {
        pict_dc.DrawBitmap( m_Pict_Bitmap, 0, 0, false );
        greyscale_dc.DrawBitmap( m_Greyscale_Bitmap, 0, 0, false );
        nb_dc.DrawBitmap( m_BN_Bitmap, 0, 0, false );
    }
241 242
}

243

244 245 246 247
/* Called to load a bitmap file
 */
void BM2CMP_FRAME::OnLoadFile( wxCommandEvent& event )
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
248
    wxFileName  fn( m_BitmapFileName );
249
    wxString    path = fn.GetPath();
250

jean-pierre charras's avatar
jean-pierre charras committed
251 252 253
    if( path.IsEmpty() || !wxDirExists(path) )
        path = wxGetCwd();

254
    wxFileDialog fileDlg( this, _( "Choose Image" ), path, wxEmptyString,
255 256
                          _( "Image Files " ) + wxImage::GetImageExtWildcard(),
                          wxFD_OPEN );
257 258

    int diag = fileDlg.ShowModal();
259 260 261

    if( diag != wxID_OK )
        return;
262

263
    wxString fullFilename = fileDlg.GetPath();
264

265
    if( !OpenProjectFiles( std::vector<wxString>( 1, fullFilename ) ) )
266 267
        return;

268
    m_buttonExport->Enable( true );
269 270 271
    SetStatusText( fullFilename );
    Refresh();
}
272

273

274
bool BM2CMP_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
275
{
276 277 278
    // Prj().MaybeLoadProjectSettings();

    m_BitmapFileName = aFileSet[0];
jean-pierre charras's avatar
jean-pierre charras committed
279 280

    if( !m_Pict_Image.LoadFile( m_BitmapFileName ) )
281
    {
282
        // LoadFile has its own UI, no need for further failure notification here
283
        return false;
284 285 286 287 288 289 290
    }

    m_Pict_Bitmap = wxBitmap( m_Pict_Image );

    int h  = m_Pict_Bitmap.GetHeight();
    int w  = m_Pict_Bitmap.GetWidth();

291 292 293 294
    // 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
    m_imageDPI.x = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
    m_imageDPI.y = m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309
    if( m_imageDPI.x > 1 && m_imageDPI.y > 1 )
    {
        if( m_Pict_Image.GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
        {
            // When the initial resolution is given in bits per cm,
            // experience shows adding 1.27 to the resolution converted in dpi
            // before convert to int value reduce the conversion error
            // but it is not perfect
            m_imageDPI.x = m_imageDPI.x * 2.54 + 1.27;
            m_imageDPI.y = m_imageDPI.y * 2.54 + 1.27;
        }
    }
    else    // fallback to the default value
        m_imageDPI.x = m_imageDPI.y = DEFAULT_DPI;
310

311 312 313 314 315
    // Display image info:
    // We are using ChangeValue here to avoid generating a wxEVT_TEXT event.
    m_DPIValueX->ChangeValue( wxString::Format( wxT( "%d" ), m_imageDPI.x ) );
    m_DPIValueY->ChangeValue( wxString::Format( wxT( "%d" ), m_imageDPI.y ) );
    updateImageInfo();
316 317 318 319 320 321 322

    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( );
323

324 325
    if( m_rbOptions->GetSelection() > 0 )
        NegateGreyscaleImage( );
326

327 328 329
    m_Greyscale_Bitmap = wxBitmap( m_Greyscale_Image );

    m_NB_Image  = m_Greyscale_Image;
330
    Binarize( (double) m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
331

332 333
    return true;
}
334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
void BM2CMP_FRAME::updateImageInfo()
{
    // 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 modifed here.
    int h  = m_Pict_Bitmap.GetHeight();
    int w  = m_Pict_Bitmap.GetWidth();
    int nb = m_Pict_Bitmap.GetDepth();

    m_SizeXValue->SetLabel( wxString::Format( wxT( "%d" ), w ) );
    m_SizeYValue->SetLabel( wxString::Format( wxT( "%d" ), h ) );
    m_BPPValue->SetLabel( wxString::Format( wxT( "%d" ), nb ) );

    m_SizeXValue_mm->SetLabel( wxString::Format( wxT( "%.1f" ),
        (double) w / m_imageDPI.x * 25.4 ) );
    m_SizeYValue_mm->SetLabel( wxString::Format( wxT( "%.1f" ),
        (double) h / m_imageDPI.y * 25.4 ) );
}

void BM2CMP_FRAME::OnResolutionChange( wxCommandEvent& event )
{
    long tmp;

    if( m_DPIValueX->GetValue().ToLong( &tmp ) )
        m_imageDPI.x = tmp;

    if(  m_DPIValueY->GetValue().ToLong( &tmp ) )
        m_imageDPI.y = tmp;

    if( m_imageDPI.x < 32 )
        m_imageDPI.x = 32;

    if( m_imageDPI.y < 32 )
        m_imageDPI.y = 32;

    updateImageInfo();
}
372

373
void BM2CMP_FRAME::Binarize( double aThreshold )
374 375 376 377 378
{
    unsigned int  pixin;
    unsigned char pixout;
    int           h = m_Greyscale_Image.GetHeight();
    int           w = m_Greyscale_Image.GetWidth();
379
    unsigned int  threshold = (int)(aThreshold * 256);
380

381 382
    for( int y = 0; y < h; y++ )
        for( int x = 0; x < w; x++ )
383 384
        {
            pixin   = m_Greyscale_Image.GetGreen( x, y );
385

386 387 388 389
            if( pixin < threshold )
                pixout = 0;
            else
                pixout = 255;
390

391 392 393 394 395 396
            m_NB_Image.SetRGB( x, y, pixout, pixout, pixout );
        }

    m_BN_Bitmap = wxBitmap( m_NB_Image );
}

397

398 399 400
void BM2CMP_FRAME::NegateGreyscaleImage( )
{
    unsigned char  pix;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
401 402
    int             h = m_Greyscale_Image.GetHeight();
    int             w = m_Greyscale_Image.GetWidth();
403

404 405
    for( int y = 0; y < h; y++ )
        for( int x = 0; x < w; x++ )
406 407 408 409 410 411 412 413 414 415 416 417
        {
            pix   = m_Greyscale_Image.GetGreen( x, y );
            pix = ~pix;
            m_Greyscale_Image.SetRGB( x, y, pix, pix, pix );
        }
}

/* Called on Normal/Negative change option */
void BM2CMP_FRAME::OnOptionsSelection( wxCommandEvent& event )
{
    NegateGreyscaleImage( );
    m_Greyscale_Bitmap = wxBitmap( m_Greyscale_Image );
418
    Binarize( (double)m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
419 420 421
    Refresh();
}

422

423 424
void BM2CMP_FRAME::OnThresholdChange( wxScrollEvent& event )
{
425
    Binarize( (double)m_sliderThreshold->GetValue()/m_sliderThreshold->GetMax() );
426 427 428
    Refresh();
}

429

430 431
void BM2CMP_FRAME::OnExport( wxCommandEvent& event )
{
432 433 434
    // choices of m_radioBoxFormat are expected to be in same order as
    // OUTPUT_FMT_ID. See bitmap2component.h
    OUTPUT_FMT_ID sel = (OUTPUT_FMT_ID) m_radioBoxFormat->GetSelection();
435 436 437

    switch( sel )
    {
438
    case EESCHEMA_FMT:
439 440
        OnExportEeschema();
        break;
441

442 443
    case PCBNEW_KICAD_MOD:
        OnExportPcbnew();
444
        break;
445

446
    case POSTSCRIPT_FMT:
447 448
        OnExportPostScript();
        break;
449

450
    case KICAD_LOGO:
451 452
        OnExportLogo();
        break;
453 454 455
    }
}

456

457 458
void BM2CMP_FRAME::OnExportLogo()
{
Dick Hollenbeck's avatar
Dick Hollenbeck committed
459
    wxFileName  fn( m_ConvertedFileName );
460
    wxString    path = fn.GetPath();
461 462 463 464

    if( path.IsEmpty() || !wxDirExists(path) )
        path = ::wxGetCwd();

465 466 467
    wxFileDialog fileDlg( this, _( "Create a logo file" ),
                          path, wxEmptyString,
                          wxGetTranslation( PageLayoutDescrFileWildcard ),
468
                          wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
469
    int          diag = fileDlg.ShowModal();
470 471 472 473

    if( diag != wxID_OK )
        return;

474 475 476
    fn = fileDlg.GetPath();
    fn.SetExt( PageLayoutDescrFileExtension );
    m_ConvertedFileName = fn.GetFullPath();
477

478 479 480 481 482 483
    FILE*    outfile;
    outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );

    if( outfile == NULL )
    {
        wxString msg;
484
        msg.Printf( _( "File '%s' could not be created" ), GetChars(m_ConvertedFileName) );
485 486 487 488
        wxMessageBox( msg );
        return;
    }

489
    ExportFile( outfile, KICAD_LOGO );
490 491 492
    fclose( outfile );
}

493

494 495
void BM2CMP_FRAME::OnExportPostScript()
{
496 497
    wxFileName  fn( m_ConvertedFileName );
    wxString    path = fn.GetPath();
498

499
    if( path.IsEmpty() || !wxDirExists( path ) )
500 501
        path = ::wxGetCwd();

502 503 504
    wxFileDialog fileDlg( this, _( "Create a Postscript file" ),
                          path, wxEmptyString,
                          wxGetTranslation( PSFileWildcard ),
505
                          wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
506 507

    int          diag = fileDlg.ShowModal();
508 509 510 511

    if( diag != wxID_OK )
        return;

512 513 514
    fn = fileDlg.GetPath();
    fn.SetExt( wxT( "ps" ) );
    m_ConvertedFileName = fn.GetFullPath();
515

516 517 518 519 520 521
    FILE*    outfile;
    outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );

    if( outfile == NULL )
    {
        wxString msg;
522
        msg.Printf( _( "File '%s' could not be created" ), GetChars( m_ConvertedFileName ) );
523 524 525 526
        wxMessageBox( msg );
        return;
    }

527
    ExportFile( outfile, POSTSCRIPT_FMT );
528 529
    fclose( outfile );
}
530

531

532
void BM2CMP_FRAME::OnExportEeschema()
533
{
534 535
    wxFileName  fn( m_ConvertedFileName );
    wxString    path = fn.GetPath();
536

jean-pierre charras's avatar
jean-pierre charras committed
537 538
    if( path.IsEmpty() || !wxDirExists(path) )
        path = ::wxGetCwd();
539

540 541 542
    wxFileDialog fileDlg( this, _( "Create a lib file for Eeschema" ),
                          path, wxEmptyString,
                          wxGetTranslation( SchematicLibraryFileWildcard ),
543
                          wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
544 545

    int          diag = fileDlg.ShowModal();
546 547 548

    if( diag != wxID_OK )
        return;
549

550 551 552
    fn = fileDlg.GetPath();
    fn.SetExt( SchematicLibraryFileExtension );
    m_ConvertedFileName = fn.GetFullPath();
553

554
    FILE*    outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );
555

556 557 558
    if( outfile == NULL )
    {
        wxString msg;
559
        msg.Printf( _( "File '%s' could not be created" ), GetChars( m_ConvertedFileName ) );
560 561 562 563
        wxMessageBox( msg );
        return;
    }

564
    ExportFile( outfile, EESCHEMA_FMT );
565 566 567 568
    fclose( outfile );
}


569
void BM2CMP_FRAME::OnExportPcbnew()
570
{
571 572
    wxFileName  fn( m_ConvertedFileName );
    wxString    path = fn.GetPath();
573

574
    if( path.IsEmpty() || !wxDirExists( path ) )
jean-pierre charras's avatar
jean-pierre charras committed
575
        path = ::wxGetCwd();
576

577
    wxFileDialog fileDlg( this, _( "Create a footprint file for PcbNew" ),
578
                          path, wxEmptyString,
579
                          wxGetTranslation( KiCadFootprintLibFileWildcard ),
580
                          wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
581 582

    int          diag = fileDlg.ShowModal();
583 584 585

    if( diag != wxID_OK )
        return;
586

587 588 589
    fn = fileDlg.GetPath();
    fn.SetExt( KiCadFootprintFileExtension );
    m_ConvertedFileName = fn.GetFullPath();
590

591
    FILE* outfile = wxFopen( m_ConvertedFileName, wxT( "w" ) );
592

593 594 595
    if( outfile == NULL )
    {
        wxString msg;
596
        msg.Printf( _( "File '%s' could not be created" ), GetChars( m_ConvertedFileName ) );
597 598 599 600
        wxMessageBox( msg );
        return;
    }

601
    ExportFile( outfile, PCBNEW_KICAD_MOD );
602 603 604
    fclose( outfile );
}

605

606
void BM2CMP_FRAME::ExportFile( FILE* aOutfile, OUTPUT_FMT_ID aFormat )
607 608 609 610 611
{
    // Create a potrace bitmap
    int h = m_NB_Image.GetHeight();
    int w = m_NB_Image.GetWidth();
    potrace_bitmap_t* potrace_bitmap = bm_new( w, h );
612

613 614 615
    if( !potrace_bitmap )
    {
        wxString msg;
616
        msg.Printf( wxT( "Error allocating memory for potrace bitmap" ) );
617 618 619 620 621
        wxMessageBox( msg );
        return;
    }

    /* fill the bitmap with data */
622
    for( int y = 0; y < h; y++ )
623
    {
624
        for( int x = 0; x < w; x++ )
625 626 627 628 629 630
        {
            unsigned char pix = m_NB_Image.GetGreen( x, y );
            BM_PUT( potrace_bitmap, x, y, pix ? 1 : 0 );
        }
    }

631
    bitmap2component( potrace_bitmap, aOutfile, aFormat, m_imageDPI.x, m_imageDPI.y );
632 633 634 635
}



636
//-----<KIFACE>-----------------------------------------------------------------
637

638
namespace BMP2CMP {
639

640
static struct IFACE : public KIFACE_I
641
{
642
    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits );
643 644 645 646 647

    wxWindow* CreateWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway, int aCtlBits = 0 )
    {
        switch( aClassId )
        {
648

649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
        default:
            {
                KIWAY_PLAYER* frame = new BM2CMP_FRAME( aKiway, aParent );
                return frame;
            }
        }
    }

    /**
     * Function IfaceOrAddress
     * return a pointer to the requested object.  The safest way to use this
     * is to retrieve a pointer to a static instance of an interface, similar to
     * how the KIFACE interface is exported.  But if you know what you are doing
     * use it to retrieve anything you want.
     *
     * @param aDataId identifies which object you want the address of.
     *
     * @return void* - and must be cast into the know type.
     */
    void* IfaceOrAddress( int aDataId )
    {
        return NULL;
    }
672

673 674 675
    IFACE( const char* aDSOname, KIWAY::FACE_T aType ) :
        KIFACE_I( aDSOname, aType )
    {}
676

677 678 679 680 681 682 683 684 685 686 687
} kiface( "BMP2CMP", KIWAY::FACE_BMP2CMP );

}   // namespace BMP2CMP

using namespace BMP2CMP;

static PGM_BASE* process;

KIFACE_I& Kiface()
{
    return kiface;
688
}
689 690


691 692 693
// KIFACE_GETTER's actual spelling is a substitution macro found in kiway.h.
// KIFACE_GETTER will not have name mangling due to declaration in kiway.h.
MY_API( KIFACE* ) KIFACE_GETTER( int* aKIFACEversion, int aKIWAYversion, PGM_BASE* aProgram )
694
{
695 696
    process = (PGM_BASE*) aProgram;
    return &kiface;
697
}
698 699 700 701 702 703 704 705 706 707 708


#if defined(BUILD_KIWAY_DLLS)
PGM_BASE& Pgm()
{
    wxASSERT( process );    // KIFACE_GETTER has already been called.
    return *process;
}
#endif


709
bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
710
{
711
    return start_common( aCtlBits );
712 713
}