PCB Editor: Add User Background Images

This commit is contained in:
Mike Williams 2022-02-08 14:29:54 -05:00
parent 1f4079802c
commit 3669cb4673
46 changed files with 978 additions and 30 deletions

View File

@ -512,6 +512,7 @@ set( PCB_COMMON_SRCS
${CMAKE_SOURCE_DIR}/pcbnew/netinfo_list.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pad.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_target.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_bitmap.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_text.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_textbox.cpp
${CMAKE_SOURCE_DIR}/pcbnew/board_stackup_manager/board_stackup.cpp

View File

@ -25,6 +25,10 @@
#include <wx/dcclient.h>
#include <confirm.h>
#include <bitmap_base.h>
#include <pcb_base_edit_frame.h>
#include <pcb_bitmap.h>
#include <tool/tool_manager.h>
#include <tool/actions.h>
#include <dialogs/dialog_image_editor.h>
@ -129,3 +133,24 @@ void DIALOG_IMAGE_EDITOR::TransferToImage( BITMAP_BASE* aItem )
m_workingImage->SetScale( scale );
aItem->ImportData( m_workingImage );
}
void PCB_BASE_EDIT_FRAME::ShowBitmapPropertiesDialog( BOARD_ITEM* aBitmap )
{
PCB_BITMAP* bitmap = static_cast<PCB_BITMAP*>( aBitmap );
DIALOG_IMAGE_EDITOR dlg( this, bitmap->GetImage() );
if( dlg.ShowModal() == wxID_OK )
{
// save old image in undo list if not already in edit
if( bitmap->GetEditFlags() == 0 )
SaveCopyInUndoList( bitmap, UNDO_REDO::CHANGED );
dlg.TransferToImage( bitmap->GetImage() );
// The bitmap is cached in Opengl: clear the cache in case it has become invalid
GetCanvas()->GetView()->RecacheAllItems();
m_toolManager->PostEvent( EVENTS::SelectedItemsModified );
this->OnModify();
}
}

View File

@ -315,6 +315,7 @@ static struct EDA_ITEM_DESC
.Map( PCB_FOOTPRINT_T, _HKI( "Footprint" ) )
.Map( PCB_PAD_T, _HKI( "Pad" ) )
.Map( PCB_SHAPE_T, _HKI( "Graphic" ) )
.Map( PCB_BITMAP_T, _HKI( "Bitmap" ) )
.Map( PCB_TEXT_T, _HKI( "Text" ) )
.Map( PCB_TEXTBOX_T, _HKI( "Text Box" ) )
.Map( PCB_FP_TEXT_T, _HKI( "Text" ) )

View File

@ -480,10 +480,12 @@ void CAIRO_GAL_BASE::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aCo
}
void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap )
void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
{
cairo_save( m_currentContext );
alphaBlend = std::clamp( alphaBlend, 0.0, 1.0 );
// We have to calculate the pixel size in users units to draw the image.
// m_worldUnitLength is a factor used for converting IU to inches
double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
@ -537,7 +539,7 @@ void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap )
cairo_surface_mark_dirty( image );
cairo_set_source_surface( m_currentContext, image, 0, 0 );
cairo_paint( m_currentContext );
cairo_paint_with_alpha( m_currentContext, alphaBlend );
// store the image handle so it can be destroyed later
m_imageSurfaces.push_back( image );

View File

@ -1232,8 +1232,10 @@ void OPENGL_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aContro
}
void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap )
void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
{
GLfloat alpha = std::clamp( alphaBlend, 0.0, 1.0 );
// We have to calculate the pixel size in users units to draw the image.
// m_worldUnitLength is a factor used for converting IU to inches
double scale = 1.0 / ( aBitmap.GetPPI() * m_worldUnitLength );
@ -1259,16 +1261,16 @@ void OPENGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap )
glBindTexture( GL_TEXTURE_2D, texture_id );
glBegin( GL_QUADS );
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glColor4f( 1.0, 1.0, 1.0, alpha );
glTexCoord2f( 0.0, 0.0 );
glVertex3f( v0.x, v0.y, m_layerDepth );
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glColor4f( 1.0, 1.0, 1.0, alpha );
glTexCoord2f( 1.0, 0.0 );
glVertex3f( v1.x, v0.y, m_layerDepth );
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glColor4f( 1.0, 1.0, 1.0, alpha );
glTexCoord2f( 1.0, 1.0 );
glVertex3f( v1.x, v1.y, m_layerDepth );
glColor4f( 1.0, 1.0, 1.0, 1.0 );
glColor4f( 1.0, 1.0, 1.0, alpha );
glTexCoord2f( 0.0, 1.0 );
glVertex3f( v0.x, v1.y, m_layerDepth );
glEnd();

View File

@ -74,6 +74,7 @@ convexhull
copper_line_width
copper_text_dims
courtyard_line_width
data
date
defaults
descr
@ -148,6 +149,7 @@ hide
hole_to_hole_min
host
id
image
island
island_removal_mode
island_area_min

View File

@ -157,6 +157,7 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin
m_params.emplace_back( new PARAM<double>( "board.opacity.vias", &m_ViaOpacity, 1.0 ) );
m_params.emplace_back( new PARAM<double>( "board.opacity.pads", &m_PadOpacity, 1.0 ) );
m_params.emplace_back( new PARAM<double>( "board.opacity.zones", &m_ZoneOpacity, 0.6 ) );
m_params.emplace_back( new PARAM<double>( "board.opacity.background_images", &m_BgImageOpacity, 0.6 ) );
m_params.emplace_back( new PARAM_LIST<wxString>( "board.hidden_nets", &m_HiddenNets, {} ) );

View File

@ -88,6 +88,7 @@ enum KICAD_T
PCB_FOOTPRINT_T, ///< class FOOTPRINT, a footprint
PCB_PAD_T, ///< class PAD, a pad in a footprint
PCB_SHAPE_T, ///< class PCB_SHAPE, a segment not on copper layers
PCB_BITMAP_T, ///< class PCB_BITMAP, bitmap on a layer
PCB_TEXT_T, ///< class PCB_TEXT, text on a layer
PCB_TEXTBOX_T, ///< class PCB_TEXTBOX, wrapped text on a layer
PCB_FP_TEXT_T, ///< class FP_TEXT, text in a footprint
@ -111,7 +112,7 @@ enum KICAD_T
PCB_DIM_ORTHOGONAL_T, ///< class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
PCB_TARGET_T, ///< class PCB_TARGET, a target (graphic item)
PCB_ZONE_T, ///< class ZONE, a copper pour area
PCB_ITEM_LIST_T , ///< class BOARD_ITEM_LIST, a list of board items
PCB_ITEM_LIST_T, ///< class BOARD_ITEM_LIST, a list of board items
PCB_NETINFO_T, ///< class NETINFO_ITEM, a description of a net
PCB_GROUP_T, ///< class PCB_GROUP, a set of BOARD_ITEMs
@ -406,6 +407,7 @@ constexpr bool IsPcbnewType( const KICAD_T aType )
case PCB_FOOTPRINT_T:
case PCB_PAD_T:
case PCB_SHAPE_T:
case PCB_BITMAP_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_FP_TEXT_T:

