From 3669cb46739cb5af2c73d7801c1c18e7a514bbee Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Tue, 8 Feb 2022 14:29:54 -0500 Subject: [PATCH] PCB Editor: Add User Background Images --- common/CMakeLists.txt | 1 + common/dialogs/dialog_image_editor.cpp | 25 +++ common/eda_item.cpp | 1 + common/gal/cairo/cairo_gal.cpp | 6 +- common/gal/opengl/opengl_gal.cpp | 12 +- common/pcb.keywords | 2 + common/project/project_local_settings.cpp | 1 + include/core/typeinfo.h | 4 +- include/gal/cairo/cairo_gal.h | 2 +- include/gal/graphics_abstraction_layer.h | 2 +- include/gal/opengl/opengl_gal.h | 2 +- include/pcb_display_options.h | 2 + include/project/project_local_settings.h | 1 + pcbnew/CMakeLists.txt | 1 + pcbnew/array_creator.cpp | 1 + pcbnew/board.cpp | 6 + pcbnew/board_commit.cpp | 1 + pcbnew/collectors.cpp | 3 + pcbnew/edit.cpp | 4 + pcbnew/footprint.cpp | 16 +- pcbnew/footprint_edit_frame.cpp | 1 + pcbnew/menubar_footprint_editor.cpp | 1 + pcbnew/menubar_pcb_editor.cpp | 1 + pcbnew/pcb_base_edit_frame.h | 1 + pcbnew/pcb_bitmap.cpp | 211 +++++++++++++++++++ pcbnew/pcb_bitmap.h | 133 ++++++++++++ pcbnew/pcb_draw_panel_gal.cpp | 2 + pcbnew/pcb_edit_frame.cpp | 1 + pcbnew/pcb_painter.cpp | 69 ++++++- pcbnew/pcb_painter.h | 3 + pcbnew/pcbnew_config.cpp | 2 + pcbnew/plugins/kicad/pcb_parser.cpp | 94 +++++++++ pcbnew/plugins/kicad/pcb_parser.h | 2 + pcbnew/plugins/kicad/pcb_plugin.cpp | 58 ++++++ pcbnew/plugins/kicad/pcb_plugin.h | 6 +- pcbnew/python/swig/pcb_bitmap.i | 7 + pcbnew/toolbars_footprint_editor.cpp | 1 + pcbnew/toolbars_pcb_editor.cpp | 1 + pcbnew/tools/drawing_tool.cpp | 234 ++++++++++++++++++++++ pcbnew/tools/drawing_tool.h | 7 + pcbnew/tools/edit_tool.cpp | 1 + pcbnew/tools/pcb_actions.cpp | 5 + pcbnew/tools/pcb_actions.h | 1 + pcbnew/tools/pcb_point_editor.cpp | 52 +++++ pcbnew/tools/pcb_selection_tool.cpp | 6 + pcbnew/widgets/appearance_controls.cpp | 15 +- 46 files changed, 978 insertions(+), 30 deletions(-) create mode 100644 pcbnew/pcb_bitmap.cpp create mode 100644 pcbnew/pcb_bitmap.h create mode 100644 pcbnew/python/swig/pcb_bitmap.i diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index f9a412ecce..7ea49d6d98 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -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 diff --git a/common/dialogs/dialog_image_editor.cpp b/common/dialogs/dialog_image_editor.cpp index c77fe65b82..0acfc45979 100644 --- a/common/dialogs/dialog_image_editor.cpp +++ b/common/dialogs/dialog_image_editor.cpp @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -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( 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(); + } +} diff --git a/common/eda_item.cpp b/common/eda_item.cpp index ff24438318..8b7d7006c8 100644 --- a/common/eda_item.cpp +++ b/common/eda_item.cpp @@ -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" ) ) diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp index d5361d4476..e6701a1549 100644 --- a/common/gal/cairo/cairo_gal.cpp +++ b/common/gal/cairo/cairo_gal.cpp @@ -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 ); diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index 344cb2e097..44d8c64602 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -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(); diff --git a/common/pcb.keywords b/common/pcb.keywords index b1176f04f5..08f55a36bf 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -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 diff --git a/common/project/project_local_settings.cpp b/common/project/project_local_settings.cpp index ae26409289..134f00c062 100644 --- a/common/project/project_local_settings.cpp +++ b/common/project/project_local_settings.cpp @@ -157,6 +157,7 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin m_params.emplace_back( new PARAM( "board.opacity.vias", &m_ViaOpacity, 1.0 ) ); m_params.emplace_back( new PARAM( "board.opacity.pads", &m_PadOpacity, 1.0 ) ); m_params.emplace_back( new PARAM( "board.opacity.zones", &m_ZoneOpacity, 0.6 ) ); + m_params.emplace_back( new PARAM( "board.opacity.background_images", &m_BgImageOpacity, 0.6 ) ); m_params.emplace_back( new PARAM_LIST( "board.hidden_nets", &m_HiddenNets, {} ) ); diff --git a/include/core/typeinfo.h b/include/core/typeinfo.h index 757d1c0c9b..a07a6dd4b5 100644 --- a/include/core/typeinfo.h +++ b/include/core/typeinfo.h @@ -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: diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h index fa74ad550f..5e484ed6cf 100644 --- a/include/gal/cairo/cairo_gal.h +++ b/include/gal/cairo/cairo_gal.h @@ -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 diff --git a/include/gal/graphics_abstraction_layer.h b/include/gal/graphics_abstraction_layer.h index 28603045e4..7d0bcb61c3 100644 --- a/include/gal/graphics_abstraction_layer.h +++ b/include/gal/graphics_abstraction_layer.h @@ -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 diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index 29e2f506ca..68d0ce16aa 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -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, diff --git a/include/pcb_display_options.h b/include/pcb_display_options.h index 31bd842301..64533b0220 100644 --- a/include/pcb_display_options.h +++ b/include/pcb_display_options.h @@ -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_ diff --git a/include/project/project_local_settings.h b/include/project/project_local_settings.h index 7c9686f8ce..ab310f3a99 100644 --- a/include/project/project_local_settings.h +++ b/include/project/project_local_settings.h @@ -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. diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index c99dfe964d..46319c0618 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -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 diff --git a/pcbnew/array_creator.cpp b/pcbnew/array_creator.cpp index 412faeb892..3eabc31f56 100644 --- a/pcbnew/array_creator.cpp +++ b/pcbnew/array_creator.cpp @@ -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: diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index 7e7a8cec76..6856ac43fa 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -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: diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index 1336313f14..3b13065d0a 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -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) diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp index 591077209e..810416fb99 100644 --- a/pcbnew/collectors.cpp +++ b/pcbnew/collectors.cpp @@ -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 }; diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index de107c7e0f..9ac0874c2f 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.cpp @@ -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 ); diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index f11434107e..ebdc342611 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -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; diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index f9e30d58ec..3058b1c0f2 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -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 ); diff --git a/pcbnew/menubar_footprint_editor.cpp b/pcbnew/menubar_footprint_editor.cpp index 37a088f83f..220e24a26f 100644 --- a/pcbnew/menubar_footprint_editor.cpp +++ b/pcbnew/menubar_footprint_editor.cpp @@ -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 ); diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index bb1eed72e6..766b276cd4 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -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 ); diff --git a/pcbnew/pcb_base_edit_frame.h b/pcbnew/pcb_base_edit_frame.h index c19b57200c..065bc6128f 100644 --- a/pcbnew/pcb_base_edit_frame.h +++ b/pcbnew/pcb_base_edit_frame.h @@ -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 ); diff --git a/pcbnew/pcb_bitmap.cpp b/pcbnew/pcb_bitmap.cpp new file mode 100644 index 0000000000..77c37dc99a --- /dev/null +++ b/pcbnew/pcb_bitmap.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +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 PCB_BITMAP::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const +{ + EDA_RECT box = GetBoundingBox(); + return std::shared_ptr( + 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& 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; +} diff --git a/pcbnew/pcb_bitmap.h b/pcbnew/pcb_bitmap.h new file mode 100644 index 0000000000..af00c8589c --- /dev/null +++ b/pcbnew/pcb_bitmap.h @@ -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 +#include + + +/** + * 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 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& 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_ diff --git a/pcbnew/pcb_draw_panel_gal.cpp b/pcbnew/pcb_draw_panel_gal.cpp index 1aa3b9edfa..6ffcbb1561 100644 --- a/pcbnew/pcb_draw_panel_gal.cpp +++ b/pcbnew/pcb_draw_panel_gal.cpp @@ -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 ); diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index bfaf0483d6..56faa52a4c 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -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 ); diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index ec9f15114f..9d6a7e3d7b 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -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( 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( item ), aLayer ); break; + case PCB_BITMAP_T: + draw( static_cast( item ), aLayer ); + break; + case PCB_TEXT_T: draw( static_cast( 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() ); diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h index b211a227c8..b0b53d048e 100644 --- a/pcbnew/pcb_painter.h +++ b/pcbnew/pcb_painter.h @@ -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 ); diff --git a/pcbnew/pcbnew_config.cpp b/pcbnew/pcbnew_config.cpp index 19e8617fe7..d827ca61fc 100644 --- a/pcbnew/pcbnew_config.cpp +++ b/pcbnew/pcbnew_config.cpp @@ -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(); diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp index d9658a74c3..5b8e7b5893 100644 --- a/pcbnew/plugins/kicad/pcb_parser.cpp +++ b/pcbnew/plugins/kicad/pcb_parser.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,12 @@ #include #include +// 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 +#include + 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 bitmap = std::make_unique( 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 ); diff --git a/pcbnew/plugins/kicad/pcb_parser.h b/pcbnew/plugins/kicad/pcb_parser.h index e50fba4e29..35f8f35d36 100644 --- a/pcbnew/plugins/kicad/pcb_parser.h +++ b/pcbnew/plugins/kicad/pcb_parser.h @@ -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 ); diff --git a/pcbnew/plugins/kicad/pcb_plugin.cpp b/pcbnew/plugins/kicad/pcb_plugin.cpp index b258098606..72c90e20f5 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.cpp +++ b/pcbnew/plugins/kicad/pcb_plugin.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,13 @@ #include #include +// 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 +#include + + using namespace PCB_KEYS_T; @@ -418,6 +426,10 @@ void PCB_PLUGIN::Format( const BOARD_ITEM* aItem, int aNestLevel ) const format( static_cast( aItem ), aNestLevel ); break; + case PCB_BITMAP_T: + format( static_cast( aItem ), aNestLevel ); + break; + case PCB_FP_SHAPE_T: format( static_cast( 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" : ""; diff --git a/pcbnew/plugins/kicad/pcb_plugin.h b/pcbnew/plugins/kicad/pcb_plugin.h index fdc6d84c9b..53b439d3cf 100644 --- a/pcbnew/plugins/kicad/pcb_plugin.h +++ b/pcbnew/plugins/kicad/pcb_plugin.h @@ -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; diff --git a/pcbnew/python/swig/pcb_bitmap.i b/pcbnew/python/swig/pcb_bitmap.i new file mode 100644 index 0000000000..fb068ec8ea --- /dev/null +++ b/pcbnew/python/swig/pcb_bitmap.i @@ -0,0 +1,7 @@ + + +%include pcb_bitmap.h +%{ +#include +%} + diff --git a/pcbnew/toolbars_footprint_editor.cpp b/pcbnew/toolbars_footprint_editor.cpp index f026cdd60b..73be26656b 100644 --- a/pcbnew/toolbars_footprint_editor.cpp +++ b/pcbnew/toolbars_footprint_editor.cpp @@ -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 ); diff --git a/pcbnew/toolbars_pcb_editor.cpp b/pcbnew/toolbars_pcb_editor.cpp index a11ba97cbc..becbafcefe 100644 --- a/pcbnew/toolbars_pcb_editor.cpp +++ b/pcbnew/toolbars_pcb_editor.cpp @@ -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 ); diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 9caed037a0..c79e5cb5e7 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -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(); + 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(); + 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 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() ); diff --git a/pcbnew/tools/drawing_tool.h b/pcbnew/tools/drawing_tool.h index 138d586eab..a3c114ae10 100644 --- a/pcbnew/tools/drawing_tool.h +++ b/pcbnew/tools/drawing_tool.h @@ -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. diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index 2be84e1082..17a9081b1a 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -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: diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index 322e5cb9aa..3b91c05336 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -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" ), diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index b7d3cedc4b..4c10dffd43 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -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; diff --git a/pcbnew/tools/pcb_point_editor.cpp b/pcbnew/tools/pcb_point_editor.cpp index 8e2de49a24..5670e49812 100644 --- a/pcbnew/tools/pcb_point_editor.cpp +++ b/pcbnew/tools/pcb_point_editor.cpp @@ -41,6 +41,7 @@ using namespace std::placeholders; #include #include #include +#include #include #include #include @@ -191,6 +192,20 @@ std::shared_ptr 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: { diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index dc400adb01..3353bc17ce 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -33,6 +33,7 @@ using namespace std::placeholders; #include #include #include +#include #include #include #include @@ -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( item )->GetDrill() / 2 ) * 1.5; } + else if( item->Type() == PCB_BITMAP_T ) + { + VECTOR2I size = static_cast( item )->GetSize(); + area = size.x * size.y; + } else { try diff --git a/pcbnew/widgets/appearance_controls.cpp b/pcbnew/widgets/appearance_controls.cpp index def1330857..b43113bcd0 100644 --- a/pcbnew/widgets/appearance_controls.cpp +++ b/pcbnew/widgets/appearance_controls.cpp @@ -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 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( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break; - case static_cast( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break; - case static_cast( LAYER_PADS ): options.m_PadOpacity = aOpacity; break; - case static_cast( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break; + case static_cast( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break; + case static_cast( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break; + case static_cast( LAYER_PADS ): options.m_PadOpacity = aOpacity; break; + case static_cast( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break; + case static_cast( LAYER_DRAW_BITMAPS ): options.m_BgImageOpacity = aOpacity; break; default: return; }