KiCad PCB EDA Suite
Loading...
Searching...
No Matches
bitmap_base.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 (C) 2017 jean-pierre.charras
5 * Copyright (C) 2011-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <bitmap_base.h>
26#include <gr_basic.h>
27#include <math/util.h> // for KiROUND
28#include <memory> // for make_unique, unique_ptr
29#include <plotters/plotter.h>
30#include <richio.h>
31#include <wx/bitmap.h> // for wxBitmap
32#include <wx/mstream.h>
33
34
36{
37 m_scale = 1.0; // 1.0 = original bitmap size
38 m_bitmap = nullptr;
39 m_image = nullptr;
40 m_originalImage = nullptr;
41 m_ppi = 300; // the bitmap definition. the default is 300PPI
42 m_pixelSizeIu = 254000.0 / m_ppi; // a pixel size value OK for bitmaps using 300 PPI
43 // for Eeschema which uses currently 254000PPI
44 m_isMirroredX = false;
45 m_isMirroredY = false;
47}
48
49
51{
52 m_scale = aSchBitmap.m_scale;
53 m_ppi = aSchBitmap.m_ppi;
54 m_pixelSizeIu = aSchBitmap.m_pixelSizeIu;
55 m_isMirroredX = aSchBitmap.m_isMirroredX;
56 m_isMirroredY = aSchBitmap.m_isMirroredY;
57 m_rotation = aSchBitmap.m_rotation;
58
59 m_image = nullptr;
60 m_bitmap = nullptr;
61 m_originalImage = nullptr;
62
63 if( aSchBitmap.m_image )
64 {
65 m_image = new wxImage( *aSchBitmap.m_image );
66 m_bitmap = new wxBitmap( *m_image );
67 m_originalImage = new wxImage( *aSchBitmap.m_originalImage );
68 m_imageId = aSchBitmap.m_imageId;
69 }
70}
71
72
73void BITMAP_BASE::SetImage( wxImage* aImage )
74{
75 delete m_image;
76 m_image = aImage;
77 delete m_originalImage;
78 m_originalImage = new wxImage( *aImage );
80 updatePPI();
81}
82
83
84void BITMAP_BASE::rebuildBitmap( bool aResetID )
85{
86 if( m_bitmap )
87 delete m_bitmap;
88
89 m_bitmap = new wxBitmap( *m_image );
90
91 if( aResetID )
92 m_imageId = KIID();
93}
94
95
97{
98 // Todo: eventually we need to support dpi / scaling in both dimensions
99 int dpiX = m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
100
101 if( dpiX > 1 )
102 {
103 if( m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
104 m_ppi = KiROUND( dpiX * 2.54 );
105 else
106 m_ppi = dpiX;
107 }
108}
109
110
112{
113 *m_image = *aItem->m_image;
114 *m_bitmap = *aItem->m_bitmap;
116 m_imageId = aItem->m_imageId;
117 m_scale = aItem->m_scale;
118 m_ppi = aItem->m_ppi;
122 m_rotation = aItem->m_rotation;
123}
124
125
126bool BITMAP_BASE::ReadImageFile( wxInputStream& aInStream )
127{
128 std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
129
130 if( !new_image->LoadFile( aInStream ) )
131 return false;
132
133 delete m_image;
134 m_image = new_image.release();
135 delete m_originalImage;
136 m_originalImage = new wxImage( *m_image );
138 updatePPI();
139
140 return true;
141}
142
143
144bool BITMAP_BASE::ReadImageFile( const wxString& aFullFilename )
145{
146 wxImage* new_image = new wxImage();
147
148 if( !new_image->LoadFile( aFullFilename ) )
149 {
150 delete new_image;
151 return false;
152 }
153
154 delete m_image;
155 m_image = new_image;
156 delete m_originalImage;
157 m_originalImage = new wxImage( *m_image );
159 updatePPI();
160
161 return true;
162}
163
164
165bool BITMAP_BASE::SaveData( FILE* aFile ) const
166{
167 if( m_image )
168 {
169 wxMemoryOutputStream stream;
170 m_image->SaveFile( stream, wxBITMAP_TYPE_PNG );
171
172 // Write binary data in hexadecimal form (ASCII)
173 wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
174 char* begin = (char*) buffer->GetBufferStart();
175
176 for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
177 {
178 if( ii >= 32 )
179 {
180 ii = 0;
181
182 if( fprintf( aFile, "\n" ) == EOF )
183 return false;
184 }
185
186 if( fprintf( aFile, "%2.2X ", *begin & 0xFF ) == EOF )
187 return false;
188 }
189 }
190
191 return true;
192}
193
194
195void BITMAP_BASE::SaveData( wxArrayString& aPngStrings ) const
196{
197 if( m_image )
198 {
199 wxMemoryOutputStream stream;
200 m_image->SaveFile( stream, wxBITMAP_TYPE_PNG );
201
202 // Write binary data in hexadecimal form (ASCII)
203 wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
204 char* begin = (char*) buffer->GetBufferStart();
205 wxString line;
206
207 for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
208 {
209 if( ii >= 32 )
210 {
211 ii = 0;
212 aPngStrings.Add( line );
213 line.Empty();
214 }
215
216 line << wxString::Format( wxT( "%2.2X " ), *begin & 0xFF );
217 }
218
219 // Add last line:
220 if( !line.IsEmpty() )
221 aPngStrings.Add( line );
222 }
223}
224
225
226bool BITMAP_BASE::LoadData( LINE_READER& aLine, wxString& aErrorMsg )
227{
228 wxMemoryOutputStream stream;
229 char* line;
230
231 while( true )
232 {
233 if( !aLine.ReadLine() )
234 {
235 aErrorMsg = wxT("Unexpected end of data");
236 return false;
237 }
238
239 line = aLine.Line();
240
241 if( strncasecmp( line, "EndData", 4 ) == 0 )
242 {
243 // all the PNG date is read.
244 // We expect here m_image and m_bitmap are void
245 m_image = new wxImage();
246 wxMemoryInputStream istream( stream );
247 m_image->LoadFile( istream, wxBITMAP_TYPE_PNG );
248 m_bitmap = new wxBitmap( *m_image );
249 m_originalImage = new wxImage( *m_image );
250 break;
251 }
252
253 // Read PNG data, stored in hexadecimal,
254 // each byte = 2 hexadecimal digits and a space between 2 bytes
255 // and put it in memory stream buffer
256 int len = strlen( line );
257
258 for( ; len > 0; len -= 3, line += 3 )
259 {
260 int value = 0;
261
262 if( sscanf( line, "%X", &value ) == 1 )
263 stream.PutC( (char) value );
264 else
265 break;
266 }
267 }
268
269 return true;
270}
271
272
274{
275 BOX2I bbox;
276 VECTOR2I size = GetSize();
277
278 bbox.Inflate( size.x / 2, size.y / 2 );
279
280 return bbox;
281}
282
283
284void BITMAP_BASE::DrawBitmap( wxDC* aDC, const VECTOR2I& aPos )
285{
286 if( m_bitmap == nullptr )
287 return;
288
289 VECTOR2I pos = aPos;
290 VECTOR2I size = GetSize();
291
292 // This fixes a bug in OSX that should be fixed in the 3.0.3 version or later.
293 if( ( size.x == 0 ) || ( size.y == 0 ) )
294 return;
295
296 // To draw the bitmap, pos is the upper left corner position
297 pos.x -= size.x / 2;
298 pos.y -= size.y / 2;
299
300 double scale;
301 int logicalOriginX, logicalOriginY;
302 aDC->GetUserScale( &scale, &scale );
303 aDC->GetLogicalOrigin( &logicalOriginX, &logicalOriginY );
304
305 // We already have issues to draw a bitmap on the wxDC, depending on wxWidgets version.
306 // Now we have an issue on wxWidgets 3.1.6 to fix the clip area
307 // and the bitmap position when using TransformMatrix
308 // So for version == 3.1.6 do not use it
309 // Be carefull before changing the code.
310 bool useTransform = aDC->CanUseTransformMatrix();
311
312 #if wxCHECK_VERSION( 3, 1, 6 ) && !wxCHECK_VERSION( 3, 1, 7 )
313 useTransform = false;
314 #endif
315
316 wxAffineMatrix2D init_matrix = aDC->GetTransformMatrix();
317
318 // Note: clipping bitmap area was made to fix a minor issue in old versions of
319 // Kicad/wxWidgets (5.1 / wx 3.0)
320 // However SetClippingRegion creates a lot of issues (different ways to fix the
321 // position and size of the area, depending on wxWidget version)because it changes with
322 // each versions of wxWigets, so it is now disabled
323 // However the code is still here, just in case
324 // #define USE_CLIP_AREA
325
326 wxPoint clipAreaPos;
327
328 if( useTransform )
329 {
330 wxAffineMatrix2D matrix = aDC->GetTransformMatrix();
331 matrix.Translate( pos.x, pos.y );
332 matrix.Scale( GetScalingFactor(), GetScalingFactor() );
333 aDC->SetTransformMatrix( matrix );
334 // Needed on wx <= 3.1.5, and this is strange...
335 // Nevertheless, this code has problem (the bitmap is not seen)
336 // with wx version > 3.1.5
337 clipAreaPos.x = pos.x;
338 clipAreaPos.y = pos.y;
339
340 pos.x = pos.y = 0;
341 }
342 else
343 {
344 aDC->SetUserScale( scale * GetScalingFactor(), scale * GetScalingFactor() );
345 aDC->SetLogicalOrigin( logicalOriginX / GetScalingFactor(),
346 logicalOriginY / GetScalingFactor() );
347
348 pos.x = KiROUND( pos.x / GetScalingFactor() );
349 pos.y = KiROUND( pos.y / GetScalingFactor() );
350 size.x = KiROUND( size.x / GetScalingFactor() );
351 size.y = KiROUND( size.y / GetScalingFactor() );
352 clipAreaPos.x = pos.x;
353 clipAreaPos.y = pos.y;
354 }
355
356 #ifdef USE_CLIP_AREA
357 aDC->DestroyClippingRegion();
358 aDC->SetClippingRegion( clipAreaPos, wxSize( size.x, size.y ) );
359 #endif
360
362 {
363 wxBitmap result( m_bitmap->ConvertToImage().ConvertToGreyscale() );
364 aDC->DrawBitmap( result, pos.x, pos.y, true );
365 }
366 else
367 {
368 aDC->DrawBitmap( *m_bitmap, pos.x, pos.y, true );
369 }
370
371 if( useTransform )
372 aDC->SetTransformMatrix( init_matrix );
373 else
374 {
375 aDC->SetUserScale( scale, scale );
376 aDC->SetLogicalOrigin( logicalOriginX, logicalOriginY );
377 }
378
379 #ifdef USE_CLIP_AREA
380 aDC->DestroyClippingRegion();
381 #endif
382}
383
384
386{
387 VECTOR2I size;
388
389 if( m_bitmap )
390 {
391 size.x = m_bitmap->GetWidth();
392 size.y = m_bitmap->GetHeight();
393
394 size.x = KiROUND( size.x * GetScalingFactor() );
395 size.y = KiROUND( size.y * GetScalingFactor() );
396 }
397
398 return size;
399}
400
401
402void BITMAP_BASE::Mirror( bool aVertically )
403{
404 if( m_image )
405 {
406 // wxImage::Mirror() clear some parameters of the original image.
407 // We need to restore them, especially resolution and unit, to be
408 // sure image parameters saved in file are the right parameters, not
409 // the defualt values
410 int resX = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
411 int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
412 int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
413
414 *m_image = m_image->Mirror( not aVertically );
415
416 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
417 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
418 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
419
420 if( aVertically )
422 else
424
425 rebuildBitmap( false );
426 }
427}
428
429
430void BITMAP_BASE::Rotate( bool aRotateCCW )
431{
432 if( m_image )
433 {
434 // wxImage::Rotate90() clear some parameters of the original image.
435 // We need to restore them, especially resolution and unit, to be
436 // sure image parameters saved in file are the right parameters, not
437 // the defualt values
438 int resX = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
439 int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
440 int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
441
442 *m_image = m_image->Rotate90( aRotateCCW );
443
444 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
445 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
446 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
447
448 m_rotation += ( aRotateCCW ? -ANGLE_90 : ANGLE_90 );
449 rebuildBitmap( false );
450 }
451}
452
453
455{
456 if( m_image )
457 {
458 *m_image = m_image->ConvertToGreyscale();
459 *m_originalImage = m_originalImage->ConvertToGreyscale();
461 }
462}
463
464
465void BITMAP_BASE::PlotImage( PLOTTER* aPlotter, const VECTOR2I& aPos,
466 const COLOR4D& aDefaultColor,
467 int aDefaultPensize ) const
468{
469 if( m_image == nullptr )
470 return;
471
472 // These 2 lines are useful only for plotters that cannot plot a bitmap
473 // and plot a rectangle instead of.
474 aPlotter->SetColor( aDefaultColor );
475 aPlotter->SetCurrentLineWidth( aDefaultPensize );
476 aPlotter->PlotImage( *m_image, aPos, GetScalingFactor() );
477}
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:52
void Rotate(bool aRotateCCW)
Rotate image CW or CCW.
double GetScalingFactor() const
This scaling factor depends on m_pixelSizeIu and m_scale.
Definition: bitmap_base.h:99
bool SaveData(FILE *aFile) const
Write the bitmap data to aFile.
void PlotImage(PLOTTER *aPlotter, const VECTOR2I &aPos, const KIGFX::COLOR4D &aDefaultColor, int aDefaultPensize) const
Plot bitmap on plotter.
void updatePPI()
Definition: bitmap_base.cpp:96
const BOX2I GetBoundingBox() const
Return the orthogonal, bounding box of this object for display purposes.
bool m_isMirroredY
Definition: bitmap_base.h:254
bool m_isMirroredX
Definition: bitmap_base.h:253
void Mirror(bool aVertically)
Mirror image vertically (i.e.
KIID m_imageId
Definition: bitmap_base.h:252
wxImage * m_originalImage
Definition: bitmap_base.h:245
void rebuildBitmap(bool aResetID=true)
Definition: bitmap_base.cpp:84
wxBitmap * m_bitmap
Definition: bitmap_base.h:246
void ImportData(BITMAP_BASE *aItem)
Copy aItem image to this object and update m_bitmap.
void ConvertToGreyscale()
void DrawBitmap(wxDC *aDC, const VECTOR2I &aPos)
void SetImage(wxImage *aImage)
Definition: bitmap_base.cpp:73
bool ReadImageFile(const wxString &aFullFilename)
Reads and stores in memory an image file.
wxImage * m_image
Definition: bitmap_base.h:244
double m_scale
Definition: bitmap_base.h:242
double m_pixelSizeIu
Definition: bitmap_base.h:247
bool LoadData(LINE_READER &aLine, wxString &aErrorMsg)
Load an image data saved by SaveData.
BITMAP_BASE(const VECTOR2I &pos=VECTOR2I(0, 0))
Definition: bitmap_base.cpp:35
VECTOR2I GetSize() const
EDA_ANGLE m_rotation
Definition: bitmap_base.h:255
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
Definition: kiid.h:48
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:93
virtual char * ReadLine()=0
Read a line of text into the buffer and increments the line number counter.
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:129
Base plotter engine class.
Definition: plotter.h:110
virtual void PlotImage(const wxImage &aImage, const VECTOR2I &aPos, double aScaleFactor)
Only PostScript plotters can plot bitmaps.
Definition: plotter.cpp:254
virtual void SetCurrentLineWidth(int width, void *aData=nullptr)=0
Set the line width for the next drawing.
virtual void SetColor(const COLOR4D &color)=0
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:431
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:429
bool GetGRForceBlackPenState(void)
Definition: gr_basic.cpp:165
Plot settings, and plotting engines (PostScript, Gerber, HPGL and DXF)
const int scale
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85