View File

@ -118,7 +118,7 @@ public:
double aFilterValue = 0.0 ) override;
/// @copydoc GAL::DrawBitmap()
void DrawBitmap( const BITMAP_BASE& aBitmap ) override;
void DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend = 1.0 ) override;
// --------------
// Screen methods

View File

@ -206,7 +206,7 @@ public:
/**
* Draw a bitmap image.
*/
virtual void DrawBitmap( const BITMAP_BASE& aBitmap ) {};
virtual void DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend = 1.0 ) {};
// --------------
// Screen methods

View File

@ -157,7 +157,7 @@ public:
double aFilterValue = 0.0 ) override;
/// @copydoc GAL::DrawBitmap()
void DrawBitmap( const BITMAP_BASE& aBitmap ) override;
void DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend = 1.0 ) override;
/// @copydoc GAL::BitmapText()
void BitmapText( const wxString& aText, const VECTOR2I& aPosition,

View File

@ -40,6 +40,7 @@ public:
m_ViaOpacity = 1.0;
m_PadOpacity = 1.0;
m_ZoneOpacity = 1.0;
m_BgImageOpacity = 1.0;
}
/// @see ZONE_DISPLAY_MODE - stored in the project
@ -57,6 +58,7 @@ public:
double m_ViaOpacity; ///< Opacity override for all types of via
double m_PadOpacity; ///< Opacity override for SMD pads and PTHs
double m_ZoneOpacity; ///< Opacity override for filled zone areas
double m_BgImageOpacity; ///< Opacity override for background images
};
#endif // PCBSTRUCT_H_

View File

@ -123,6 +123,7 @@ public:
double m_ViaOpacity; ///< Opacity override for all types of via
double m_PadOpacity; ///< Opacity override for SMD pads and PTH
double m_ZoneOpacity; ///< Opacity override for filled zones
double m_BgImageOpacity; ///< Opacity override for filled zones
/**
* A list of netnames that have been manually hidden in the board editor.

View File

@ -478,6 +478,7 @@ add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pcbnew_wrap.cxx
DEPENDS python/swig/netinfo.i
DEPENDS python/swig/pad.i
DEPENDS python/swig/pcb_group.i
DEPENDS python/swig/pcb_bitmap.i
DEPENDS python/swig/pcb_text.i
DEPENDS python/swig/pcb_group.i
DEPENDS python/swig/plugins.i

View File

@ -112,6 +112,7 @@ void ARRAY_CREATOR::Invoke()
{
case PCB_FOOTPRINT_T:
case PCB_SHAPE_T:
case PCB_BITMAP_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TRACE_T:

View File

@ -44,6 +44,7 @@
#include <pcb_group.h>
#include <pcb_target.h>
#include <pcb_shape.h>
#include <pcb_bitmap.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pgm_base.h>
@ -307,6 +308,7 @@ void BOARD::Move( const VECTOR2I& aMoveVector ) // overload
// @todo : anything like this elsewhere? maybe put into GENERAL_COLLECTOR class.
static const KICAD_T top_level_board_stuff[] = {
PCB_MARKER_T,
PCB_BITMAP_T,
PCB_TEXT_T,
PCB_TEXTBOX_T,
PCB_SHAPE_T,
@ -739,6 +741,7 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity
case PCB_DIM_ORTHOGONAL_T:
case PCB_DIM_LEADER_T:
case PCB_SHAPE_T:
case PCB_BITMAP_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TARGET_T:
@ -851,6 +854,7 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
case PCB_DIM_ORTHOGONAL_T:
case PCB_DIM_LEADER_T:
case PCB_SHAPE_T:
case PCB_BITMAP_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TARGET_T:
@ -1334,6 +1338,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s
break;
case PCB_SHAPE_T:
case PCB_BITMAP_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_DIM_ALIGNED_T:
@ -1350,6 +1355,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s
switch( stype = *++p )
{
case PCB_SHAPE_T:
case PCB_BITMAP_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_DIM_ALIGNED_T:

View File

@ -341,6 +341,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
// Board items
case PCB_SHAPE_T: // a shape (normally not on copper layers)
case PCB_BITMAP_T: // a bitmap on a user layer
case PCB_TEXT_T: // a text on a layer
case PCB_TEXTBOX_T: // a wrapped text on a layer
case PCB_TRACE_T: // a track segment (segment on a copper layer)

View File

@ -49,6 +49,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = {
// *** all items in a same list (shown here) must be contiguous ****
PCB_MARKER_T, // in m_markers
PCB_TEXT_T, // in m_drawings
PCB_BITMAP_T, // in m_drawings
PCB_TEXTBOX_T, // in m_drawings
PCB_SHAPE_T, // in m_drawings
PCB_DIM_ALIGNED_T, // in m_drawings
@ -72,6 +73,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = {
const KICAD_T GENERAL_COLLECTOR::BoardLevelItems[] = {
PCB_MARKER_T,
PCB_BITMAP_T,
PCB_TEXT_T,
PCB_TEXTBOX_T,
PCB_SHAPE_T,
@ -118,6 +120,7 @@ const KICAD_T GENERAL_COLLECTOR::FootprintItems[] = {
PCB_PAD_T,
PCB_FP_ZONE_T,
PCB_GROUP_T,
PCB_BITMAP_T,
EOT
};

View File

@ -119,6 +119,10 @@ void PCB_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
{
switch( aItem->Type() )
{
case PCB_BITMAP_T:
ShowBitmapPropertiesDialog( aItem);
break;
case PCB_TEXT_T:
case PCB_FP_TEXT_T:
ShowTextPropertiesDialog( aItem );

View File

@ -532,6 +532,7 @@ void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectiv
case PCB_FP_DIM_ORTHOGONAL_T:
case PCB_FP_SHAPE_T:
case PCB_FP_TEXTBOX_T:
case PCB_BITMAP_T:
if( aMode == ADD_MODE::APPEND )
m_drawings.push_back( aBoardItem );
else
@ -786,7 +787,10 @@ const EDA_RECT FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisi
for( BOARD_ITEM* item : m_drawings )
{
if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
// We want the bitmap bounding box just in the footprint editor
// so it will start with the correct initial zoom
if( !isFPEdit
&& ( m_privateLayers.test( item->GetLayer() ) || item->Type() != PCB_BITMAP_T ) )
continue;
if( item->Type() != PCB_FP_TEXT_T )
@ -893,7 +897,7 @@ SHAPE_POLY_SET FOOTPRINT::GetBoundingHull() const
if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
continue;
if( item->Type() != PCB_FP_TEXT_T )
if( item->Type() != PCB_FP_TEXT_T && item->Type() != PCB_BITMAP_T )
{
item->TransformShapeWithClearanceToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
ERROR_OUTSIDE );
@ -1096,9 +1100,12 @@ bool FOOTPRINT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
{
// Text items are selectable on their own, and are therefore excluded from this
// test. TextBox items are NOT selectable on their own, and so MUST be included
// here.
if( item->Type() != PCB_FP_TEXT_T && item->HitTest( arect, false, 0 ) )
// here. Bitmaps aren't selectable since they aren't displayed.
if( item->Type() != PCB_FP_TEXT_T && item->Type() != PCB_FP_TEXT_T
&& item->HitTest( arect, false, 0 ) )
{
return true;
}
}
// Groups are not hit-tested; only their members
@ -1689,6 +1696,7 @@ void FOOTPRINT::SetPosition( const VECTOR2I& aPos )
case PCB_FP_DIM_ORTHOGONAL_T:
case PCB_FP_DIM_RADIAL_T:
case PCB_FP_DIM_LEADER_T:
case PCB_BITMAP_T:
item->Move( delta );
break;

View File

@ -1181,6 +1181,7 @@ void FOOTPRINT_EDIT_FRAME::setupUIConditions()
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawArc );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawPolygon );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawRuleArea );
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeImage );
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeText );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTextBox );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawAlignedDimension );

View File

@ -185,6 +185,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar()
placeMenu->Add( PCB_ACTIONS::drawRectangle );
placeMenu->Add( PCB_ACTIONS::drawCircle );
placeMenu->Add( PCB_ACTIONS::drawPolygon );
placeMenu->Add( PCB_ACTIONS::placeImage );
placeMenu->Add( PCB_ACTIONS::placeText );
placeMenu->Add( PCB_ACTIONS::drawTextBox );

View File

@ -326,6 +326,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
placeMenu->Add( PCB_ACTIONS::drawRectangle );
placeMenu->Add( PCB_ACTIONS::drawCircle );
placeMenu->Add( PCB_ACTIONS::drawPolygon );
placeMenu->Add( PCB_ACTIONS::placeImage );
placeMenu->Add( PCB_ACTIONS::placeText );
placeMenu->Add( PCB_ACTIONS::drawTextBox );

View File

@ -172,6 +172,7 @@ public:
*/
//void SetRotationAngle( EDA_ANGLE aRotationAngle );
void ShowBitmapPropertiesDialog( BOARD_ITEM* aBitmap );
void ShowTextPropertiesDialog( BOARD_ITEM* aText );
int ShowTextBoxPropertiesDialog( BOARD_ITEM* aText );
void ShowGraphicItemPropertiesDialog( BOARD_ITEM* aItem );

