From 3f131e20114bc1125ac7009863719e51ba787c51 Mon Sep 17 00:00:00 2001 From: John Beard Date: Sun, 29 Sep 2024 22:03:53 +0100 Subject: [PATCH] Abstract REFERENCE_IMAGE to a separate class Break the non-PCB-specfic parts of PCB_REFERENCE_IMAGE out to a common REFERENCE_IMAGE class, which is then composed into the PCB_REFERENCE_IMAGE. This will make it easier to bring the transform origin logic to eeschema without repetition. --- common/CMakeLists.txt | 1 + common/bitmap_base.cpp | 26 +- common/dialogs/panel_image_editor.cpp | 16 +- common/reference_image.cpp | 280 ++++++++++++++++++ include/bitmap_base.h | 2 +- include/dialogs/panel_image_editor.h | 25 +- include/reference_image.h | 137 +++++++++ libs/kimath/include/geometry/geometry_utils.h | 27 +- libs/kimath/src/geometry/geometry_utils.cpp | 18 ++ .../dialog_reference_image_properties.cpp | 29 +- .../dialog_reference_image_properties.h | 12 +- .../easyedapro/pcb_io_easyedapro_parser.cpp | 9 +- .../pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp | 10 +- .../kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp | 15 +- pcbnew/pcb_painter.cpp | 13 +- pcbnew/pcb_reference_image.cpp | 238 +++++---------- pcbnew/pcb_reference_image.h | 90 ++---- pcbnew/tools/drawing_tool.cpp | 2 +- pcbnew/tools/pcb_point_editor.cpp | 51 ++-- pcbnew/tools/pcb_selection_tool.cpp | 4 +- qa/tests/pcbnew/test_reference_image_load.cpp | 11 +- 21 files changed, 670 insertions(+), 346 deletions(-) create mode 100644 common/reference_image.cpp create mode 100644 include/reference_image.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a45295f863..cfc7af82d7 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -586,6 +586,7 @@ set( COMMON_SRCS ptree.cpp rc_item.cpp refdes_utils.cpp + reference_image.cpp render_settings.cpp scintilla_tricks.cpp status_popup.cpp diff --git a/common/bitmap_base.cpp b/common/bitmap_base.cpp index d3f61f0734..76a5c54a0b 100644 --- a/common/bitmap_base.cpp +++ b/common/bitmap_base.cpp @@ -107,20 +107,20 @@ void BITMAP_BASE::updatePPI() } -void BITMAP_BASE::ImportData( BITMAP_BASE* aItem ) +void BITMAP_BASE::ImportData( BITMAP_BASE& aItem ) { - *m_image = *aItem->m_image; - *m_bitmap = *aItem->m_bitmap; - *m_originalImage = *aItem->m_originalImage; - m_imageId = aItem->m_imageId; - m_scale = aItem->m_scale; - m_ppi = aItem->m_ppi; - m_pixelSizeIu = aItem->m_pixelSizeIu; - m_isMirroredX = aItem->m_isMirroredX; - m_isMirroredY = aItem->m_isMirroredY; - m_rotation = aItem->m_rotation; - m_imageType = aItem->m_imageType; - m_imageData = aItem->m_imageData; + *m_image = *aItem.m_image; + *m_bitmap = *aItem.m_bitmap; + *m_originalImage = *aItem.m_originalImage; + m_imageId = aItem.m_imageId; + m_scale = aItem.m_scale; + m_ppi = aItem.m_ppi; + m_pixelSizeIu = aItem.m_pixelSizeIu; + m_isMirroredX = aItem.m_isMirroredX; + m_isMirroredY = aItem.m_isMirroredY; + m_rotation = aItem.m_rotation; + m_imageType = aItem.m_imageType; + m_imageData = aItem.m_imageData; } diff --git a/common/dialogs/panel_image_editor.cpp b/common/dialogs/panel_image_editor.cpp index 17f3b0bd82..94c3b31793 100644 --- a/common/dialogs/panel_image_editor.cpp +++ b/common/dialogs/panel_image_editor.cpp @@ -35,11 +35,10 @@ #include -PANEL_IMAGE_EDITOR::PANEL_IMAGE_EDITOR( wxWindow* aParent, BITMAP_BASE* aItem ) : - PANEL_IMAGE_EDITOR_BASE( aParent ) +PANEL_IMAGE_EDITOR::PANEL_IMAGE_EDITOR( wxWindow* aParent, const BITMAP_BASE& aItem ) : + PANEL_IMAGE_EDITOR_BASE( aParent ), + m_workingImage( std::make_unique( aItem ) ) { - m_workingImage = new BITMAP_BASE( *aItem ); - wxString msg; msg.Printf( wxT( "%f" ), m_workingImage->GetScale() ); m_textCtrlScale->SetValue( msg ); @@ -49,6 +48,11 @@ PANEL_IMAGE_EDITOR::PANEL_IMAGE_EDITOR( wxWindow* aParent, BITMAP_BASE* aItem ) } +PANEL_IMAGE_EDITOR::~PANEL_IMAGE_EDITOR() +{ +} + + void PANEL_IMAGE_EDITOR::OnGreyScaleConvert( wxCommandEvent& event ) { m_workingImage->ConvertToGreyscale(); @@ -132,11 +136,11 @@ void PANEL_IMAGE_EDITOR::OnRedrawPanel( wxPaintEvent& event ) } -void PANEL_IMAGE_EDITOR::TransferToImage( BITMAP_BASE* aItem ) +void PANEL_IMAGE_EDITOR::TransferToImage( BITMAP_BASE& aItem ) { wxString msg = m_textCtrlScale->GetValue(); double scale = 1.0; msg.ToDouble( &scale ); m_workingImage->SetScale( scale ); - aItem->ImportData( m_workingImage ); + aItem.ImportData( *m_workingImage ); } diff --git a/common/reference_image.cpp b/common/reference_image.cpp new file mode 100644 index 0000000000..113d59b7b5 --- /dev/null +++ b/common/reference_image.cpp @@ -0,0 +1,280 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 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 + */ + +#include "reference_image.h" + +#include + +#include +#include + + +REFERENCE_IMAGE::REFERENCE_IMAGE( const EDA_IU_SCALE& aIuScale ) : + m_iuScale( aIuScale ), m_pos( 0, 0 ), m_transformOriginOffset( 0, 0 ), + m_bitmapBase( std::make_unique() ) +{ + updatePixelSizeInIU(); +} + + +REFERENCE_IMAGE::REFERENCE_IMAGE( const REFERENCE_IMAGE& aOther ) : + m_iuScale( aOther.m_iuScale ), m_pos( aOther.m_pos ), + m_transformOriginOffset( aOther.m_transformOriginOffset ), + m_bitmapBase( std::make_unique( *aOther.m_bitmapBase ) ) +{ + updatePixelSizeInIU(); +} + + +REFERENCE_IMAGE::~REFERENCE_IMAGE() +{ +} + + +void REFERENCE_IMAGE::updatePixelSizeInIU() +{ + const double pixelSizeIu = (double) m_iuScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI(); + m_bitmapBase->SetPixelSizeIu( pixelSizeIu ); +} + + +REFERENCE_IMAGE& REFERENCE_IMAGE::operator=( const REFERENCE_IMAGE& aOther ) +{ + wxASSERT( m_iuScale.IU_PER_MILS == aOther.m_iuScale.IU_PER_MILS ); + + if( &aOther != this ) + { + if( aOther.m_bitmapBase ) + { + m_bitmapBase = std::make_unique( *aOther.m_bitmapBase ); + } + m_pos = aOther.m_pos; + m_transformOriginOffset = aOther.m_transformOriginOffset; + updatePixelSizeInIU(); + } + + return *this; +} + + +bool REFERENCE_IMAGE::operator==( const REFERENCE_IMAGE& aOther ) const +{ + if( m_pos != aOther.m_pos ) + return false; + + if( m_transformOriginOffset != aOther.m_transformOriginOffset ) + return false; + + if( m_bitmapBase->GetSize() != aOther.m_bitmapBase->GetSize() ) + return false; + + if( m_bitmapBase->GetPPI() != aOther.m_bitmapBase->GetPPI() ) + return false; + + if( m_bitmapBase->GetScale() != aOther.m_bitmapBase->GetScale() ) + return false; + + if( m_bitmapBase->GetImageID() != aOther.m_bitmapBase->GetImageID() ) + return false; + + if( m_bitmapBase->GetImageData() != aOther.m_bitmapBase->GetImageData() ) + return false; + + return true; +} + + +double REFERENCE_IMAGE::Similarity( const REFERENCE_IMAGE& aOther ) const +{ + double similarity = 1.0; + + if( m_pos != aOther.m_pos ) + similarity *= 0.9; + + if( m_bitmapBase->GetSize() != aOther.m_bitmapBase->GetSize() ) + similarity *= 0.9; + + if( m_bitmapBase->GetPPI() != aOther.m_bitmapBase->GetPPI() ) + similarity *= 0.9; + + if( m_bitmapBase->GetScale() != aOther.m_bitmapBase->GetScale() ) + similarity *= 0.9; + + if( m_bitmapBase->GetImageID() != aOther.m_bitmapBase->GetImageID() ) + similarity *= 0.9; + + if( m_bitmapBase->GetImageData() != aOther.m_bitmapBase->GetImageData() ) + similarity *= 0.9; + + return similarity; +} + + +BOX2I REFERENCE_IMAGE::GetBoundingBox() const +{ + return BOX2I::ByCenter( m_pos, m_bitmapBase->GetSize() ); +} + + +VECTOR2I REFERENCE_IMAGE::GetPosition() const +{ + return m_pos; +} + + +void REFERENCE_IMAGE::SetPosition( const VECTOR2I& aPos ) +{ + const BOX2D newBox = BOX2D::ByCenter( aPos, m_bitmapBase->GetSize() ); + + if( !IsBOX2Safe( newBox ) ) + return; + + m_pos = aPos; +} + + +VECTOR2I REFERENCE_IMAGE::GetTransformOriginOffset() const +{ + return m_transformOriginOffset; +} + + +void REFERENCE_IMAGE::SetTransformOriginOffset( const VECTOR2I& aCenter ) +{ + m_transformOriginOffset = aCenter; +} + + +VECTOR2I REFERENCE_IMAGE::GetSize() const +{ + return m_bitmapBase->GetSize(); +} + + +double REFERENCE_IMAGE::GetImageScale() const +{ + return m_bitmapBase->GetScale(); +} + + +void REFERENCE_IMAGE::SetImageScale( double aScale ) +{ + if( aScale < 0 ) + return; + + const double ratio = aScale / m_bitmapBase->GetScale(); + + const VECTOR2D currentOrigin = m_pos + m_transformOriginOffset; + const VECTOR2D newOffset = m_transformOriginOffset * ratio; + const VECTOR2D newCenter = currentOrigin - newOffset; + const VECTOR2D newSize = m_bitmapBase->GetSize() * ratio; + + // The span of the image is limited to the size of the coordinate system + if( !IsVec2SafeXY( newSize ) ) + return; + + const BOX2D newBox = BOX2D::ByCenter( newCenter, newSize ); + + // Any overflow, just reject the call + if( !IsBOX2Safe( newBox ) ) + return; + + m_bitmapBase->SetScale( aScale ); + SetTransformOriginOffset( KiROUND( newOffset ) ); + // Don't need to recheck the box, we just did that + m_pos = KiROUND( newCenter ); +} + + +void REFERENCE_IMAGE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) +{ + VECTOR2I newPos = m_pos; + MIRROR( newPos, aCentre, aFlipDirection ); + + const BOX2D newBox = BOX2D::ByCenter( newPos, m_bitmapBase->GetSize() ); + + if( !IsBOX2Safe( newBox ) ) + return; + + m_pos = newPos; + m_bitmapBase->Mirror( aFlipDirection ); +} + + +void REFERENCE_IMAGE::Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle ) +{ + EDA_ANGLE norm( aAngle.AsDegrees(), DEGREES_T ); + + RotatePoint( m_pos, aCenter, aAngle ); + + norm.Normalize(); + + // each call to m_bitmapBase->Rotate() rotates 90 degrees CCW + for( double ang = 45.0; ang < norm.AsDegrees(); ang += 90.0 ) + m_bitmapBase->Rotate( false ); +} + + +bool REFERENCE_IMAGE::ReadImageFile( const wxString& aFullFilename ) +{ + if( m_bitmapBase->ReadImageFile( aFullFilename ) ) + { + updatePixelSizeInIU(); + return true; + } + + return false; +} + + +bool REFERENCE_IMAGE::ReadImageFile( wxMemoryBuffer& aBuffer ) +{ + if( m_bitmapBase->ReadImageFile( aBuffer ) ) + { + updatePixelSizeInIU(); + return true; + } + + return false; +} + + +const BITMAP_BASE& REFERENCE_IMAGE::GetImage() const +{ + // This cannot be null after construction + return *m_bitmapBase; +} + + +BITMAP_BASE& REFERENCE_IMAGE::MutableImage() const +{ + return *m_bitmapBase; +} + + +void REFERENCE_IMAGE::SwapData( REFERENCE_IMAGE& aOther ) +{ + std::swap( m_pos, aOther.m_pos ); + std::swap( m_transformOriginOffset, aOther.m_transformOriginOffset ); + std::swap( m_bitmapBase, aOther.m_bitmapBase ); +} \ No newline at end of file diff --git a/include/bitmap_base.h b/include/bitmap_base.h index 64ae3a170b..280e0a925a 100644 --- a/include/bitmap_base.h +++ b/include/bitmap_base.h @@ -78,7 +78,7 @@ public: /** * Copy aItem image to this object and update #m_bitmap. */ - void ImportData( BITMAP_BASE* aItem ); + void ImportData( BITMAP_BASE& aItem ); /** * This scaling factor depends on #m_pixelSizeIu and #m_scale. diff --git a/include/dialogs/panel_image_editor.h b/include/dialogs/panel_image_editor.h index d0967865ea..e8d8e20cce 100644 --- a/include/dialogs/panel_image_editor.h +++ b/include/dialogs/panel_image_editor.h @@ -2,7 +2,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2018 jean-pierre.charras - * Copyright (C) 2018 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018-2024 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 @@ -22,22 +22,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef PANEL_IMAGE_EDITOR_H -#define PANEL_IMAGE_EDITOR_H +#pragma once -#include #include +#include + +class BITMAP_BASE; + class PANEL_IMAGE_EDITOR : public PANEL_IMAGE_EDITOR_BASE { -private: - BITMAP_BASE* m_workingImage; // The copy of BITMAP_BASE to be edited - public: - PANEL_IMAGE_EDITOR( wxWindow* aParent, BITMAP_BASE* aItem ); - ~PANEL_IMAGE_EDITOR() { delete m_workingImage; } - + PANEL_IMAGE_EDITOR( wxWindow* aParent, const BITMAP_BASE& aItem ); + ~PANEL_IMAGE_EDITOR(); public: bool TransferDataFromWindow() override; @@ -46,13 +44,14 @@ public: * Function TransferToImage * copy edited image to aItem */ - void TransferToImage( BITMAP_BASE* aItem ); + void TransferToImage( BITMAP_BASE& aItem ); private: void OnGreyScaleConvert( wxCommandEvent& event ) override; void OnRedrawPanel( wxPaintEvent& event ) override; bool CheckValues(); -}; -#endif + // A copy of BITMAP_BASE to be edited + std::unique_ptr m_workingImage; +}; \ No newline at end of file diff --git a/include/reference_image.h b/include/reference_image.h new file mode 100644 index 0000000000..2509573230 --- /dev/null +++ b/include/reference_image.h @@ -0,0 +1,137 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2024 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 + */ + +#pragma once + +#include + +#include +#include +#include +#include + +class BITMAP_BASE; + +class wxMemoryBuffer; + +/** + * A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is + * displayed in an editor as a reference for the user. + */ +class REFERENCE_IMAGE +{ +public: + REFERENCE_IMAGE( const EDA_IU_SCALE& aIuScale ); + REFERENCE_IMAGE( const REFERENCE_IMAGE& aOther ); + ~REFERENCE_IMAGE(); + + REFERENCE_IMAGE& operator=( const REFERENCE_IMAGE& aOther ); + + bool operator==( const REFERENCE_IMAGE& aOther ) const; + + double Similarity( const REFERENCE_IMAGE& aOther ) const; + + BOX2I GetBoundingBox() const; + + bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const; + + VECTOR2I GetPosition() const; + void SetPosition( const VECTOR2I& aPos ); + + VECTOR2I GetSize() const; + + /** + * @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; + + /** + * Set the image "zoom" value. + * + * The image is scaled such that the position of the image's + * transform origin is unchanged. + * + * If the scale is negative or the image would overflow the + * the coordinate system, nothing is updated. + */ + void SetImageScale( double aScale ); + + void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ); + + void Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle ); + + /** + * 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 ); + + /** + * Read and store an image file. + * + * Initialize the bitmap used to draw this item format. + * + * @param aBuf is the memory buffer containing the image file to read. + * @return true if success reading else false. + */ + bool ReadImageFile( wxMemoryBuffer& aBuf ); + + void SwapData( REFERENCE_IMAGE& aItem ); + + /** + * Get the underlying image. + * + * This will always return a valid reference, but it may be to an empty image. + */ + const BITMAP_BASE& GetImage() const; + + /** + * Only use this if you really need to modify the underlying image + */ + BITMAP_BASE& MutableImage() const; + + + /** + * Get the center of scaling, etc, relative to the image center (GetPosition()). + */ + VECTOR2I GetTransformOriginOffset() const; + void SetTransformOriginOffset( const VECTOR2I& aCenter ); + +private: + void updatePixelSizeInIU(); + + const EDA_IU_SCALE& m_iuScale; + + VECTOR2I m_pos; // XY coordinates of center of the bitmap + ///< Center of scaling, etc, relative to the image center + VECTOR2I m_transformOriginOffset; + + std::unique_ptr m_bitmapBase; +}; \ No newline at end of file diff --git a/libs/kimath/include/geometry/geometry_utils.h b/libs/kimath/include/geometry/geometry_utils.h index a9e5d7bedd..67f6d3edfb 100644 --- a/libs/kimath/include/geometry/geometry_utils.h +++ b/libs/kimath/include/geometry/geometry_utils.h @@ -27,8 +27,7 @@ * @brief a few functions useful in geometry calculations. */ -#ifndef GEOMETRY_UTILS_H -#define GEOMETRY_UTILS_H +#pragma once #include #include // for copysign @@ -203,4 +202,26 @@ inline bool IsVec2SafeXY( const VECTOR2& aVec ) bool ClipLine( const BOX2I *aClipBox, int &x1, int &y1, int &x2, int &y2 ); -#endif // #ifndef GEOMETRY_UTILS_H \ No newline at end of file +namespace KIGEOM +{ +/** + * Perform a point-to-box hit test. + * + * @param aHitPoint - The point that is hitting the box + * @param aHittee - The box that is tested for hit. + * @param aAccuracy - The accuracy of the hit test. + */ +bool BoxHitTest( const VECTOR2I& aHitPoint, const BOX2I& aHittee, int aAccuracy ); + +/** + * Perform a box-to-box hit test. + * + * @param aHitter - The box that is either hitting or containing the hittee. + * @param aHittee - The box that is either being hit or contained by the hitter + * (this is possibly an object's bounding box). + * @param aHitteeContained - True if the hittee is tested for total containment, + * false if it is tested for intersection. + * @param aAccuracy - The accuracy of the hit test. + */ +bool BoxHitTest( const BOX2I& aHitter, const BOX2I& aHittee, bool aHitteeContained, int aAccuracy ); +}; // namespace KIGEOM \ No newline at end of file diff --git a/libs/kimath/src/geometry/geometry_utils.cpp b/libs/kimath/src/geometry/geometry_utils.cpp index 2b2c2f7519..2135c0b0cc 100644 --- a/libs/kimath/src/geometry/geometry_utils.cpp +++ b/libs/kimath/src/geometry/geometry_utils.cpp @@ -198,3 +198,21 @@ bool ClipLine( const BOX2I *aClipBox, int &x1, int &y1, int &x2, int &y2 ) return false; } + +bool KIGEOM::BoxHitTest( const VECTOR2I& aHitter, const BOX2I& aHittee, int aAccuracy ) +{ + const BOX2I hittee = aHittee.GetInflated( aAccuracy ); + return hittee.Contains( aHitter ); +} + + +bool KIGEOM::BoxHitTest( const BOX2I& aHitter, const BOX2I& aHittee, bool aHitteeContained, + int aAccuracy ) +{ + const BOX2I hitter = aHitter.GetInflated( aAccuracy ); + + if( aHitteeContained ) + return hitter.Contains( aHittee ); + + return hitter.Intersects( aHittee ); +} \ No newline at end of file diff --git a/pcbnew/dialogs/dialog_reference_image_properties.cpp b/pcbnew/dialogs/dialog_reference_image_properties.cpp index 8d2d7c116c..42b4415dd0 100644 --- a/pcbnew/dialogs/dialog_reference_image_properties.cpp +++ b/pcbnew/dialogs/dialog_reference_image_properties.cpp @@ -33,7 +33,7 @@ DIALOG_REFERENCE_IMAGE_PROPERTIES::DIALOG_REFERENCE_IMAGE_PROPERTIES( PCB_BASE_FRAME* aParent, - PCB_REFERENCE_IMAGE* aBitmap ) : + PCB_REFERENCE_IMAGE& aBitmap ) : DIALOG_REFERENCE_IMAGE_PROPERTIES_BASE( aParent ), m_frame( aParent ), m_bitmap( aBitmap ), @@ -41,14 +41,15 @@ DIALOG_REFERENCE_IMAGE_PROPERTIES::DIALOG_REFERENCE_IMAGE_PROPERTIES( PCB_BASE_F m_posY( aParent, m_YPosLabel, m_ModPositionY, m_YPosUnit ) { // Create the image editor page - m_imageEditor = new PANEL_IMAGE_EDITOR( m_Notebook, aBitmap->MutableImage() ); + m_imageEditor = + new PANEL_IMAGE_EDITOR( m_Notebook, aBitmap.GetReferenceImage().MutableImage() ); m_Notebook->AddPage( m_imageEditor, _( "Image" ), false ); m_posX.SetCoordType( ORIGIN_TRANSFORMS::ABS_X_COORD ); m_posY.SetCoordType( ORIGIN_TRANSFORMS::ABS_Y_COORD ); // Only show unactivated board layers if the bitmap is on one of them - if( !m_frame->GetBoard()->IsLayerEnabled( m_bitmap->GetLayer() ) ) + if( !m_frame->GetBoard()->IsLayerEnabled( m_bitmap.GetLayer() ) ) m_LayerSelectionCtrl->ShowNonActivatedLayers( true ); m_LayerSelectionCtrl->SetLayersHotkeys( false ); @@ -63,7 +64,7 @@ DIALOG_REFERENCE_IMAGE_PROPERTIES::DIALOG_REFERENCE_IMAGE_PROPERTIES( PCB_BASE_F void PCB_BASE_EDIT_FRAME::ShowReferenceImagePropertiesDialog( BOARD_ITEM* aBitmap ) { - PCB_REFERENCE_IMAGE* bitmap = static_cast( aBitmap ); + PCB_REFERENCE_IMAGE& bitmap = static_cast( *aBitmap ); DIALOG_REFERENCE_IMAGE_PROPERTIES dlg( this, bitmap ); if( dlg.ShowModal() == wxID_OK ) @@ -78,12 +79,12 @@ void PCB_BASE_EDIT_FRAME::ShowReferenceImagePropertiesDialog( BOARD_ITEM* aBitma bool DIALOG_REFERENCE_IMAGE_PROPERTIES::TransferDataToWindow() { - m_posX.SetValue( m_bitmap->GetPosition().x ); - m_posY.SetValue( m_bitmap->GetPosition().y ); + m_posX.SetValue( m_bitmap.GetPosition().x ); + m_posY.SetValue( m_bitmap.GetPosition().y ); - m_LayerSelectionCtrl->SetLayerSelection( m_bitmap->GetLayer() ); + m_LayerSelectionCtrl->SetLayerSelection( m_bitmap.GetLayer() ); - m_cbLocked->SetValue( m_bitmap->IsLocked() ); + m_cbLocked->SetValue( m_bitmap.IsLocked() ); m_cbLocked->SetToolTip( _( "Locked items cannot be freely moved and oriented on the canvas " "and can only be selected when the 'Locked items' checkbox is " "checked in the selection filter." ) ); @@ -97,16 +98,16 @@ bool DIALOG_REFERENCE_IMAGE_PROPERTIES::TransferDataFromWindow() if( m_imageEditor->TransferDataFromWindow() ) { // Save old image in undo list if not already in edit - if( m_bitmap->GetEditFlags() == 0 ) - m_frame->SaveCopyInUndoList( m_bitmap, UNDO_REDO::CHANGED ); + if( m_bitmap.GetEditFlags() == 0 ) + m_frame->SaveCopyInUndoList( &m_bitmap, UNDO_REDO::CHANGED ); // Update our bitmap from the editor - m_imageEditor->TransferToImage( m_bitmap->MutableImage() ); + m_imageEditor->TransferToImage( m_bitmap.GetReferenceImage().MutableImage() ); // Set position, etc. - m_bitmap->SetPosition( VECTOR2I( m_posX.GetValue(), m_posY.GetValue() ) ); - m_bitmap->SetLayer( ToLAYER_ID( m_LayerSelectionCtrl->GetLayerSelection() ) ); - m_bitmap->SetLocked( m_cbLocked->GetValue() ); + m_bitmap.SetPosition( VECTOR2I( m_posX.GetValue(), m_posY.GetValue() ) ); + m_bitmap.SetLayer( ToLAYER_ID( m_LayerSelectionCtrl->GetLayerSelection() ) ); + m_bitmap.SetLocked( m_cbLocked->GetValue() ); return true; } diff --git a/pcbnew/dialogs/dialog_reference_image_properties.h b/pcbnew/dialogs/dialog_reference_image_properties.h index a7f482ef54..d5dd9c7100 100644 --- a/pcbnew/dialogs/dialog_reference_image_properties.h +++ b/pcbnew/dialogs/dialog_reference_image_properties.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2018-2024 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 @@ -21,8 +21,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef DIALOG_REFERENCE_IMAGE_PROPERTIES_H -#define DIALOG_REFERENCE_IMAGE_PROPERTIES_H +#pragma once #include #include @@ -36,7 +35,7 @@ class PANEL_IMAGE_EDITOR; class DIALOG_REFERENCE_IMAGE_PROPERTIES : public DIALOG_REFERENCE_IMAGE_PROPERTIES_BASE { public: - DIALOG_REFERENCE_IMAGE_PROPERTIES( PCB_BASE_FRAME* aParent, PCB_REFERENCE_IMAGE* aBitmap ); + DIALOG_REFERENCE_IMAGE_PROPERTIES( PCB_BASE_FRAME* aParent, PCB_REFERENCE_IMAGE& aBitmap ); ~DIALOG_REFERENCE_IMAGE_PROPERTIES() override {} private: @@ -45,11 +44,10 @@ private: private: PCB_BASE_FRAME* m_frame; - PCB_REFERENCE_IMAGE* m_bitmap; + ///< The reference image being edited + PCB_REFERENCE_IMAGE& m_bitmap; PANEL_IMAGE_EDITOR* m_imageEditor; UNIT_BINDER m_posX; UNIT_BINDER m_posY; }; - -#endif // DIALOG_REFERENCE_IMAGE_PROPERTIES_H diff --git a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp index db33e96892..8c53bafbf1 100644 --- a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp +++ b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp @@ -1466,14 +1466,15 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard( std::unique_ptr bitmap = std::make_unique( aBoard, kcenter, klayer ); + REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage(); wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags() & ~wxImage::Load_Verbose ); - if( bitmap->ReadImageFile( buf ) ) + if( refImage.ReadImageFile( buf ) ) { - double scaleFactor = ScaleSize( size.x ) / bitmap->GetSize().x; - bitmap->SetImageScale( scaleFactor ); + double scaleFactor = ScaleSize( size.x ) / refImage.GetSize().x; + refImage.SetImageScale( scaleFactor ); // TODO: support non-90-deg angles bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) ); @@ -1484,7 +1485,7 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard( MIRROR( x, KiROUND( kstart.x ) ); bitmap->SetX( x ); - bitmap->MutableImage()->Mirror( FLIP_DIRECTION::LEFT_RIGHT ); + refImage.MutableImage().Mirror( FLIP_DIRECTION::LEFT_RIGHT ); } aBoard->Add( bitmap.release(), ADD_MODE::APPEND ); diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp index b87fbfd486..d87a53759d 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp @@ -1072,7 +1072,9 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_REFERENCE_IMAGE* aBitmap, int aNestLe { wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" ); - const wxImage* image = aBitmap->GetImage()->GetImageData(); + const REFERENCE_IMAGE& refImage = aBitmap->GetReferenceImage(); + + const wxImage* image = refImage.GetImage().GetImageData(); wxCHECK_RET( image != nullptr, "wxImage* is NULL" ); @@ -1082,15 +1084,15 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_REFERENCE_IMAGE* aBitmap, int aNestLe formatLayer( aBitmap->GetLayer() ); - if( aBitmap->GetImage()->GetScale() != 1.0 ) - m_out->Print( 0, "(scale %g)", aBitmap->GetImage()->GetScale() ); + if( refImage.GetImageScale() != 1.0 ) + m_out->Print( 0, "(scale %g)", refImage.GetImageScale() ); if( const bool locked = aBitmap->IsLocked() ) KICAD_FORMAT::FormatBool( m_out, 0, "locked", locked ); m_out->Print( aNestLevel + 1, "(data" ); - wxString out = wxBase64Encode( aBitmap->GetImage()->GetImageDataBuffer() ); + wxString out = wxBase64Encode( refImage.GetImage().GetImageDataBuffer() ); // Apparently the MIME standard character width for base64 encoding is 76 (unconfirmed) // so use it in a vain attempt to be standard like. diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp index c1e7136ab3..88af4f0e74 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp @@ -3195,14 +3195,16 @@ PCB_REFERENCE_IMAGE* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_REFERENCE_IMAGE( BOARD_ break; case T_scale: - bitmap->SetImageScale( parseDouble( "image scale factor" ) ); + { + REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage(); + refImage.SetImageScale( parseDouble( "image scale factor" ) ); - if( !std::isnormal( bitmap->GetImage()->GetScale() ) ) - bitmap->SetImageScale( 1.0 ); + if( !std::isnormal( refImage.GetImageScale() ) ) + refImage.SetImageScale( 1.0 ); NeedRIGHT(); break; - + } case T_data: { token = NextTok(); @@ -3222,9 +3224,10 @@ PCB_REFERENCE_IMAGE* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_REFERENCE_IMAGE( BOARD_ token = NextTok(); } - wxMemoryBuffer buffer = wxBase64Decode( data ); + wxMemoryBuffer buffer = wxBase64Decode( data ); - if( !bitmap->ReadImageFile( buffer ) ) + REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage(); + if( !refImage.ReadImageFile( buffer ) ) THROW_IO_ERROR( _( "Failed to read image data." ) ); break; diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index ecc5ef0dba..e294ca76e9 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -2062,11 +2062,13 @@ void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2I& aPosition, void PCB_PAINTER::draw( const PCB_REFERENCE_IMAGE* aBitmap, int aLayer ) { m_gal->Save(); - m_gal->Translate( aBitmap->GetPosition() ); + + const REFERENCE_IMAGE& refImg = aBitmap->GetReferenceImage(); + m_gal->Translate( refImg.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(); + const double img_scale = refImg.GetImageScale(); if( img_scale != 1.0 ) m_gal->Scale( VECTOR2D( img_scale, img_scale ) ); @@ -2080,7 +2082,7 @@ void PCB_PAINTER::draw( const PCB_REFERENCE_IMAGE* aBitmap, int aLayer ) m_gal->SetIsFill( false ); // Draws a bounding box. - VECTOR2D bm_size( aBitmap->GetSize() ); + VECTOR2D bm_size( refImg.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. @@ -2094,13 +2096,12 @@ void PCB_PAINTER::draw( const PCB_REFERENCE_IMAGE* aBitmap, int aLayer ) // Hard code reference images as opaque when selected. Otherwise cached layers will // not be rendered under the selected image because cached layers are rendered after // non-cached layers (e.g. bitmaps), which will have a closer Z order. - m_gal->DrawBitmap( *aBitmap->GetImage(), 1.0 ); + m_gal->DrawBitmap( refImg.GetImage(), 1.0 ); } else - m_gal->DrawBitmap( *aBitmap->GetImage(), + m_gal->DrawBitmap( refImg.GetImage(), m_pcbSettings.GetColor( aBitmap, aBitmap->GetLayer() ).a ); - m_gal->Restore(); } diff --git a/pcbnew/pcb_reference_image.cpp b/pcbnew/pcb_reference_image.cpp index 33b0d62023..4d4fae5e4f 100644 --- a/pcbnew/pcb_reference_image.cpp +++ b/pcbnew/pcb_reference_image.cpp @@ -25,19 +25,19 @@ #include "pcb_reference_image.h" -#include -#include -#include -#include -#include #include +#include +#include #include -#include #include -#include -#include +#include +#include +#include +#include #include #include +#include +#include #include @@ -47,26 +47,20 @@ using KIGFX::PCB_RENDER_SETTINGS; PCB_REFERENCE_IMAGE::PCB_REFERENCE_IMAGE( BOARD_ITEM* aParent, const VECTOR2I& aPos, PCB_LAYER_ID aLayer ) : - BOARD_ITEM( aParent, PCB_REFERENCE_IMAGE_T, aLayer ), m_pos( aPos ), - m_transformOriginOffset( 0, 0 ) + BOARD_ITEM( aParent, PCB_REFERENCE_IMAGE_T, aLayer ), m_referenceImage( pcbIUScale ) { - m_bitmapBase = new BITMAP_BASE(); - m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() ); + m_referenceImage.SetPosition( aPos ); } PCB_REFERENCE_IMAGE::PCB_REFERENCE_IMAGE( const PCB_REFERENCE_IMAGE& aPCBBitmap ) : - BOARD_ITEM( aPCBBitmap ), m_pos( aPCBBitmap.m_pos ), - m_transformOriginOffset( aPCBBitmap.m_transformOriginOffset ) + BOARD_ITEM( aPCBBitmap ), m_referenceImage( aPCBBitmap.m_referenceImage ) { - m_bitmapBase = new BITMAP_BASE( *aPCBBitmap.m_bitmapBase ); - m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() ); } PCB_REFERENCE_IMAGE::~PCB_REFERENCE_IMAGE() { - delete m_bitmapBase; } @@ -79,43 +73,14 @@ PCB_REFERENCE_IMAGE& PCB_REFERENCE_IMAGE::operator=( const BOARD_ITEM& aItem ) if( &aItem != this ) { BOARD_ITEM::operator=( aItem ); - - PCB_REFERENCE_IMAGE* bitmap = (PCB_REFERENCE_IMAGE*) &aItem; - - delete m_bitmapBase; - m_bitmapBase = new BITMAP_BASE( *bitmap->m_bitmapBase ); - m_pos = bitmap->m_pos; - m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() ); + const PCB_REFERENCE_IMAGE& refImg = static_cast( aItem ); + m_referenceImage = refImg.m_referenceImage; } return *this; } -bool PCB_REFERENCE_IMAGE::ReadImageFile( const wxString& aFullFilename ) -{ - if( m_bitmapBase->ReadImageFile( aFullFilename ) ) - { - m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() ); - return true; - } - - return false; -} - - -bool PCB_REFERENCE_IMAGE::ReadImageFile( wxMemoryBuffer& aBuffer ) -{ - if( m_bitmapBase->ReadImageFile( aBuffer ) ) - { - m_bitmapBase->SetPixelSizeIu( (float) pcbIUScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI() ); - return true; - } - - return false; -} - - EDA_ITEM* PCB_REFERENCE_IMAGE::Clone() const { return new PCB_REFERENCE_IMAGE( *this ); @@ -135,8 +100,7 @@ void PCB_REFERENCE_IMAGE::swapData( BOARD_ITEM* aItem ) std::swap( m_flags, item->m_flags ); std::swap( m_parent, item->m_parent ); std::swap( m_forceVisible, item->m_forceVisible ); - std::swap( m_pos, item->m_pos ); - std::swap( m_bitmapBase, item->m_bitmapBase ); + m_referenceImage.SwapData( item->m_referenceImage ); } @@ -165,11 +129,7 @@ double PCB_REFERENCE_IMAGE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const const BOX2I PCB_REFERENCE_IMAGE::GetBoundingBox() const { - // Bitmaps are center origin, BOX2Is need top-left origin - const VECTOR2I size = m_bitmapBase->GetSize(); - const VECTOR2I topLeft{ m_pos.x - size.x / 2, m_pos.y - size.y / 2 }; - - return BOX2I{ topLeft, size }; + return m_referenceImage.GetBoundingBox(); } @@ -181,84 +141,33 @@ std::shared_ptr PCB_REFERENCE_IMAGE::GetEffectiveShape( PCB_LAYER_ID aLay } -void PCB_REFERENCE_IMAGE::SetPosition( const VECTOR2I& aPos ) +VECTOR2I PCB_REFERENCE_IMAGE::GetPosition() const { - const BOX2D newBox = BOX2D::ByCenter( aPos, m_bitmapBase->GetSize() ); - - if( !IsBOX2Safe( newBox ) ) - return; - - m_pos = aPos; + return m_referenceImage.GetPosition(); } -void PCB_REFERENCE_IMAGE::Move( const VECTOR2I& aMoveVector ) -{ - // Defer to SetPosition to check the new position overflow - SetPosition( m_pos + aMoveVector ); -} - - -void PCB_REFERENCE_IMAGE::SetImageScale( double aScale ) +void PCB_REFERENCE_IMAGE::SetPosition( const VECTOR2I& aPos ) { - if( aScale < 0 ) - return; - - const double ratio = aScale / m_bitmapBase->GetScale(); - - const VECTOR2D currentOrigin = m_pos + m_transformOriginOffset; - const VECTOR2D newOffset = m_transformOriginOffset * ratio; - const VECTOR2D newCenter = currentOrigin - newOffset; - const VECTOR2D newSize = m_bitmapBase->GetSize() * ratio; - - // The span of the image is limited to the size of the coordinate system - if( !IsVec2SafeXY( newSize ) ) - return; - - const BOX2D newBox = BOX2D::ByCenter( newCenter, newSize ); - - // Any overflow, just reject the call - if( !IsBOX2Safe( newBox ) ) - return; - - m_bitmapBase->SetScale( aScale ); - SetTransformOriginOffset( KiROUND( newOffset ) ); - // Don't need to recheck the box, we just did that - m_pos = KiROUND( newCenter ); + m_referenceImage.SetPosition( aPos ); } -const VECTOR2I PCB_REFERENCE_IMAGE::GetSize() const +void PCB_REFERENCE_IMAGE::Move( const VECTOR2I& aMoveVector ) { - return m_bitmapBase->GetSize(); + // Defer to SetPosition to check the new position overflow + SetPosition( GetPosition() + aMoveVector ); } void PCB_REFERENCE_IMAGE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) { - VECTOR2I newPos = m_pos; - MIRROR( newPos, aCentre, aFlipDirection ); - - const BOX2D newBox = BOX2D::ByCenter( newPos, m_bitmapBase->GetSize() ); - - if( !IsBOX2Safe( newBox ) ) - return; - - m_pos = newPos; - m_bitmapBase->Mirror( aFlipDirection ); + m_referenceImage.Flip( aCentre, aFlipDirection ); } void PCB_REFERENCE_IMAGE::Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle ) { - EDA_ANGLE norm( aAngle.AsDegrees(), DEGREES_T ); - - RotatePoint( m_pos, aCenter, aAngle ); - - norm.Normalize(); - - // each call to m_bitmapBase->Rotate() rotates 90 degrees CCW - for( double ang = 45.0; ang < norm.AsDegrees(); ang += 90.0 ) - m_bitmapBase->Rotate( false ); + m_referenceImage.Rotate( aCenter, aAngle ); } @@ -268,31 +177,21 @@ void PCB_REFERENCE_IMAGE::Show( int nestLevel, std::ostream& os ) const // XML output: wxString s = GetClass(); - NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str() << m_pos << "/>\n"; + NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str() << m_referenceImage.GetPosition() + << "/>\n"; } #endif bool PCB_REFERENCE_IMAGE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const { - BOX2I rect = GetBoundingBox(); - - rect.Inflate( aAccuracy ); - - return rect.Contains( aPosition ); + return KIGEOM::BoxHitTest( aPosition, GetBoundingBox(), aAccuracy ); } bool PCB_REFERENCE_IMAGE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const { - BOX2I rect = aRect; - - rect.Inflate( aAccuracy ); - - if( aContained ) - return rect.Contains( GetBoundingBox() ); - - return rect.Intersects( GetBoundingBox() ); + return KIGEOM::BoxHitTest( aRect, GetBoundingBox(), aContained, aAccuracy ); } @@ -302,16 +201,20 @@ BITMAPS PCB_REFERENCE_IMAGE::GetMenuImage() const } -void PCB_REFERENCE_IMAGE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, +void PCB_REFERENCE_IMAGE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) { aList.emplace_back( _( "Reference Image" ), wxEmptyString ); - aList.emplace_back( _( "PPI" ), wxString::Format( wxT( "%d "), GetImage()->GetPPI() ) ); - aList.emplace_back( _( "Scale" ), wxString::Format( wxT( "%f "), GetImageScale() ) ); + aList.emplace_back( _( "PPI" ), + wxString::Format( wxT( "%d " ), m_referenceImage.GetImage().GetPPI() ) ); + aList.emplace_back( _( "Scale" ), + wxString::Format( wxT( "%f " ), m_referenceImage.GetImageScale() ) ); - aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( GetSize().x ) ); - aList.emplace_back( _( "Height" ), aFrame->MessageTextFromValue( GetSize().y ) ); + aList.emplace_back( _( "Width" ), + aFrame->MessageTextFromValue( m_referenceImage.GetSize().x ) ); + aList.emplace_back( _( "Height" ), + aFrame->MessageTextFromValue( m_referenceImage.GetSize().y ) ); aList.emplace_back( _( "Layer" ), LayerName( m_layer ) ); } @@ -339,22 +242,7 @@ bool PCB_REFERENCE_IMAGE::operator==( const PCB_REFERENCE_IMAGE& aOther ) const if( m_layer != aOther.m_layer ) return false; - if( m_pos != aOther.m_pos ) - return false; - - if( m_bitmapBase->GetSize() != aOther.m_bitmapBase->GetSize() ) - return false; - - if( m_bitmapBase->GetPPI() != aOther.m_bitmapBase->GetPPI() ) - return false; - - if( m_bitmapBase->GetScale() != aOther.m_bitmapBase->GetScale() ) - return false; - - if( m_bitmapBase->GetImageID() != aOther.m_bitmapBase->GetImageID() ) - return false; - - if( m_bitmapBase->GetImageData() != aOther.m_bitmapBase->GetImageData() ) + if( m_referenceImage != aOther.m_referenceImage ) return false; return true; @@ -373,25 +261,49 @@ double PCB_REFERENCE_IMAGE::Similarity( const BOARD_ITEM& aOther ) const if( m_layer != other.m_layer ) similarity *= 0.9; - if( m_pos != other.m_pos ) - similarity *= 0.9; + similarity *= m_referenceImage.Similarity( other.m_referenceImage ); - if( m_bitmapBase->GetSize() != other.m_bitmapBase->GetSize() ) - similarity *= 0.9; + return similarity; +} - if( m_bitmapBase->GetPPI() != other.m_bitmapBase->GetPPI() ) - similarity *= 0.9; - if( m_bitmapBase->GetScale() != other.m_bitmapBase->GetScale() ) - similarity *= 0.9; +int PCB_REFERENCE_IMAGE::GetTransformOriginOffsetX() const +{ + return m_referenceImage.GetTransformOriginOffset().x; +} - if( m_bitmapBase->GetImageID() != other.m_bitmapBase->GetImageID() ) - similarity *= 0.9; - if( m_bitmapBase->GetImageData() != other.m_bitmapBase->GetImageData() ) - similarity *= 0.9; +void PCB_REFERENCE_IMAGE::SetTransformOriginOffsetX( int aX ) +{ + VECTOR2I offset = m_referenceImage.GetTransformOriginOffset(); + offset.x = aX; + m_referenceImage.SetTransformOriginOffset( offset ); +} - return similarity; + +int PCB_REFERENCE_IMAGE::GetTransformOriginOffsetY() const +{ + return m_referenceImage.GetTransformOriginOffset().y; +} + + +void PCB_REFERENCE_IMAGE::SetTransformOriginOffsetY( int aY ) +{ + VECTOR2I offset = m_referenceImage.GetTransformOriginOffset(); + offset.y = aY; + m_referenceImage.SetTransformOriginOffset( offset ); +} + + +double PCB_REFERENCE_IMAGE::GetImageScale() const +{ + return m_referenceImage.GetImageScale(); +} + + +void PCB_REFERENCE_IMAGE::SetImageScale( double aScale ) +{ + m_referenceImage.SetImageScale( aScale ); } diff --git a/pcbnew/pcb_reference_image.h b/pcbnew/pcb_reference_image.h index cb0e8bd42b..2650d98074 100644 --- a/pcbnew/pcb_reference_image.h +++ b/pcbnew/pcb_reference_image.h @@ -27,6 +27,7 @@ #include #include +#include /** @@ -44,39 +45,11 @@ public: PCB_REFERENCE_IMAGE& operator=( const BOARD_ITEM& aItem ); - const BITMAP_BASE* GetImage() const - { - wxCHECK_MSG( m_bitmapBase != nullptr, nullptr, - wxS( "Invalid PCB_REFERENCE_IMAGE init, m_bitmapBase is NULL." ) ); - return m_bitmapBase; - } - - /** - * Only use this if you really need to modify the underlying image - */ - BITMAP_BASE* MutableImage() const - { - return m_bitmapBase; - } - - /** - * @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_bitmapBase->GetScale(); } - /** - * Set the image "zoom" value. - * - * The image is scaled such that the position of the image's - * transform origin is unchanged. - * - * If the scale is negaive or the image would overflow the - * the coordinate system, nothing is updated. + * @return the underlying reference image object. */ - void SetImageScale( double aScale ); + REFERENCE_IMAGE& GetReferenceImage() { return m_referenceImage; } + const REFERENCE_IMAGE& GetReferenceImage() const { return m_referenceImage; } static inline bool ClassOf( const EDA_ITEM* aItem ) { @@ -85,11 +58,6 @@ public: wxString GetClass() const override { return wxT( "PCB_REFERENCE_IMAGE" ); } - /** - * @return the actual size (in user units, not in pixels) of the image. - */ - const VECTOR2I GetSize() const; - double ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override; const BOX2I GetBoundingBox() const override; @@ -102,26 +70,6 @@ public: /// @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 ); - - /** - * Read and store an image file. - * - * Initialize the bitmap used to draw this item format. - * - * @param aBuf is the memory buffer containing the image file to read. - * @return true if success reading else false. - */ - bool ReadImageFile( wxMemoryBuffer& aBuf ); - void Move( const VECTOR2I& aMoveVector ) override; void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override; @@ -139,7 +87,7 @@ public: /** * Get the position of the image (this is the center of the image). */ - VECTOR2I GetPosition() const override { return m_pos; } + VECTOR2I GetPosition() const override; /** * Set the position of the image. @@ -148,12 +96,6 @@ public: */ void SetPosition( const VECTOR2I& aPosition ) override; - /** - * Get the center of scaling, etc, relative to the image center (GetPosition()). - */ - VECTOR2I GetTransformOriginOffset() const { return m_transformOriginOffset; } - void SetTransformOriginOffset( const VECTOR2I& aCenter ) { m_transformOriginOffset = aCenter; } - bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override; bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override; @@ -168,18 +110,20 @@ public: void Show( int nestLevel, std::ostream& os ) const override; #endif - // Property manager interfaces - int GetTransformOriginOffsetX() const { return m_transformOriginOffset.x; } - void SetTransformOriginOffsetX( int aX ) { m_transformOriginOffset.x = aX; } - int GetTransformOriginOffsetY() const { return m_transformOriginOffset.y; } - void SetTransformOriginOffsetY( int aY ) { m_transformOriginOffset.y = aY; } - protected: void swapData( BOARD_ITEM* aItem ) override; private: - VECTOR2I m_pos; // XY coordinates of center of the bitmap - ///< Center of scaling, etc, relative to the image center - VECTOR2I m_transformOriginOffset; - BITMAP_BASE* m_bitmapBase; // the BITMAP_BASE item + friend struct PCB_REFERENCE_IMAGE_DESC; + + // Property manager interfaces + int GetTransformOriginOffsetX() const; + void SetTransformOriginOffsetX( int aX ); + int GetTransformOriginOffsetY() const; + void SetTransformOriginOffsetY( int aY ); + + double GetImageScale() const; + void SetImageScale( double aScale ); + + REFERENCE_IMAGE m_referenceImage; }; diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 426711b0af..83a7d81079 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -746,7 +746,7 @@ int DRAWING_TOOL::PlaceReferenceImage( const TOOL_EVENT& aEvent ) if( wxFileExists( fullFilename ) ) image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos ); - if( !image || !image->ReadImageFile( fullFilename ) ) + if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) ) { wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) ); delete image; diff --git a/pcbnew/tools/pcb_point_editor.cpp b/pcbnew/tools/pcb_point_editor.cpp index f4c296de2c..d8cbfca4fd 100644 --- a/pcbnew/tools/pcb_point_editor.cpp +++ b/pcbnew/tools/pcb_point_editor.cpp @@ -214,16 +214,18 @@ std::shared_ptr PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem ) { case PCB_REFERENCE_IMAGE_T: { - const PCB_REFERENCE_IMAGE* refImage = static_cast( aItem ); - const VECTOR2I topLeft = refImage->GetPosition() - refImage->GetSize() / 2; - const VECTOR2I botRight = refImage->GetPosition() + refImage->GetSize() / 2; + const REFERENCE_IMAGE& refImage = + static_cast( *aItem ).GetReferenceImage(); + + const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2; + const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2; points->AddPoint( topLeft ); points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) ); points->AddPoint( botRight ); points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) ); - points->AddPoint( refImage->GetPosition() + refImage->GetTransformOriginOffset() ); + points->AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() ); break; } @@ -1361,7 +1363,8 @@ void PCB_POINT_EDITOR::updateItem( BOARD_COMMIT* aCommit ) { case PCB_REFERENCE_IMAGE_T: { - PCB_REFERENCE_IMAGE* bitmap = static_cast( item ); + PCB_REFERENCE_IMAGE& bitmap = static_cast( *item ); + REFERENCE_IMAGE& refImg = bitmap.GetReferenceImage(); const VECTOR2I topLeft = m_editPoints->Point( RECT_TOP_LEFT ).GetPosition(); const VECTOR2I topRight = m_editPoints->Point( RECT_TOP_RIGHT ).GetPosition(); const VECTOR2I botLeft = m_editPoints->Point( RECT_BOT_LEFT ).GetPosition(); @@ -1373,52 +1376,53 @@ void PCB_POINT_EDITOR::updateItem( BOARD_COMMIT* aCommit ) // Moving the transform origin // As the other points didn't move, we can get the image extent from them const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2; - bitmap->SetTransformOriginOffset( newOffset ); + refImg.SetTransformOriginOffset( newOffset ); } else { - const VECTOR2I oldOrigin = bitmap->GetPosition() + bitmap->GetTransformOriginOffset(); - const VECTOR2I oldSize = bitmap->GetSize(); + const VECTOR2I oldOrigin = refImg.GetPosition() + refImg.GetTransformOriginOffset(); + const VECTOR2I oldSize = refImg.GetSize(); + const VECTOR2I pos = refImg.GetPosition(); OPT_VECTOR2I newCorner; - OPT_VECTOR2I oldCorner; + VECTOR2I oldCorner = pos; if( isModified( m_editPoints->Point( RECT_TOP_LEFT ) ) ) { newCorner = topLeft; - oldCorner = ( bitmap->GetPosition() - oldSize / 2 ); + oldCorner -= oldSize / 2; } else if( isModified( m_editPoints->Point( RECT_TOP_RIGHT ) ) ) { newCorner = topRight; - oldCorner = ( bitmap->GetPosition() - VECTOR2I( -oldSize.x, oldSize.y ) / 2 ); + oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2; } else if( isModified( m_editPoints->Point( RECT_BOT_LEFT ) ) ) { newCorner = botLeft; - oldCorner = ( bitmap->GetPosition() - VECTOR2I( oldSize.x, -oldSize.y ) / 2 ); + oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2; } else if( isModified( m_editPoints->Point( RECT_BOT_RIGHT ) ) ) { newCorner = botRight; - oldCorner = ( bitmap->GetPosition() + oldSize / 2 ); + oldCorner += oldSize / 2; } - if( newCorner && oldCorner ) + if( newCorner ) { // Turn in the respective vectors from the origin *newCorner -= xfrmOrigin; - *oldCorner -= oldOrigin; + oldCorner -= oldOrigin; // If we tried to cross the origin, clamp it to stop it - if( sign( newCorner->x ) != sign( oldCorner->x ) - || sign( newCorner->y ) != sign( oldCorner->y ) ) + if( sign( newCorner->x ) != sign( oldCorner.x ) + || sign( newCorner->y ) != sign( oldCorner.y ) ) { *newCorner = VECTOR2I( 0, 0 ); } const double newLength = newCorner->EuclideanNorm(); - const double oldLength = oldCorner->EuclideanNorm(); + const double oldLength = oldCorner.EuclideanNorm(); double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0; @@ -1429,7 +1433,7 @@ void PCB_POINT_EDITOR::updateItem( BOARD_COMMIT* aCommit ) ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y ); // Also handles the origin offset - bitmap->SetImageScale( bitmap->GetImageScale() * ratio ); + refImg.SetImageScale( refImg.GetImageScale() * ratio ); } } @@ -2064,9 +2068,10 @@ void PCB_POINT_EDITOR::updatePoints() { case PCB_REFERENCE_IMAGE_T: { - const PCB_REFERENCE_IMAGE* bitmap = static_cast( item ); - const VECTOR2I topLeft = bitmap->GetPosition() - bitmap->GetSize() / 2; - const VECTOR2I botRight = bitmap->GetPosition() + bitmap->GetSize() / 2; + const REFERENCE_IMAGE& refImg = + static_cast( *item ).GetReferenceImage(); + const VECTOR2I topLeft = refImg.GetPosition() - refImg.GetSize() / 2; + const VECTOR2I botRight = refImg.GetPosition() + refImg.GetSize() / 2; m_editPoints->Point( RECT_TOP_LEFT ).SetPosition( topLeft ); m_editPoints->Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y ); @@ -2074,7 +2079,7 @@ void PCB_POINT_EDITOR::updatePoints() m_editPoints->Point( RECT_BOT_RIGHT ).SetPosition( botRight ); m_editPoints->Point( REFIMG_ORIGIN ) - .SetPosition( bitmap->GetPosition() + bitmap->GetTransformOriginOffset() ); + .SetPosition( refImg.GetPosition() + refImg.GetTransformOriginOffset() ); break; } diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index dbc1301ba6..642b1f7aba 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -3580,8 +3580,8 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector } else if( item->Type() == PCB_REFERENCE_IMAGE_T ) { - VECTOR2D size = static_cast( item )->GetSize(); - area = size.x * size.y; + BOX2I box = item->GetBoundingBox(); + area = (double) box.GetWidth() * box.GetHeight(); } else { diff --git a/qa/tests/pcbnew/test_reference_image_load.cpp b/qa/tests/pcbnew/test_reference_image_load.cpp index fa3dbdf818..aa04c9dc66 100644 --- a/qa/tests/pcbnew/test_reference_image_load.cpp +++ b/qa/tests/pcbnew/test_reference_image_load.cpp @@ -105,18 +105,15 @@ BOOST_FIXTURE_TEST_CASE( ReferenceImageLoading, REFERENCE_IMAGE_LOAD_TEST_FIXTUR const auto& image = static_cast( KI_TEST::RequireBoardItemWithTypeAndId( aBoard, PCB_REFERENCE_IMAGE_T, imageTestCase.m_imageUuid ) ); + const REFERENCE_IMAGE& refImage = image.GetReferenceImage(); BOOST_CHECK_EQUAL( image.IsLocked(), imageTestCase.m_expectedLocked ); BOOST_CHECK_EQUAL( image.GetPosition(), imageTestCase.m_expectedPos * 1000000 ); - BOOST_CHECK_CLOSE( image.GetImageScale(), imageTestCase.m_expectedScale, 1e-6 ); + BOOST_CHECK_CLOSE( refImage.GetImageScale(), imageTestCase.m_expectedScale, 1e-6 ); - const BITMAP_BASE* bitmap = image.GetImage(); + const BITMAP_BASE& bitmap = refImage.GetImage(); - BOOST_REQUIRE( bitmap ); - - BOOST_TEST_MESSAGE( "Got underlying image" ); - - BOOST_CHECK_EQUAL( bitmap->GetSizePixels(), imageTestCase.m_expectedPixelSize ); + BOOST_CHECK_EQUAL( bitmap.GetSizePixels(), imageTestCase.m_expectedPixelSize ); } };