2011-09-30 18:15:37 +00:00
|
|
|
/**
|
2014-11-02 16:25:04 +00:00
|
|
|
* @file class_bitmap_base.cpp
|
2011-09-30 18:15:37 +00:00
|
|
|
*/
|
2011-08-31 14:59:20 +00:00
|
|
|
|
|
|
|
/*
|
2011-09-30 18:15:37 +00:00
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
2011-08-31 14:59:20 +00:00
|
|
|
*
|
2017-11-16 11:45:53 +00:00
|
|
|
* Copyright (C) 2017 jean-pierre.charras
|
2022-01-20 15:26:31 +00:00
|
|
|
* Copyright (C) 2011-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
2011-08-31 14:59:20 +00: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
|
|
|
|
*/
|
|
|
|
|
2020-01-07 17:12:59 +00:00
|
|
|
#include <bitmap_base.h>
|
|
|
|
#include <eda_rect.h> // for EDA_RECT
|
|
|
|
#include <gr_basic.h>
|
2021-08-18 20:38:14 +00:00
|
|
|
#include <math/util.h> // for KiROUND
|
2020-01-07 17:12:59 +00:00
|
|
|
#include <memory> // for make_unique, unique_ptr
|
2021-08-18 20:38:14 +00:00
|
|
|
#include <plotters/plotter.h>
|
2020-01-07 17:12:59 +00:00
|
|
|
#include <richio.h>
|
|
|
|
#include <wx/bitmap.h> // for wxBitmap
|
2011-08-31 14:59:20 +00:00
|
|
|
#include <wx/mstream.h>
|
|
|
|
|
|
|
|
|
|
|
|
BITMAP_BASE::BITMAP_BASE( const wxPoint& pos )
|
|
|
|
{
|
2020-11-28 09:53:26 +00:00
|
|
|
m_scale = 1.0; // 1.0 = original bitmap size
|
2021-07-15 19:26:35 +00:00
|
|
|
m_bitmap = nullptr;
|
|
|
|
m_image = nullptr;
|
2020-11-28 09:53:26 +00:00
|
|
|
m_ppi = 300; // the bitmap definition. the default is 300PPI
|
|
|
|
m_pixelSizeIu = 254000.0 / m_ppi; // a pixel size value OK for bitmaps using 300 PPI
|
|
|
|
// for Eeschema which uses currently 254000PPI
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BITMAP_BASE::BITMAP_BASE( const BITMAP_BASE& aSchBitmap )
|
|
|
|
{
|
2016-07-11 19:48:46 +00:00
|
|
|
m_scale = aSchBitmap.m_scale;
|
2015-02-26 10:33:15 +00:00
|
|
|
m_ppi = aSchBitmap.m_ppi;
|
2020-11-28 09:53:26 +00:00
|
|
|
m_pixelSizeIu = aSchBitmap.m_pixelSizeIu;
|
2019-05-23 15:52:07 +00:00
|
|
|
|
|
|
|
m_image = nullptr;
|
|
|
|
m_bitmap = nullptr;
|
|
|
|
|
|
|
|
if( aSchBitmap.m_image )
|
|
|
|
{
|
|
|
|
m_image = new wxImage( *aSchBitmap.m_image );
|
|
|
|
m_bitmap = new wxBitmap( *m_image );
|
|
|
|
}
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BITMAP_BASE::ImportData( BITMAP_BASE* aItem )
|
|
|
|
{
|
|
|
|
*m_image = *aItem->m_image;
|
|
|
|
*m_bitmap = *aItem->m_bitmap;
|
2016-07-11 19:48:46 +00:00
|
|
|
m_scale = aItem->m_scale;
|
2013-10-19 10:29:54 +00:00
|
|
|
m_ppi = aItem->m_ppi;
|
2020-11-28 09:53:26 +00:00
|
|
|
m_pixelSizeIu = aItem->m_pixelSizeIu;
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-24 15:35:01 +00:00
|
|
|
bool BITMAP_BASE::ReadImageFile( wxInputStream& aInStream )
|
|
|
|
{
|
|
|
|
auto new_image = std::make_unique<wxImage>();
|
|
|
|
|
|
|
|
if( !new_image->LoadFile( aInStream ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete m_image;
|
|
|
|
m_image = new_image.release();
|
|
|
|
m_bitmap = new wxBitmap( *m_image );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
bool BITMAP_BASE::ReadImageFile( const wxString& aFullFilename )
|
|
|
|
{
|
|
|
|
wxImage* new_image = new wxImage();
|
|
|
|
|
|
|
|
if( !new_image->LoadFile( aFullFilename ) )
|
|
|
|
{
|
|
|
|
delete new_image;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete m_image;
|
|
|
|
m_image = new_image;
|
|
|
|
m_bitmap = new wxBitmap( *m_image );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BITMAP_BASE::SaveData( FILE* aFile ) const
|
|
|
|
{
|
|
|
|
if( m_image )
|
|
|
|
{
|
|
|
|
wxMemoryOutputStream stream;
|
|
|
|
m_image->SaveFile( stream, wxBITMAP_TYPE_PNG );
|
|
|
|
|
|
|
|
// Write binary data in hexadecimal form (ASCII)
|
|
|
|
wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
|
|
|
|
char* begin = (char*) buffer->GetBufferStart();
|
2016-02-22 21:23:17 +00:00
|
|
|
|
2016-11-09 08:46:35 +00:00
|
|
|
for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
|
2011-08-31 14:59:20 +00:00
|
|
|
{
|
|
|
|
if( ii >= 32 )
|
|
|
|
{
|
|
|
|
ii = 0;
|
2016-02-22 21:23:17 +00:00
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
if( fprintf( aFile, "\n" ) == EOF )
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-22 21:23:17 +00:00
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
if( fprintf( aFile, "%2.2X ", *begin & 0xFF ) == EOF )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-02-22 21:23:17 +00:00
|
|
|
|
2013-10-18 17:38:03 +00:00
|
|
|
void BITMAP_BASE::SaveData( wxArrayString& aPngStrings ) const
|
|
|
|
{
|
|
|
|
if( m_image )
|
|
|
|
{
|
|
|
|
wxMemoryOutputStream stream;
|
|
|
|
m_image->SaveFile( stream, wxBITMAP_TYPE_PNG );
|
|
|
|
|
|
|
|
// Write binary data in hexadecimal form (ASCII)
|
|
|
|
wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
|
|
|
|
char* begin = (char*) buffer->GetBufferStart();
|
2016-02-22 21:23:17 +00:00
|
|
|
wxString line;
|
|
|
|
|
2016-11-09 08:46:35 +00:00
|
|
|
for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
|
2013-10-18 17:38:03 +00:00
|
|
|
{
|
|
|
|
if( ii >= 32 )
|
|
|
|
{
|
|
|
|
ii = 0;
|
|
|
|
aPngStrings.Add( line );
|
|
|
|
line.Empty();
|
|
|
|
}
|
|
|
|
|
2016-02-22 21:23:17 +00:00
|
|
|
line << wxString::Format( wxT( "%2.2X " ), *begin & 0xFF );
|
2013-10-18 17:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add last line:
|
|
|
|
if( !line.IsEmpty() )
|
|
|
|
aPngStrings.Add( line );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
|
|
|
|
bool BITMAP_BASE::LoadData( LINE_READER& aLine, wxString& aErrorMsg )
|
|
|
|
{
|
|
|
|
wxMemoryOutputStream stream;
|
|
|
|
char* line;
|
|
|
|
|
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
if( !aLine.ReadLine() )
|
2013-10-18 17:38:03 +00:00
|
|
|
{
|
|
|
|
aErrorMsg = wxT("Unexpected end of data");
|
2011-08-31 14:59:20 +00:00
|
|
|
return false;
|
2013-10-18 17:38:03 +00:00
|
|
|
}
|
2011-08-31 14:59:20 +00:00
|
|
|
|
|
|
|
line = aLine.Line();
|
2013-10-18 17:38:03 +00:00
|
|
|
|
2016-08-16 08:27:09 +00:00
|
|
|
if( strncasecmp( line, "EndData", 4 ) == 0 )
|
2011-08-31 14:59:20 +00:00
|
|
|
{
|
|
|
|
// all the PNG date is read.
|
|
|
|
// We expect here m_image and m_bitmap are void
|
|
|
|
m_image = new wxImage();
|
|
|
|
wxMemoryInputStream istream( stream );
|
|
|
|
m_image->LoadFile( istream, wxBITMAP_TYPE_PNG );
|
|
|
|
m_bitmap = new wxBitmap( *m_image );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read PNG data, stored in hexadecimal,
|
|
|
|
// each byte = 2 hexadecimal digits and a space between 2 bytes
|
|
|
|
// and put it in memory stream buffer
|
|
|
|
int len = strlen( line );
|
2016-07-11 19:48:46 +00:00
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
for( ; len > 0; len -= 3, line += 3 )
|
|
|
|
{
|
|
|
|
int value = 0;
|
2016-07-11 19:48:46 +00:00
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
if( sscanf( line, "%X", &value ) == 1 )
|
|
|
|
stream.PutC( (char) value );
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-18 17:28:14 +00:00
|
|
|
const EDA_RECT BITMAP_BASE::GetBoundingBox() const
|
2011-08-31 14:59:20 +00:00
|
|
|
{
|
|
|
|
EDA_RECT rect;
|
|
|
|
|
|
|
|
wxSize size = GetSize();
|
|
|
|
|
|
|
|
rect.Inflate( size.x / 2, size.y / 2 );
|
|
|
|
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-03 09:04:57 +00:00
|
|
|
void BITMAP_BASE::DrawBitmap( wxDC* aDC, const wxPoint& aPos )
|
2011-08-31 14:59:20 +00:00
|
|
|
{
|
2021-07-15 19:26:35 +00:00
|
|
|
if( m_bitmap == nullptr )
|
2011-08-31 14:59:20 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
wxPoint pos = aPos;
|
|
|
|
wxSize size = GetSize();
|
|
|
|
|
2016-02-22 21:23:17 +00:00
|
|
|
// This fixes a bug in OSX that should be fixed in the 3.0.3 version or later.
|
|
|
|
if( ( size.x == 0 ) || ( size.y == 0 ) )
|
|
|
|
return;
|
|
|
|
|
2011-08-31 14:59:20 +00:00
|
|
|
// To draw the bitmap, pos is the upper left corner position
|
|
|
|
pos.x -= size.x / 2;
|
|
|
|
pos.y -= size.y / 2;
|
|
|
|
|
|
|
|
double scale;
|
|
|
|
int logicalOriginX, logicalOriginY;
|
|
|
|
aDC->GetUserScale( &scale, &scale );
|
|
|
|
aDC->GetLogicalOrigin( &logicalOriginX, &logicalOriginY );
|
2020-01-11 17:44:54 +00:00
|
|
|
|
2020-05-24 19:00:11 +00:00
|
|
|
bool useTransform = aDC->CanUseTransformMatrix();
|
2022-08-04 12:56:16 +00:00
|
|
|
|
|
|
|
// We already have issues to draw a bitmap on the wxDC, depending on wxWidgets version.
|
|
|
|
// Now we have an issue on wxWidgets 3.1.6 and later to fix the clipboard
|
|
|
|
// and the bitmap position when using TransformMatrix
|
|
|
|
// So for version >= 3.1.6 do not use it
|
|
|
|
// Be carefull before changing the code.
|
|
|
|
#if wxCHECK_VERSION( 3, 1, 6 )
|
|
|
|
useTransform = false;
|
|
|
|
#endif
|
|
|
|
|
2020-05-24 19:00:11 +00:00
|
|
|
wxAffineMatrix2D init_matrix = aDC->GetTransformMatrix();
|
|
|
|
|
2022-01-20 15:26:31 +00:00
|
|
|
wxPoint clipAreaPos;
|
|
|
|
|
2020-05-24 19:00:11 +00:00
|
|
|
if( useTransform )
|
|
|
|
{
|
|
|
|
wxAffineMatrix2D matrix = aDC->GetTransformMatrix();
|
|
|
|
matrix.Translate( pos.x, pos.y );
|
|
|
|
matrix.Scale( GetScalingFactor(), GetScalingFactor() );
|
|
|
|
aDC->SetTransformMatrix( matrix );
|
2022-08-04 12:56:16 +00:00
|
|
|
// Needed on wx <= 3.1.5, and this is strange...
|
|
|
|
// Nevertheless, this code has problem (the bitmap is not seen)
|
|
|
|
// with wx version > 3.1.5
|
2022-01-20 15:26:31 +00:00
|
|
|
clipAreaPos.x = pos.x;
|
|
|
|
clipAreaPos.y = pos.y;
|
2022-07-29 11:35:51 +00:00
|
|
|
|
2020-05-24 19:00:11 +00:00
|
|
|
pos.x = pos.y = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aDC->SetUserScale( scale * GetScalingFactor(), scale * GetScalingFactor() );
|
|
|
|
aDC->SetLogicalOrigin( logicalOriginX / GetScalingFactor(),
|
|
|
|
logicalOriginY / GetScalingFactor() );
|
|
|
|
|
|
|
|
pos.x = KiROUND( pos.x / GetScalingFactor() );
|
|
|
|
pos.y = KiROUND( pos.y / GetScalingFactor() );
|
2022-01-08 01:15:59 +00:00
|
|
|
size.x = KiROUND( size.x / GetScalingFactor() );
|
|
|
|
size.y = KiROUND( size.y / GetScalingFactor() );
|
2022-01-20 15:26:31 +00:00
|
|
|
clipAreaPos.x = pos.x;
|
|
|
|
clipAreaPos.y = pos.y;
|
2020-05-24 19:00:11 +00:00
|
|
|
}
|
2020-01-11 17:55:30 +00:00
|
|
|
|
2022-01-08 01:15:59 +00:00
|
|
|
aDC->DestroyClippingRegion();
|
2022-01-20 15:26:31 +00:00
|
|
|
aDC->SetClippingRegion( clipAreaPos, size );
|
2022-01-08 01:15:59 +00:00
|
|
|
|
2020-01-11 17:55:30 +00:00
|
|
|
if( GetGRForceBlackPenState() )
|
|
|
|
{
|
|
|
|
wxBitmap result( m_bitmap->ConvertToImage().ConvertToGreyscale() );
|
|
|
|
aDC->DrawBitmap( result, pos.x, pos.y, true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aDC->DrawBitmap( *m_bitmap, pos.x, pos.y, true );
|
|
|
|
}
|
|
|
|
|
2020-05-24 19:00:11 +00:00
|
|
|
if( useTransform )
|
|
|
|
aDC->SetTransformMatrix( init_matrix );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aDC->SetUserScale( scale, scale );
|
|
|
|
aDC->SetLogicalOrigin( logicalOriginX, logicalOriginY );
|
|
|
|
}
|
2022-01-08 01:15:59 +00:00
|
|
|
|
|
|
|
aDC->DestroyClippingRegion();
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxSize BITMAP_BASE::GetSize() const
|
|
|
|
{
|
|
|
|
wxSize size;
|
|
|
|
|
|
|
|
if( m_bitmap )
|
|
|
|
{
|
|
|
|
size.x = m_bitmap->GetWidth();
|
|
|
|
size.y = m_bitmap->GetHeight();
|
|
|
|
|
// Dick Hollenbeck's KiROUND R&D
// This provides better project control over rounding to int from double
// than wxRound() did. This scheme provides better logging in Debug builds
// and it provides for compile time calculation of constants.
#include <stdio.h>
#include <assert.h>
#include <limits.h>
//-----<KiROUND KIT>------------------------------------------------------------
/**
* KiROUND
* rounds a floating point number to an int using
* "round halfway cases away from zero".
* In Debug build an assert fires if will not fit into an int.
*/
#if defined( DEBUG )
// DEBUG: a macro to capture line and file, then calls this inline
static inline int KiRound( double v, int line, const char* filename )
{
v = v < 0 ? v - 0.5 : v + 0.5;
if( v > INT_MAX + 0.5 )
{
printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v );
}
else if( v < INT_MIN - 0.5 )
{
printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v );
}
return int( v );
}
#define KiROUND( v ) KiRound( v, __LINE__, __FILE__ )
#else
// RELEASE: a macro so compile can pre-compute constants.
#define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 )
#endif
//-----</KiROUND KIT>-----------------------------------------------------------
// Only a macro is compile time calculated, an inline function causes a static constructor
// in a situation like this.
// Therefore the Release build is best done with a MACRO not an inline function.
int Computed = KiROUND( 14.3 * 8 );
int main( int argc, char** argv )
{
for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 )
{
int i = KiROUND( d );
printf( "t: %d %.16g\n", i, d );
}
return 0;
}
2012-04-19 06:55:45 +00:00
|
|
|
size.x = KiROUND( size.x * GetScalingFactor() );
|
|
|
|
size.y = KiROUND( size.y * GetScalingFactor() );
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BITMAP_BASE::Mirror( bool aVertically )
|
|
|
|
{
|
|
|
|
if( m_image )
|
|
|
|
{
|
|
|
|
*m_image = m_image->Mirror( not aVertically );
|
2011-09-02 19:43:56 +00:00
|
|
|
RebuildBitmap();
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BITMAP_BASE::Rotate( bool aRotateCCW )
|
|
|
|
{
|
|
|
|
if( m_image )
|
|
|
|
{
|
|
|
|
*m_image = m_image->Rotate90( aRotateCCW );
|
2011-09-02 19:43:56 +00:00
|
|
|
RebuildBitmap();
|
2011-08-31 14:59:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-03 18:37:56 +00:00
|
|
|
void BITMAP_BASE::PlotImage( PLOTTER* aPlotter,
|
|
|
|
const wxPoint& aPos,
|
2021-07-26 17:28:37 +00:00
|
|
|
const COLOR4D& aDefaultColor,
|
2021-03-06 09:27:41 +00:00
|
|
|
int aDefaultPensize ) const
|
2011-08-31 14:59:20 +00:00
|
|
|
{
|
2021-07-15 19:26:35 +00:00
|
|
|
if( m_image == nullptr )
|
2011-08-31 14:59:20 +00:00
|
|
|
return;
|
|
|
|
|
2016-02-22 21:23:17 +00:00
|
|
|
// These 2 lines are useful only for plotters that cannot plot a bitmap
|
2012-09-15 12:13:03 +00:00
|
|
|
// and plot a rectangle instead of.
|
2012-05-03 18:37:56 +00:00
|
|
|
aPlotter->SetColor( aDefaultColor );
|
|
|
|
aPlotter->SetCurrentLineWidth( aDefaultPensize );
|
2011-08-31 14:59:20 +00:00
|
|
|
aPlotter->PlotImage( *m_image, aPos, GetScalingFactor() );
|
|
|
|
}
|