211
pcbnew/pcb_bitmap.cpp Normal file
View File

@ -0,0 +1,211 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2011 jean-pierre.charras
* Copyright (C) 2022 Mike Williams
* Copyright (C) 2011-2022 KiCad Developers, see AUTHORS.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
*/
/**
* @file pcb_bitmap.cpp
*/
#include <pcb_draw_panel_gal.h>
#include <plotters/plotter.h>
#include <settings/color_settings.h>
#include <bitmaps.h>
#include <base_units.h>
#include <common.h>
#include <eda_draw_frame.h>
#include <core/mirror.h>
#include <pcb_bitmap.h>
#include <trigo.h>
#include <geometry/shape_rect.h>
#include <convert_to_biu.h>
#include <wx/mstream.h>
PCB_BITMAP::PCB_BITMAP( BOARD_ITEM* aParent, const VECTOR2I& pos ) :
BOARD_ITEM( aParent, PCB_BITMAP_T )
{
m_pos = pos;
m_layer = PCB_LAYER_ID::Dwgs_User; // used only to draw/plot a rectangle,
// when a bitmap cannot be drawn or plotted
m_image = new BITMAP_BASE();
m_image->SetPixelSizeIu( (float) Mils2iu( 1000 ) / m_image->GetPPI() );
}
PCB_BITMAP::PCB_BITMAP( const PCB_BITMAP& aPCBBitmap ) : BOARD_ITEM( aPCBBitmap )
{
m_pos = aPCBBitmap.m_pos;
m_layer = aPCBBitmap.m_layer;
m_image = new BITMAP_BASE( *aPCBBitmap.m_image );
m_image->SetPixelSizeIu( (float) Mils2iu( 1000 ) / m_image->GetPPI() );
}
PCB_BITMAP& PCB_BITMAP::operator=( const BOARD_ITEM& aItem )
{
wxCHECK_MSG( Type() == aItem.Type(), *this,
wxT( "Cannot assign object type " ) + aItem.GetClass() + wxT( " to type " )
+ GetClass() );
if( &aItem != this )
{
BOARD_ITEM::operator=( aItem );
PCB_BITMAP* bitmap = (PCB_BITMAP*) &aItem;
delete m_image;
m_image = new BITMAP_BASE( *bitmap->m_image );
m_pos = bitmap->m_pos;
m_image->SetPixelSizeIu( Mils2iu( 1000 ) / m_image->GetPPI() );
}
return *this;
}
bool PCB_BITMAP::ReadImageFile( const wxString& aFullFilename )
{
return m_image->ReadImageFile( aFullFilename );
}
EDA_ITEM* PCB_BITMAP::Clone() const
{
return new PCB_BITMAP( *this );
}
void PCB_BITMAP::SwapData( BOARD_ITEM* aItem )
{
wxCHECK_RET( aItem->Type() == PCB_BITMAP_T,
wxString::Format( wxT( "PCB_BITMAP object cannot swap data with %s object." ),
aItem->GetClass() ) );
PCB_BITMAP* item = (PCB_BITMAP*) aItem;
std::swap( m_pos, item->m_pos );
std::swap( m_image, item->m_image );
}
const EDA_RECT PCB_BITMAP::GetBoundingBox() const
{
// Bitmaps are center origin, EDA_RECTs need top-left origin
VECTOR2I size = m_image->GetSize();
VECTOR2I topLeft = { m_pos.x - size.x / 2, m_pos.y - size.y / 2 };
return EDA_RECT( topLeft, size );
}
std::shared_ptr<SHAPE> PCB_BITMAP::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
{
EDA_RECT box = GetBoundingBox();
return std::shared_ptr<SHAPE_RECT>(
new SHAPE_RECT( box.GetCenter(), box.GetWidth(), box.GetHeight() ) );
}
const VECTOR2I PCB_BITMAP::GetSize() const
{
return m_image->GetSize();
}
void PCB_BITMAP::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
{
if( aFlipLeftRight )
{
MIRROR( m_pos.x, aCentre.x );
m_image->Mirror( false );
}
else
{
MIRROR( m_pos.y, aCentre.y );
m_image->Mirror( true );
}
}
void PCB_BITMAP::Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle )
{
RotatePoint( m_pos, aCenter, aAngle );
m_image->Rotate( false );
}
#if defined( DEBUG )
void PCB_BITMAP::Show( int nestLevel, std::ostream& os ) const
{
// XML output:
wxString s = GetClass();
NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str() << m_pos << "/>\n";
}
#endif
bool PCB_BITMAP::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
{
EDA_RECT rect = GetBoundingBox();
rect.Inflate( aAccuracy );
return rect.Contains( aPosition );
}
bool PCB_BITMAP::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
{
EDA_RECT rect = aRect;
rect.Inflate( aAccuracy );
if( aContained )
return rect.Contains( GetBoundingBox() );
return rect.Intersects( GetBoundingBox() );
}
BITMAPS PCB_BITMAP::GetMenuImage() const
{
return BITMAPS::image;
}
void PCB_BITMAP::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
aList.emplace_back( _( "Bitmap" ), wxEmptyString );
aList.emplace_back( _( "Width" ), MessageTextFromValue( aFrame->GetUserUnits(), GetSize().x ) );
aList.emplace_back( _( "Height" ),
MessageTextFromValue( aFrame->GetUserUnits(), GetSize().y ) );
}
void PCB_BITMAP::ViewGetLayers( int aLayers[], int& aCount ) const
{
aCount = 1;
aLayers[0] = LAYER_DRAW_BITMAPS;
}

