KiCad PCB EDA Suite
Loading...
Searching...
No Matches
reference_image.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "reference_image.h"
25
26#include <string>
27
28#include <wx/debug.h>
29#include <wx/mstream.h>
30
31#include <bitmap_base.h>
33
34
35static bool compareImages( const BITMAP_BASE& aLeft, const BITMAP_BASE& aRight )
36{
37 const wxImage* leftImage = aLeft.GetImageData();
38 const wxImage* rightImage = aRight.GetImageData();
39
40 if( !leftImage || !rightImage )
41 return leftImage == rightImage;
42
43 wxMemoryOutputStream leftStream;
44 wxMemoryOutputStream rightStream;
45
46 if( !aLeft.SaveImageData( leftStream ) || !aRight.SaveImageData( rightStream ) )
47 return false;
48
49 size_t leftSize = leftStream.GetSize();
50 size_t rightSize = rightStream.GetSize();
51
52 if( leftSize != rightSize )
53 return false;
54
55 if( leftSize == 0 )
56 return true;
57
58 std::string leftData( leftSize, '\0' );
59 std::string rightData( rightSize, '\0' );
60
61 leftStream.CopyTo( leftData.data(), leftSize );
62 rightStream.CopyTo( rightData.data(), rightSize );
63
64 return leftData == rightData;
65}
66
67
69 m_iuScale( aIuScale ), m_pos( 0, 0 ), m_transformOriginOffset( 0, 0 ),
70 m_bitmapBase( std::make_unique<BITMAP_BASE>() )
71{
73}
74
75
83
84
88
89
91{
92 const double pixelSizeIu = (double) m_iuScale.MilsToIU( 1000 ) / m_bitmapBase->GetPPI();
93 m_bitmapBase->SetPixelSizeIu( pixelSizeIu );
94}
95
96
98{
99 wxASSERT( m_iuScale.IU_PER_MILS == aOther.m_iuScale.IU_PER_MILS );
100
101 if( &aOther != this )
102 {
103 if( aOther.m_bitmapBase )
104 {
105 m_bitmapBase = std::make_unique<BITMAP_BASE>( *aOther.m_bitmapBase );
106 }
107 m_pos = aOther.m_pos;
110 }
111
112 return *this;
113}
114
115
117{
118 if( m_pos != aOther.m_pos )
119 return false;
120
122 return false;
123
124 if( m_bitmapBase->GetSize() != aOther.m_bitmapBase->GetSize() )
125 return false;
126
127 if( m_bitmapBase->GetPPI() != aOther.m_bitmapBase->GetPPI() )
128 return false;
129
130 if( m_bitmapBase->GetScale() != aOther.m_bitmapBase->GetScale() )
131 return false;
132
133 if( !compareImages( *m_bitmapBase, *aOther.m_bitmapBase ) )
134 return false;
135
136 return true;
137}
138
139
140double REFERENCE_IMAGE::Similarity( const REFERENCE_IMAGE& aOther ) const
141{
142 double similarity = 1.0;
143
144 if( m_pos != aOther.m_pos )
145 similarity *= 0.9;
146
147 if( m_bitmapBase->GetSize() != aOther.m_bitmapBase->GetSize() )
148 similarity *= 0.9;
149
150 if( m_bitmapBase->GetPPI() != aOther.m_bitmapBase->GetPPI() )
151 similarity *= 0.9;
152
153 if( m_bitmapBase->GetScale() != aOther.m_bitmapBase->GetScale() )
154 similarity *= 0.9;
155
156 if( !compareImages( *m_bitmapBase, *aOther.m_bitmapBase ) )
157 similarity *= 0.9;
158
159 return similarity;
160}
161
162
164{
165 return BOX2I::ByCenter( m_pos, m_bitmapBase->GetSize() );
166}
167
168
170{
171 return m_pos;
172}
173
174
176{
177 const BOX2D newBox = BOX2D::ByCenter( aPos, m_bitmapBase->GetSize() );
178
179 if( !IsBOX2Safe( newBox ) )
180 return;
181
182 m_pos = aPos;
183}
184
185
190
191
193{
194 m_transformOriginOffset = aCenter;
195}
196
197
199{
200 return m_bitmapBase->GetSize();
201}
202
203
205{
206 if( aWidth <= 0 )
207 return;
208
209 const double ratio = aWidth / (double) m_bitmapBase->GetSize().x;
210 scaleBy( ratio );
211}
212
213
214void REFERENCE_IMAGE::SetHeight( int aHeight )
215{
216 if( aHeight <= 0 )
217 return;
218
219 const double ratio = aHeight / (double) m_bitmapBase->GetSize().y;
220 scaleBy( ratio );
221}
222
223
225{
226 return m_bitmapBase->GetScale();
227}
228
229
231{
232 if( aScale <= 0 )
233 return;
234
235 const double ratio = aScale / m_bitmapBase->GetScale();
236 scaleBy( ratio );
237}
238
239
240void REFERENCE_IMAGE::scaleBy( double aRatio )
241{
242 if( aRatio <= 0 )
243 return;
244
245 const VECTOR2D currentOrigin = m_pos + m_transformOriginOffset;
246 const VECTOR2D newOffset = m_transformOriginOffset * aRatio;
247 const VECTOR2D newCenter = currentOrigin - newOffset;
248 const VECTOR2D newSize = m_bitmapBase->GetSize() * aRatio;
249
250 // The span of the image is limited to the size of the coordinate system
251 if( !IsVec2SafeXY( newSize ) )
252 return;
253
254 const BOX2D newBox = BOX2D::ByCenter( newCenter, newSize );
255
256 // Any overflow, just reject the call
257 if( !IsBOX2Safe( newBox ) )
258 return;
259
260 m_bitmapBase->SetScale( m_bitmapBase->GetScale() * aRatio );
261 SetTransformOriginOffset( KiROUND( newOffset ) );
262
263 // Don't need to recheck the box, we just did that
264 m_pos = KiROUND( newCenter );
265}
266
267
268void REFERENCE_IMAGE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
269{
270 VECTOR2I newPos = m_pos;
271 MIRROR( newPos, aCentre, aFlipDirection );
272
273 const BOX2D newBox = BOX2D::ByCenter( newPos, m_bitmapBase->GetSize() );
274
275 if( !IsBOX2Safe( newBox ) )
276 return;
277
278 m_pos = newPos;
279 m_bitmapBase->Mirror( aFlipDirection );
280}
281
282
283void REFERENCE_IMAGE::Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle )
284{
285 EDA_ANGLE norm( aAngle.AsDegrees(), DEGREES_T );
286
287 RotatePoint( m_pos, aCenter, aAngle );
288
289 norm.Normalize();
290
291 // each call to m_bitmapBase->Rotate() rotates 90 degrees
292 for( double ang = 45.0; ang < norm.AsDegrees(); ang += 90.0 )
293 m_bitmapBase->Rotate( true );
294}
295
296
297bool REFERENCE_IMAGE::ReadImageFile( const wxString& aFullFilename )
298{
299 if( m_bitmapBase->ReadImageFile( aFullFilename ) )
300 {
302 return true;
303 }
304
305 return false;
306}
307
308
309bool REFERENCE_IMAGE::ReadImageFile( wxMemoryBuffer& aBuffer )
310{
311 if( m_bitmapBase->ReadImageFile( aBuffer ) )
312 {
314 return true;
315 }
316
317 return false;
318}
319
320
321bool REFERENCE_IMAGE::SetImage( const wxImage& aImage )
322{
323 if( m_bitmapBase->SetImage( aImage ) )
324 {
326 return true;
327 }
328
329 return false;
330}
331
332
334{
335 // This cannot be null after construction
336 return *m_bitmapBase;
337}
338
339
344
345
347{
348 std::swap( m_pos, aOther.m_pos );
350 std::swap( m_bitmapBase, aOther.m_bitmapBase );
351}
constexpr bool IsBOX2Safe(const BOX2< Vec > &aInput)
Check if a BOX2 is safe for use with BOX2D (probably BOX2D or BOX2L)
Definition box2.h:949
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BOX2< VECTOR2D > BOX2D
Definition box2.h:923
This class handle bitmap images in KiCad.
Definition bitmap_base.h:49
double GetScale() const
Definition bitmap_base.h:73
bool SaveImageData(wxOutputStream &aOutStream) const
Write the bitmap data to aOutStream.
int GetPPI() const
wxImage * GetImageData()
Definition bitmap_base.h:68
VECTOR2I GetSize() const
static constexpr BOX2< VECTOR2I > ByCenter(const VECTOR2I &aCenter, const SizeVec &aSize)
Definition box2.h:75
EDA_ANGLE Normalize()
Definition eda_angle.h:229
double AsDegrees() const
Definition eda_angle.h:116
BITMAP_BASE & MutableImage() const
Only use this if you really need to modify the underlying image.
const EDA_IU_SCALE & m_iuScale
void SwapData(REFERENCE_IMAGE &aItem)
VECTOR2I m_pos
XY coordinates of center of the bitmap.
void Rotate(const VECTOR2I &aCenter, const EDA_ANGLE &aAngle)
void SetTransformOriginOffset(const VECTOR2I &aCenter)
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
VECTOR2I GetTransformOriginOffset() const
Get the center of scaling, etc, relative to the image center (GetPosition()).
VECTOR2I GetPosition() const
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
bool SetImage(const wxImage &aImage)
Set the image from an existing wxImage.
std::unique_ptr< BITMAP_BASE > m_bitmapBase
REFERENCE_IMAGE & operator=(const REFERENCE_IMAGE &aOther)
void SetHeight(int aHeight)
VECTOR2I GetSize() const
VECTOR2I m_transformOriginOffset
Center of scaling, etc, relative to the image center.
void SetPosition(const VECTOR2I &aPos)
void SetWidth(int aWidth)
const BITMAP_BASE & GetImage() const
Get the underlying image.
double Similarity(const REFERENCE_IMAGE &aOther) const
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
REFERENCE_IMAGE(const EDA_IU_SCALE &aIuScale)
BOX2I GetBoundingBox() const
void scaleBy(double ratio)
bool operator==(const REFERENCE_IMAGE &aOther) const
@ DEGREES_T
Definition eda_angle.h:31
a few functions useful in geometry calculations.
bool IsVec2SafeXY(const VECTOR2< T > &aVec)
Check if both coordinates of a vector are within the limits of the integer type.
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:45
FLIP_DIRECTION
Definition mirror.h:27
STL namespace.
static bool compareImages(const BITMAP_BASE &aLeft, const BITMAP_BASE &aRight)
const double IU_PER_MILS
Definition base_units.h:77
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686