133
pcbnew/pcb_bitmap.h Normal file
View File

@ -0,0 +1,133 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2011 jean-pierre.charras
* Copyright (C) 2022 Mike Williams
* Copyright (C) 2011-2022 KiCad Developers, see AUTHORS.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
*/
/**
* @file pcb_bitmap.h
*/
#ifndef _PCB_BITMAP_H_
#define _PCB_BITMAP_H_
#include <board_item.h>
#include <bitmap_base.h>
/**
* Object to handle a bitmap image that can be inserted in a PCB.
*/
class PCB_BITMAP : public BOARD_ITEM
{
public:
PCB_BITMAP( BOARD_ITEM* aParent, const VECTOR2I& pos = VECTOR2I( 0, 0 ) );
PCB_BITMAP( const PCB_BITMAP& aPcbBitmap );
~PCB_BITMAP() { delete m_image; }
PCB_BITMAP& operator=( const BOARD_ITEM& aItem );
BITMAP_BASE* GetImage() const
{
wxCHECK_MSG( m_image != nullptr, nullptr, "Invalid PCB_BITMAP init, m_image is NULL." );
return m_image;
}
/**
* @return the image "zoom" value.
* scale = 1.0 = original size of bitmap.
* scale < 1.0 = the bitmap is drawn smaller than its original size.
* scale > 1.0 = the bitmap is drawn bigger than its original size.
*/
double GetImageScale() const { return m_image->GetScale(); }
void SetImageScale( double aScale ) { m_image->SetScale( aScale ); }
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && PCB_BITMAP_T == aItem->Type();
}
wxString GetClass() const override { return wxT( "PCB_BITMAP" ); }
/**
* @return the actual size (in user units, not in pixels) of the image.
*/
const VECTOR2I GetSize() const;
const EDA_RECT GetBoundingBox() const override;
std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER,
FLASHING aFlash = FLASHING::DEFAULT ) const override;
void SwapData( BOARD_ITEM* aItem ) override;
//void Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset ) override;
/// @copydoc VIEW_ITEM::ViewGetLayers()
virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
/**
* Read and store an image file.
*
* Initialize the bitmap used to draw this item format.
*
* @param aFullFilename is the full filename of the image file to read.
* @return true if success reading else false.
*/
bool ReadImageFile( const wxString& aFullFilename );
void Move( const VECTOR2I& aMoveVector ) override { m_pos += aMoveVector; }
void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
void Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle ) override;
wxString GetSelectMenuText( EDA_UNITS aUnits ) const override
{
return wxString( _( "Image" ) );
}
BITMAPS GetMenuImage() const override;
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
VECTOR2I GetPosition() const override { return m_pos; }
void SetPosition( const VECTOR2I& aPosition ) override { m_pos = aPosition; }
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override;
EDA_ITEM* Clone() const override;
#if defined( DEBUG )
void Show( int nestLevel, std::ostream& os ) const override;
#endif
private:
VECTOR2I m_pos; // XY coordinates of center of the bitmap
BITMAP_BASE* m_image; // the BITMAP_BASE item
};
#endif // _PCB_BITMAP_H_

View File

@ -560,6 +560,8 @@ void PCB_DRAW_PANEL_GAL::setDefaultLayerDeps()
}
}
m_view->SetLayerTarget( LAYER_DRAW_BITMAPS, KIGFX::TARGET_NONCACHED );
m_view->SetLayerTarget( LAYER_ANCHOR, KIGFX::TARGET_NONCACHED );
m_view->SetLayerDisplayOnly( LAYER_ANCHOR );

View File

@ -828,6 +828,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawCircle );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawArc );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawPolygon );
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeImage );
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeText );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTextBox );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawAlignedDimension );

View File

@ -35,6 +35,7 @@
#include <pcb_shape.h>
#include <string_utils.h>
#include <zone.h>
#include <pcb_bitmap.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_marker.h>
@ -72,10 +73,11 @@ PCB_RENDER_SETTINGS::PCB_RENDER_SETTINGS()
m_netColorMode = NET_COLOR_MODE::RATSNEST;
m_ContrastModeDisplay = HIGH_CONTRAST_MODE::NORMAL;
m_trackOpacity = 1.0;
m_viaOpacity = 1.0;
m_padOpacity = 1.0;
m_zoneOpacity = 1.0;
m_trackOpacity = 1.0;
m_viaOpacity = 1.0;
m_padOpacity = 1.0;
m_zoneOpacity = 1.0;
m_bgImageOpacity = 1.0;
m_ForcePadSketchModeOff = false;
m_ForcePadSketchModeOn = false;
@ -143,10 +145,11 @@ void PCB_RENDER_SETTINGS::LoadDisplayOptions( const PCB_DISPLAY_OPTIONS& aOption
m_ContrastModeDisplay = aOptions.m_ContrastModeDisplay;
m_netColorMode = aOptions.m_NetColorMode;
m_trackOpacity = aOptions.m_TrackOpacity;
m_viaOpacity = aOptions.m_ViaOpacity;
m_padOpacity = aOptions.m_PadOpacity;
m_zoneOpacity = aOptions.m_ZoneOpacity;
m_trackOpacity = aOptions.m_TrackOpacity;
m_viaOpacity = aOptions.m_ViaOpacity;
m_padOpacity = aOptions.m_PadOpacity;
m_zoneOpacity = aOptions.m_ZoneOpacity;
m_bgImageOpacity = aOptions.m_BgImageOpacity;
}
@ -341,6 +344,8 @@ COLOR4D PCB_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) cons
color.a *= m_padOpacity;
else if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
color.a *= m_zoneOpacity;
else if( item->Type() == PCB_BITMAP_T )
color.a *= m_bgImageOpacity;
// No special modifiers enabled
return color;
@ -414,7 +419,10 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
{
FOOTPRINT* parentFP = static_cast<FOOTPRINT*>( item->GetParentFootprint() );
if( item->GetLayerSet().count() > 1 )
// Never draw footprint bitmaps on board
if( item->Type() == PCB_BITMAP_T )
return false;
else if( item->GetLayerSet().count() > 1 )
{
// For multi-layer objects, exclude only those layers that are private
if( IsPcbLayer( aLayer ) && parentFP->GetPrivateLayers().test( aLayer ) )
@ -461,6 +469,10 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
draw( static_cast<const PCB_SHAPE*>( item ), aLayer );
break;
case PCB_BITMAP_T:
draw( static_cast<const PCB_BITMAP*>( item ), aLayer );
break;
case PCB_TEXT_T:
draw( static_cast<const PCB_TEXT*>( item ), aLayer );
break;
@ -1633,6 +1645,45 @@ void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2I& aPosition,
}
void PCB_PAINTER::draw( const PCB_BITMAP* aBitmap, int aLayer )
{
m_gal->Save();
m_gal->Translate( aBitmap->GetPosition() );
// When the image scale factor is not 1.0, we need to modify the actual as the image scale
// factor is similar to a local zoom
double img_scale = aBitmap->GetImageScale();
if( img_scale != 1.0 )
m_gal->Scale( VECTOR2D( img_scale, img_scale ) );
m_gal->DrawBitmap( *aBitmap->GetImage(), m_pcbSettings.m_bgImageOpacity );
if( aBitmap->IsSelected() || aBitmap->IsBrightened() )
{
COLOR4D color = m_pcbSettings.GetColor( aBitmap, LAYER_ANCHOR );
m_gal->SetIsStroke( true );
m_gal->SetStrokeColor( color );
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth * 2.0f );
m_gal->SetIsFill( false );
// Draws a bounding box.
VECTOR2D bm_size( aBitmap->GetSize() );
// bm_size is the actual image size in UI.
// but m_gal scale was previously set to img_scale
// so recalculate size relative to this image size.
bm_size.x /= img_scale;
bm_size.y /= img_scale;
VECTOR2D origin( -bm_size.x / 2.0, -bm_size.y / 2.0 );
VECTOR2D end = origin + bm_size;
m_gal->DrawRectangle( origin, end );
}
m_gal->Restore();
}
void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
{
wxString resolvedText( aText->GetShownText() );

View File

@ -46,6 +46,7 @@ class PCB_SHAPE;
class PCB_GROUP;
class FOOTPRINT;
class ZONE;
class PCB_BITMAP;
class PCB_TEXT;
class PCB_TEXTBOX;
class FP_TEXT;
@ -142,6 +143,7 @@ protected:
double m_viaOpacity; ///< Opacity override for all types of via
double m_padOpacity; ///< Opacity override for SMD pads and PTHs
double m_zoneOpacity; ///< Opacity override for filled zones
double m_bgImageOpacity; ///< Opacity override for background images
};
@ -169,6 +171,7 @@ protected:
void draw( const PCB_VIA* aVia, int aLayer );
void draw( const PAD* aPad, int aLayer );
void draw( const PCB_SHAPE* aSegment, int aLayer );
void draw( const PCB_BITMAP* aBitmap, int aLayer );
void draw( const PCB_TEXT* aText, int aLayer );
void draw( const PCB_TEXTBOX* aText, int aLayer );
void draw( const FP_TEXT* aText, int aLayer );

View File

@ -106,6 +106,7 @@ bool PCB_EDIT_FRAME::LoadProjectSettings()
opts.m_PadOpacity = localSettings.m_PadOpacity;
opts.m_ZoneOpacity = localSettings.m_ZoneOpacity;
opts.m_ZoneDisplayMode = localSettings.m_ZoneDisplayMode;
opts.m_BgImageOpacity = localSettings.m_BgImageOpacity;
// No refresh here: callers of LoadProjectSettings refresh later
SetDisplayOptions( opts, false );
@ -159,6 +160,7 @@ void PCB_EDIT_FRAME::SaveProjectSettings()
localSettings.m_PadOpacity = displayOpts.m_PadOpacity;
localSettings.m_ZoneOpacity = displayOpts.m_ZoneOpacity;
localSettings.m_ZoneDisplayMode = displayOpts.m_ZoneDisplayMode;
localSettings.m_BgImageOpacity = displayOpts.m_BgImageOpacity;
// Save Design settings
const BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();

View File

@ -39,6 +39,7 @@
#include <fp_textbox.h>
#include <pcb_dimension.h>
#include <pcb_shape.h>
#include <pcb_bitmap.h>
#include <pcb_group.h>
#include <pcb_target.h>
#include <pcb_track.h>
@ -63,6 +64,12 @@
#include <progress_reporter.h>
#include <board_stackup_manager/stackup_predefined_prms.h>
// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
// base64 code. Needed for PCB_BITMAP
#define wxUSE_BASE64 1
#include <wx/base64.h>
#include <wx/mstream.h>
using namespace PCB_KEYS_T;
@ -851,6 +858,12 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
bulkAddedItems.push_back( item );
break;
case T_image:
item = parsePCB_BITMAP( m_board );
m_board->Add( item, ADD_MODE::BULK_APPEND, true );
bulkAddedItems.push_back( item );
break;
case T_gr_text:
item = parsePCB_TEXT();
m_board->Add( item, ADD_MODE::BULK_APPEND, true );
@ -2836,6 +2849,80 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE()
}
PCB_BITMAP* PCB_PARSER::parsePCB_BITMAP( BOARD_ITEM* aParent )
{
wxCHECK_MSG( CurTok() == T_image, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as an image." ) );
T token;
std::unique_ptr<PCB_BITMAP> bitmap = std::make_unique<PCB_BITMAP>( aParent );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_at:
{
VECTOR2I pos;
pos.x = parseBoardUnits( "X coordinate" );
pos.y = parseBoardUnits( "Y coordinate" );
bitmap->SetPosition( pos );
NeedRIGHT();
break;
}
case T_scale:
bitmap->GetImage()->SetScale( parseDouble( "image scale factor" ) );
if( !std::isnormal( bitmap->GetImage()->GetScale() ) )
bitmap->GetImage()->SetScale( 1.0 );
NeedRIGHT();
break;
case T_data:
{
token = NextTok();
wxString data;
// Reserve 128K because most image files are going to be larger than the default
// 1K that wxString reserves.
data.reserve( 1 << 17 );
while( token != T_RIGHT )
{
if( !IsSymbol( token ) )
Expecting( "base64 image data" );
data += FromUTF8();
token = NextTok();
}
wxMemoryBuffer buffer = wxBase64Decode( data );
wxMemoryOutputStream stream( buffer.GetData(), buffer.GetBufSize() );
wxImage* image = new wxImage();
wxMemoryInputStream istream( stream );
image->LoadFile( istream, wxBITMAP_TYPE_PNG );
bitmap->GetImage()->SetImage( image );
bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
break;
}
default:
Expecting( "at, scale, data" );
}
}
return bitmap.release();
}
PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
{
wxCHECK_MSG( CurTok() == T_gr_text, nullptr,
@ -3784,6 +3871,13 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
break;
}
case T_image:
{
PCB_BITMAP* image = parsePCB_BITMAP( footprint.get() );
footprint->Add( image, ADD_MODE::APPEND, true );
break;
}
case T_dimension:
{
PCB_DIMENSION_BASE* dimension = parseDIMENSION( footprint.get(), true );

View File

@ -49,6 +49,7 @@ class PAD;
class BOARD_DESIGN_SETTINGS;
class PCB_DIMENSION_BASE;
class PCB_SHAPE;
class PCB_BITMAP;
class EDA_TEXT;
class FP_SHAPE;
class FP_TEXT;
@ -176,6 +177,7 @@ private:
PCB_SHAPE* parsePCB_SHAPE();
PCB_TEXT* parsePCB_TEXT();
PCB_BITMAP* parsePCB_BITMAP( BOARD_ITEM* aParent );
PCB_TEXTBOX* parsePCB_TEXTBOX();
PCB_DIMENSION_BASE* parseDIMENSION( BOARD_ITEM* aParent, bool aInFP );

View File

@ -41,6 +41,7 @@
#include <pad.h>
#include <pcb_group.h>
#include <pcb_shape.h>
#include <pcb_bitmap.h>
#include <pcb_target.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
@ -57,6 +58,13 @@
#include <zone.h>
#include <zones.h>
// For some reason wxWidgets is built with wxUSE_BASE64 unset so expose the wxWidgets
// base64 code. Needed for PCB_BITMAP
#define wxUSE_BASE64 1
#include <wx/base64.h>
#include <wx/mstream.h>
using namespace PCB_KEYS_T;
@ -418,6 +426,10 @@ void PCB_PLUGIN::Format( const BOARD_ITEM* aItem, int aNestLevel ) const
format( static_cast<const PCB_SHAPE*>( aItem ), aNestLevel );
break;
case PCB_BITMAP_T:
format( static_cast<const PCB_BITMAP*>( aItem ), aNestLevel );
break;
case PCB_FP_SHAPE_T:
format( static_cast<const FP_SHAPE*>( aItem ), aNestLevel );
break;
@ -996,6 +1008,52 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
}
void PCB_PLUGIN::format( const PCB_BITMAP* aBitmap, int aNestLevel ) const
{
wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" );
const wxImage* image = aBitmap->GetImage()->GetImageData();
wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
m_out->Print( aNestLevel, "(image (at %s %s)",
FormatInternalUnits( aBitmap->GetPosition().x ).c_str(),
FormatInternalUnits( aBitmap->GetPosition().y ).c_str() );
if( aBitmap->GetImage()->GetScale() != 1.0 )
m_out->Print( 0, " (scale %g)", aBitmap->GetImage()->GetScale() );
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 1, "(data" );
wxMemoryOutputStream stream;
image->SaveFile( stream, wxBITMAP_TYPE_PNG );
// Write binary data in hexadecimal form (ASCII)
wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
wxString out = wxBase64Encode( buffer->GetBufferStart(), buffer->GetBufferSize() );
// Apparently the MIME standard character width for base64 encoding is 76 (unconfirmed)
// so use it in a vein attempt to be standard like.
#define MIME_BASE64_LENGTH 76
size_t first = 0;
while( first < out.Length() )
{
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 2, "%s", TO_UTF8( out( first, MIME_BASE64_LENGTH ) ) );
first += MIME_BASE64_LENGTH;
}
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 1, ")\n" ); // Closes data token.
m_out->Print( aNestLevel, ")\n" ); // Closes image token.
}
void PCB_PLUGIN::format( const FP_SHAPE* aFPShape, int aNestLevel ) const
{
std::string locked = aFPShape->IsLocked() ? " locked" : "";

View File

@ -39,6 +39,7 @@ class BOARD_DESIGN_SETTINGS;
class PCB_DIMENSION_BASE;
class FP_SHAPE;
class PCB_SHAPE;
class PCB_BITMAP;
class PCB_TARGET;
class PAD;
class FP_TEXT;
@ -122,7 +123,8 @@ class SHAPE_LINE_CHAIN;
//#define SEXPR_BOARD_FILE_VERSION 20220331 // Plot on all layers selection setting
//#define SEXPR_BOARD_FILE_VERSION 20220417 // Automatic dimension precisions
//#define SEXPR_BOARD_FILE_VERSION 20220427 // Exclude Edge.Cuts & Margin from fp private layers
#define SEXPR_BOARD_FILE_VERSION 20220609 // Add teardrop keywords to identify teardrop zones
//#define SEXPR_BOARD_FILE_VERSION 20220609 // Add teardrop keywords to identify teardrop zones
#define SEXPR_BOARD_FILE_VERSION 20220621 // Add Image support
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting
@ -281,6 +283,8 @@ private:
void format( const FP_SHAPE* aFPShape, int aNestLevel = 0 ) const;
void format( const PCB_BITMAP* aBitmap, int aNestLevel = 0 ) const;
void format( const PCB_GROUP* aGroup, int aNestLevel = 0 ) const;
void format( const PCB_SHAPE* aSegment, int aNestLevel = 0 ) const;

View File

@ -0,0 +1,7 @@
%include pcb_bitmap.h
%{
#include <pcb_bitmap.h>
%}

View File

@ -185,6 +185,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
m_drawToolBar->Add( PCB_ACTIONS::drawRectangle, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawCircle, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawPolygon, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::placeImage, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::placeText, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawTextBox, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->AddGroup( dimensionGroup, ACTION_TOOLBAR::TOGGLE );

View File

@ -458,6 +458,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
m_drawToolBar->Add( PCB_ACTIONS::drawRectangle, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawCircle, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawPolygon, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::placeImage, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::placeText, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawTextBox, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->AddGroup( dimensionGroup, ACTION_TOOLBAR::TOGGLE );

View File

@ -48,6 +48,7 @@
#include <view/view.h>
#include <widgets/appearance_controls.h>
#include <widgets/infobar.h>
#include <wx/filedlg.h>
#include <bitmaps.h>
#include <board.h>
@ -61,6 +62,7 @@
#include <painter.h>
#include <pcb_edit_frame.h>
#include <pcb_group.h>
#include <pcb_bitmap.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_dimension.h>
@ -524,6 +526,237 @@ int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
}
int DRAWING_TOOL::PlaceImage( const TOOL_EVENT& aEvent )
{
if( m_inDrawingTool )
return 0;
REENTRANCY_GUARD guard( &m_inDrawingTool );
PCB_BITMAP* image = aEvent.Parameter<PCB_BITMAP*>();
bool immediateMode = image != nullptr;
PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
bool ignorePrimePosition = false;
COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
VECTOR2I cursorPos = getViewControls()->GetCursorPosition();
auto selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
BOARD_COMMIT commit( m_frame );
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
// Add all the drawable symbols to preview
if( image )
{
image->SetPosition( cursorPos );
m_view->ClearPreview();
m_view->AddToPreview( image->Clone() );
}
std::string tool = aEvent.GetCommandStr().get();
m_frame->PushTool( tool );
auto setCursor =
[&]()
{
if( image )
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
else
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
};
auto cleanup =
[&] ()
{
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
m_view->ClearPreview();
delete image;
image = nullptr;
};
Activate();
// Must be done after Activate() so that it gets set into the correct context
getViewControls()->ShowCursor( true );
// Set initial cursor
setCursor();
// Prime the pump
if( image )
{
m_toolMgr->RunAction( ACTIONS::refreshPreview );
}
else if( aEvent.HasPosition() )
{
m_toolMgr->PrimeTool( aEvent.Position() );
}
else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
{
m_toolMgr->PrimeTool( { 0, 0 } );
ignorePrimePosition = true;
}
// Main loop: keep receiving events
while( TOOL_EVENT* evt = Wait() )
{
setCursor();
cursorPos = getViewControls()->GetCursorPosition( !evt->DisableGridSnapping() );
if( evt->IsCancelInteractive() )
{
if( image )
{
cleanup();
}
else
{
m_frame->PopTool( tool );
break;
}
if( immediateMode )
{
m_frame->PopTool( tool );
break;
}
}
else if( evt->IsActivate() )
{
if( image && evt->IsMoveTool() )
{
// We're already moving our own item; ignore the move tool
evt->SetPassEvent( false );
continue;
}
if( image )
{
m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
evt->SetPassEvent( false );
continue;
}
if( evt->IsMoveTool() )
{
// Leave ourselves on the stack so we come back after the move
break;
}
else
{
m_frame->PopTool( tool );
break;
}
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
{
if( !image )
{
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
_( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(),
wxFD_OPEN );
if( dlg.ShowModal() != wxID_OK )
continue;
// If we started with a hotkey which has a position then warp back to that.
// Otherwise update to the current mouse position pinned inside the autoscroll
// boundaries.
if( evt->IsPrime() && !ignorePrimePosition )
{
cursorPos = grid.Align( evt->Position() );
getViewControls()->WarpMouseCursor( cursorPos, true );
}
else
{
getViewControls()->PinCursorInsideNonAutoscrollArea( true );
cursorPos = getViewControls()->GetMousePosition();
}
cursorPos = getViewControls()->GetMousePosition( true );
wxString fullFilename = dlg.GetPath();
if( wxFileExists( fullFilename ) )
image = new PCB_BITMAP( m_frame->GetModel(), cursorPos );
if( !image || !image->ReadImageFile( fullFilename ) )
{
wxMessageBox( _( "Could not load image from '%s'." ), fullFilename );
delete image;
image = nullptr;
continue;
}
image->SetFlags( IS_NEW | IS_MOVING );
image->SetLayer( m_frame->GetActiveLayer() );
m_view->ClearPreview();
m_view->AddToPreview( image->Clone() );
m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
selectionTool->AddItemToSel( image, false );
getViewControls()->SetCursorPosition( cursorPos, false );
setCursor();
m_view->ShowPreview( true );
}
else
{
commit.Add( image );
commit.Push( _( "Place an image" ) );
m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, image );
image = nullptr;
m_toolMgr->RunAction( ACTIONS::activatePointEditor );
m_view->ClearPreview();
if( immediateMode )
{
m_frame->PopTool( tool );
break;
}
}
}
else if( evt->IsClick( BUT_RIGHT ) )
{
// Warp after context menu only if dragging...
if( !image )
m_toolMgr->VetoContextMenuMouseWarp();
m_menu.ShowContextMenu( selectionTool->GetSelection() );
}
else if( image && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
{
image->SetPosition( cursorPos );
m_view->ClearPreview();
m_view->AddToPreview( image->Clone() );
m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
}
else if( image && evt->IsAction( &ACTIONS::doDelete ) )
{
cleanup();
}
else
{
evt->SetPassEvent();
}
// Enable autopanning and cursor capture only when there is an image to be placed
getViewControls()->SetAutoPan( image != nullptr );
getViewControls()->CaptureCursor( image != nullptr );
}
getViewControls()->SetAutoPan( false );
getViewControls()->CaptureCursor( false );
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
return 0;
}
int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
{
if( m_isFootprintEditor && !m_frame->GetModel() )
@ -3004,6 +3237,7 @@ void DRAWING_TOOL::setTransitions()
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZoneCutout.MakeEvent() );
Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawSimilarZone.MakeEvent() );
Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
Go( &DRAWING_TOOL::PlaceImage, PCB_ACTIONS::placeImage.MakeEvent() );
Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawTextBox.MakeEvent() );
Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );

View File

@ -67,6 +67,7 @@ public:
RECTANGLE,
CIRCLE,
ARC,
IMAGE,
TEXT,
ANCHOR,
DXF,
@ -135,6 +136,12 @@ public:
*/
int DrawArc( const TOOL_EVENT& aEvent );
/**
* Display a dialog that allows one to select and image then decide where to place the
* image in the editor.
*/
int PlaceImage( const TOOL_EVENT& aEvent );
/**
* Display a dialog that allows one to input text and its settings and then lets the user
* decide where to place the text in editor.

View File

@ -1757,6 +1757,7 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
case PCB_FOOTPRINT_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_BITMAP_T:
case PCB_SHAPE_T:
case PCB_TRACE_T:
case PCB_ARC_T:

View File

@ -117,6 +117,11 @@ TOOL_ACTION PCB_ACTIONS::placeStackup( "pcbnew.InteractiveDrawing.placeStackup",
_( "Add a board stackup table on a graphic layer" ),
BITMAPS::INVALID_BITMAP, AF_ACTIVATE );
TOOL_ACTION PCB_ACTIONS::placeImage( "pcbnew.InteractiveDrawing.placeImage",
AS_GLOBAL, 0, "",
_( "Add Image" ), _( "Add bitmap image" ),
BITMAPS::image, AF_ACTIVATE );
TOOL_ACTION PCB_ACTIONS::placeText( "pcbnew.InteractiveDrawing.text",
AS_GLOBAL,
MD_SHIFT + MD_CTRL + 'T', LEGACY_HK_NAME( "Add Text" ),

View File

@ -146,6 +146,7 @@ public:
static TOOL_ACTION drawRectangle;
static TOOL_ACTION drawCircle;
static TOOL_ACTION drawArc;
static TOOL_ACTION placeImage;
static TOOL_ACTION placeText;
static TOOL_ACTION drawTextBox;
static TOOL_ACTION drawAlignedDimension;

View File

@ -41,6 +41,7 @@ using namespace std::placeholders;
#include <pcb_edit_frame.h>
#include <fp_shape.h>
#include <fp_textbox.h>
#include <pcb_bitmap.h>
#include <pcb_dimension.h>
#include <pcb_textbox.h>
#include <pad.h>
@ -191,6 +192,20 @@ std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
// Generate list of edit points basing on the item type
switch( aItem->Type() )
{
case PCB_BITMAP_T:
{
PCB_BITMAP* bitmap = (PCB_BITMAP*) aItem;
VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
points->AddPoint( topLeft );
points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
points->AddPoint( botRight );
points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
break;
}
case PCB_TEXTBOX_T:
case PCB_FP_TEXTBOX_T:
case PCB_SHAPE_T:
@ -1076,6 +1091,29 @@ void PCB_POINT_EDITOR::updateItem() const
switch( item->Type() )
{
case PCB_BITMAP_T:
{
PCB_BITMAP* bitmap = (PCB_BITMAP*) item;
VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition();
VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition();
VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition();
VECTOR2I botRight = m_editPoints->Point( RECT_BOT_RIGHT ).GetPosition();
pinEditedCorner( topLeft, topRight, botLeft, botRight );
double oldWidth = bitmap->GetSize().x;
double newWidth = std::max( topRight.x - topLeft.x, Mils2iu( 50 ) );
double widthRatio = newWidth / oldWidth;
double oldHeight = bitmap->GetSize().y;
double newHeight = std::max( botLeft.y - topLeft.y, Mils2iu( 50 ) );
double heightRatio = newHeight / oldHeight;
bitmap->SetImageScale( bitmap->GetImageScale() * std::min( widthRatio, heightRatio ) );
break;
}
case PCB_TEXTBOX_T:
case PCB_FP_TEXTBOX_T:
case PCB_SHAPE_T:
@ -1646,6 +1684,20 @@ void PCB_POINT_EDITOR::updatePoints()
switch( item->Type() )
{
case PCB_BITMAP_T:
{
PCB_BITMAP* bitmap = (PCB_BITMAP*) item;
VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2;
VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2;
m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft );
m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
m_editPoints->Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight );
break;
}
case PCB_TEXTBOX_T:
case PCB_FP_TEXTBOX_T:
{

View File

@ -33,6 +33,7 @@ using namespace std::placeholders;
#include <board_design_settings.h>
#include <board_item.h>
#include <clipper.hpp>
#include <pcb_bitmap.h>
#include <pcb_track.h>
#include <footprint.h>
#include <pad.h>
@ -2720,6 +2721,11 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
// segments underneath them -- so artificially reduce their size from πr² to 1.5r².
area = SEG::Square( static_cast<PCB_VIA*>( item )->GetDrill() / 2 ) * 1.5;
}
else if( item->Type() == PCB_BITMAP_T )
{
VECTOR2I size = static_cast<const PCB_BITMAP*>( item )->GetSize();
area = size.x * size.y;
}
else
{
try

View File

@ -336,6 +336,7 @@ const APPEARANCE_CONTROLS::APPEARANCE_SETTING APPEARANCE_CONTROLS::s_objectSetti
RR( _HKI( "Vias" ), LAYER_VIAS, _HKI( "Show all vias" ), true ),
RR( _HKI( "Pads" ), LAYER_PADS, _HKI( "Show all pads" ), true ),
RR( _HKI( "Zones" ), LAYER_ZONES, _HKI( "Show copper zones" ), true ),
RR( _HKI( "Background Images" ), LAYER_DRAW_BITMAPS, _HKI( "Show user background images" ), true ),
RR(),
RR( _HKI( "Footprints Front" ), LAYER_MOD_FR, _HKI( "Show footprints that are on board's front" ) ),
RR( _HKI( "Footprints Back" ), LAYER_MOD_BK, _HKI( "Show footprints that are on board's back" ) ),
@ -371,6 +372,7 @@ static std::set<int> s_allowedInFpEditor =
LAYER_MOD_REFERENCES,
LAYER_MOD_TEXT,
LAYER_MOD_TEXT_INVISIBLE,
LAYER_DRAW_BITMAPS,
LAYER_GRID
};
@ -2204,12 +2206,14 @@ void APPEARANCE_CONTROLS::syncObjectSettings()
wxASSERT( m_objectSettingsMap.count( LAYER_TRACKS )
&& m_objectSettingsMap.count( LAYER_VIAS )
&& m_objectSettingsMap.count( LAYER_PADS )
&& m_objectSettingsMap.count( LAYER_ZONES ) );
&& m_objectSettingsMap.count( LAYER_ZONES )
&& m_objectSettingsMap.count( LAYER_DRAW_BITMAPS ) );
m_objectSettingsMap[LAYER_TRACKS]->ctl_opacity->SetValue( opts.m_TrackOpacity * 100 );
m_objectSettingsMap[LAYER_VIAS]->ctl_opacity->SetValue( opts.m_ViaOpacity * 100 );
m_objectSettingsMap[LAYER_PADS]->ctl_opacity->SetValue( opts.m_PadOpacity * 100 );
m_objectSettingsMap[LAYER_ZONES]->ctl_opacity->SetValue( opts.m_ZoneOpacity * 100 );
m_objectSettingsMap[LAYER_DRAW_BITMAPS]->ctl_opacity->SetValue( opts.m_BgImageOpacity * 100 );
}
@ -2838,10 +2842,11 @@ void APPEARANCE_CONTROLS::onObjectOpacitySlider( int aLayer, float aOpacity )
switch( aLayer )
{
case static_cast<int>( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break;
case static_cast<int>( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break;
case static_cast<int>( LAYER_PADS ): options.m_PadOpacity = aOpacity; break;
case static_cast<int>( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break;
case static_cast<int>( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break;
case static_cast<int>( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break;
case static_cast<int>( LAYER_PADS ): options.m_PadOpacity = aOpacity; break;
case static_cast<int>( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break;
case static_cast<int>( LAYER_DRAW_BITMAPS ): options.m_BgImageOpacity = aOpacity; break;
default: return;
}