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#include <wx/stream.h> // for wxInputStream, wxOutputStream
34#include <wx/string.h> // for wxString
35#include <wx/wfstream.h> // for wxFileInputStream
36
37
38
40{
41 m_scale = 1.0; // 1.0 = original bitmap size
42 m_imageType = wxBITMAP_TYPE_INVALID;
43 m_bitmap = nullptr;
44 m_image = nullptr;
45 m_originalImage = nullptr;
46 m_ppi = 300; // the bitmap definition. the default is 300PPI
47 m_pixelSizeIu = 254000.0 / m_ppi; // a pixel size value OK for bitmaps using 300 PPI
48 // for Eeschema which uses currently 254000PPI
49 m_isMirroredX = false;
50 m_isMirroredY = false;
52}
53
54
56{
57 m_scale = aSchBitmap.m_scale;
58 m_ppi = aSchBitmap.m_ppi;
59 m_pixelSizeIu = aSchBitmap.m_pixelSizeIu;
60 m_isMirroredX = aSchBitmap.m_isMirroredX;
61 m_isMirroredY = aSchBitmap.m_isMirroredY;
62 m_rotation = aSchBitmap.m_rotation;
63 m_imageType = aSchBitmap.m_imageType;
64
65 m_image = nullptr;
66 m_bitmap = nullptr;
67 m_originalImage = nullptr;
68
69 if( aSchBitmap.m_image )
70 {
71 m_image = new wxImage( *aSchBitmap.m_image );
72 m_bitmap = new wxBitmap( *m_image );
73 m_originalImage = new wxImage( *aSchBitmap.m_originalImage );
74 m_imageType = aSchBitmap.m_imageType;
75 m_imageData = aSchBitmap.m_imageData;
76 m_imageId = aSchBitmap.m_imageId;
77 }
78}
79
80
81void BITMAP_BASE::rebuildBitmap( bool aResetID )
82{
83 if( m_bitmap )
84 delete m_bitmap;
85
86 m_bitmap = new wxBitmap( *m_image );
87
88 if( aResetID )
89 m_imageId = KIID();
90
91}
92
93
95{
96 // Todo: eventually we need to support dpi / scaling in both dimensions
97 int dpiX = m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
98
99 if( dpiX > 1 )
100 {
101 if( m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
102 m_ppi = KiROUND( dpiX * 2.54 );
103 else
104 m_ppi = dpiX;
105 }
106}
107
108
110{
111 *m_image = *aItem->m_image;
112 *m_bitmap = *aItem->m_bitmap;
114 m_imageId = aItem->m_imageId;
115 m_scale = aItem->m_scale;
116 m_ppi = aItem->m_ppi;
120 m_rotation = aItem->m_rotation;
121 m_imageType = aItem->m_imageType;
122 m_imageData = aItem->m_imageData;
123}
124
125
126bool BITMAP_BASE::ReadImageFile( wxInputStream& aInStream )
127{
128 // Store the original image data in m_imageData
129 size_t dataSize = aInStream.GetLength();
130 m_imageData.SetBufSize( dataSize );
131 aInStream.Read( m_imageData.GetData(), dataSize );
132 m_imageData.SetDataLen( dataSize );
133
134 std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
135
136 // Load the image from the stream into new_image
137 wxMemoryInputStream mem_stream( m_imageData.GetData(), dataSize );
138 if( !new_image->LoadFile( mem_stream ) )
139 return false;
140
141 delete m_image;
142 m_imageType = new_image->GetType();
143 m_image = new_image.release();
144
145 // Create a new wxImage object from m_image
146 delete m_originalImage;
147 m_originalImage = new wxImage( *m_image );
148
150 updatePPI();
151
152 return true;
153}
154
155
156bool BITMAP_BASE::ReadImageFile( wxMemoryBuffer& aBuf )
157{
158 // Store the original image data in m_imageData
159 m_imageData = aBuf;
160
161 std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
162
163 // Load the image from the buffer into new_image
164 wxMemoryInputStream mem_stream( m_imageData.GetData(), m_imageData.GetBufSize() );
165
166 if( !new_image->LoadFile( mem_stream ) )
167 return false;
168
169 delete m_image;
170 m_imageType = new_image->GetType();
171 m_image = new_image.release();
172
173 // Create a new wxImage object from m_image
174 delete m_originalImage;
175 m_originalImage = new wxImage( *m_image );
176
178 updatePPI();
179
180 return true;
181}
182
183
184bool BITMAP_BASE::ReadImageFile(const wxString& aFullFilename)
185{
186 wxFileInputStream file_stream(aFullFilename);
187
188 // Check if the file could be opened successfully
189 if (!file_stream.IsOk())
190 return false;
191
192 return ReadImageFile(file_stream);
193}
194
195
196bool BITMAP_BASE::SaveImageData( wxOutputStream& aOutStream ) const
197{
198 if( m_imageData.IsEmpty() )
199 {
200 // If m_imageData is empty, use wxImage::Save() method to write m_image contents to the stream.
201 wxBitmapType type = m_imageType == wxBITMAP_TYPE_JPEG ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG;
202
203 if( !m_image->SaveFile( aOutStream, type ) )
204 {
205 return false;
206 }
207 }
208 else
209 {
210 // Write the contents of m_imageData to the stream.
211 aOutStream.Write( m_imageData.GetData(), m_imageData.GetBufSize() );
212 }
213
214 return true;
215}
216
217
218bool BITMAP_BASE::LoadLegacyData( LINE_READER& aLine, wxString& aErrorMsg )
219{
220 wxMemoryOutputStream stream;
221 char* line;
222
223 while( true )
224 {
225 if( !aLine.ReadLine() )
226 {
227 aErrorMsg = wxT("Unexpected end of data");
228 return false;
229 }
230
231 line = aLine.Line();
232
233 if( strncasecmp( line, "EndData", 4 ) == 0 )
234 {
235 // all the PNG date is read.
236 // We expect here m_image and m_bitmap are void
237 m_image = new wxImage();
238 wxMemoryInputStream istream( stream );
239 m_image->LoadFile( istream, wxBITMAP_TYPE_ANY );
240 m_bitmap = new wxBitmap( *m_image );
241 m_originalImage = new wxImage( *m_image );
243 break;
244 }
245
246 // Read PNG data, stored in hexadecimal,
247 // each byte = 2 hexadecimal digits and a space between 2 bytes
248 // and put it in memory stream buffer
249 int len = strlen( line );
250
251 for( ; len > 0; len -= 3, line += 3 )
252 {
253 int value = 0;
254
255 if( sscanf( line, "%X", &value ) == 1 )
256 stream.PutC( (char) value );
257 else
258 break;
259 }
260 }
261
262 return true;
263}
264
265
267{
268 BOX2I bbox;
269 VECTOR2I size = GetSize();
270
271 bbox.Inflate( size.x / 2, size.y / 2 );
272
273 return bbox;
274}
275
276
277void BITMAP_BASE::DrawBitmap( wxDC* aDC, const VECTOR2I& aPos,
278 const KIGFX::COLOR4D& aBackgroundColor )
279{
280 if( m_bitmap == nullptr )
281 return;
282
283 VECTOR2I pos = aPos;
284 VECTOR2I size = GetSize();
285
286 // This fixes a bug in OSX that should be fixed in the 3.0.3 version or later.
287 if( ( size.x == 0 ) || ( size.y == 0 ) )
288 return;
289
290 // To draw the bitmap, pos is the upper left corner position
291 pos.x -= size.x / 2;
292 pos.y -= size.y / 2;
293
294 double scale;
295 int logicalOriginX, logicalOriginY;
296 aDC->GetUserScale( &scale, &scale );
297 aDC->GetLogicalOrigin( &logicalOriginX, &logicalOriginY );
298
299 // We already have issues to draw a bitmap on the wxDC, depending on wxWidgets version.
300 // Now we have an issue on wxWidgets 3.1.6 to fix the clip area
301 // and the bitmap position when using TransformMatrix
302 // So for version == 3.1.6 do not use it
303 // Be carefull before changing the code.
304 bool useTransform = aDC->CanUseTransformMatrix();
305
306 wxAffineMatrix2D init_matrix = aDC->GetTransformMatrix();
307
308 // Note: clipping bitmap area was made to fix a minor issue in old versions of
309 // Kicad/wxWidgets (5.1 / wx 3.0)
310 // However SetClippingRegion creates a lot of issues (different ways to fix the
311 // position and size of the area, depending on wxWidget version)because it changes with
312 // each versions of wxWigets, so it is now disabled
313 // However the code is still here, just in case
314 // #define USE_CLIP_AREA
315
316 wxPoint clipAreaPos;
317
318 if( useTransform )
319 {
320 wxAffineMatrix2D matrix = aDC->GetTransformMatrix();
321 matrix.Translate( pos.x, pos.y );
322 matrix.Scale( GetScalingFactor(), GetScalingFactor() );
323 aDC->SetTransformMatrix( matrix );
324 // Needed on wx <= 3.1.5, and this is strange...
325 // Nevertheless, this code has problem (the bitmap is not seen)
326 // with wx version > 3.1.5
327 clipAreaPos.x = pos.x;
328 clipAreaPos.y = pos.y;
329
330 pos.x = pos.y = 0;
331 }
332 else
333 {
334 aDC->SetUserScale( scale * GetScalingFactor(), scale * GetScalingFactor() );
335 aDC->SetLogicalOrigin( logicalOriginX / GetScalingFactor(),
336 logicalOriginY / GetScalingFactor() );
337
338 pos.x = KiROUND( pos.x / GetScalingFactor() );
339 pos.y = KiROUND( pos.y / GetScalingFactor() );
340 size.x = KiROUND( size.x / GetScalingFactor() );
341 size.y = KiROUND( size.y / GetScalingFactor() );
342 clipAreaPos.x = pos.x;
343 clipAreaPos.y = pos.y;
344 }
345
346 #ifdef USE_CLIP_AREA
347 aDC->DestroyClippingRegion();
348 aDC->SetClippingRegion( clipAreaPos, wxSize( size.x, size.y ) );
349 #endif
350
351 if( aBackgroundColor != COLOR4D::UNSPECIFIED && m_bitmap->HasAlpha() )
352 {
353 // Most printers don't support transparent images properly,
354 // so blend the image with background color.
355
356 int w = m_bitmap->GetWidth();
357 int h = m_bitmap->GetHeight();
358
359 wxImage image( w, h );
360 wxColour bgColor = aBackgroundColor.ToColour();
361
362 image.SetRGB( wxRect( 0, 0, w, h ), bgColor.Red(), bgColor.Green(), bgColor.Blue() );
363 image.Paste( m_bitmap->ConvertToImage(), 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE );
364
366 image = image.ConvertToGreyscale();
367
368 aDC->DrawBitmap( wxBitmap( image ), pos.x, pos.y, true );
369 }
370 else if( GetGRForceBlackPenState() )
371 {
372 wxBitmap result( m_bitmap->ConvertToImage().ConvertToGreyscale() );
373 aDC->DrawBitmap( result, pos.x, pos.y, true );
374 }
375 else
376 {
377 aDC->DrawBitmap( *m_bitmap, pos.x, pos.y, true );
378 }
379
380 if( useTransform )
381 aDC->SetTransformMatrix( init_matrix );
382 else
383 {
384 aDC->SetUserScale( scale, scale );
385 aDC->SetLogicalOrigin( logicalOriginX, logicalOriginY );
386 }
387
388 #ifdef USE_CLIP_AREA
389 aDC->DestroyClippingRegion();
390 #endif
391}
392
393
395{
396 VECTOR2I size;
397
398 if( m_bitmap )
399 {
400 size.x = m_bitmap->GetWidth();
401 size.y = m_bitmap->GetHeight();
402
403 size.x = KiROUND( size.x * GetScalingFactor() );
404 size.y = KiROUND( size.y * GetScalingFactor() );
405 }
406
407 return size;
408}
409
410
411void BITMAP_BASE::Mirror( bool aVertically )
412{
413 if( m_image )
414 {
415 // wxImage::Mirror() clear some parameters of the original image.
416 // We need to restore them, especially resolution and unit, to be
417 // sure image parameters saved in file are the right parameters, not
418 // the defualt values
419 int resX = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
420 int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
421 int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
422
423 *m_image = m_image->Mirror( not aVertically );
424
425 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
426 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
427 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
428
429 if( aVertically )
431 else
433
434 rebuildBitmap( false );
436 }
437}
438
439
440void BITMAP_BASE::Rotate( bool aRotateCCW )
441{
442 if( m_image )
443 {
444 // wxImage::Rotate90() clear some parameters of the original image.
445 // We need to restore them, especially resolution and unit, to be
446 // sure image parameters saved in file are the right parameters, not
447 // the defualt values
448 int resX = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
449 int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
450 int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
451
452 *m_image = m_image->Rotate90( aRotateCCW );
453
454 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
455 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
456 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
457
458 m_rotation += ( aRotateCCW ? ANGLE_90 : -ANGLE_90 );
459 rebuildBitmap( false );
461 }
462}
463
464
466{
467 if( m_image )
468 {
469 *m_image = m_image->ConvertToGreyscale();
470 *m_originalImage = m_originalImage->ConvertToGreyscale();
473 }
474}
475
476
477void BITMAP_BASE::PlotImage( PLOTTER* aPlotter, const VECTOR2I& aPos,
478 const COLOR4D& aDefaultColor,
479 int aDefaultPensize ) const
480{
481 if( m_image == nullptr )
482 return;
483
484 // These 2 lines are useful only for plotters that cannot plot a bitmap
485 // and plot a rectangle instead of.
486 aPlotter->SetColor( aDefaultColor );
487 aPlotter->SetCurrentLineWidth( aDefaultPensize );
488 aPlotter->PlotImage( *m_image, aPos, GetScalingFactor() );
489}
490
491
493{
494 if( m_image )
495 {
496 wxMemoryOutputStream stream;
497 wxBitmapType type = m_imageType == wxBITMAP_TYPE_JPEG ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG;
498
499 if( !m_image->SaveFile( stream, type ) )
500 return;
501
502 m_imageData.GetWriteBuf( stream.GetLength() );
503 stream.CopyTo( m_imageData.GetData(), stream.GetLength() );
504 m_imageData.SetDataLen( stream.GetLength() );
505 }
506}
This class handle bitmap images in KiCad.
Definition: bitmap_base.h:48
wxMemoryBuffer m_imageData
Definition: bitmap_base.h:261
void Rotate(bool aRotateCCW)
Rotate image CW or CCW.
bool LoadLegacyData(LINE_READER &aLine, wxString &aErrorMsg)
Load an image data saved by #SaveData.
double GetScalingFactor() const
This scaling factor depends on m_pixelSizeIu and m_scale.
Definition: bitmap_base.h:93
void PlotImage(PLOTTER *aPlotter, const VECTOR2I &aPos, const KIGFX::COLOR4D &aDefaultColor, int aDefaultPensize) const
Plot bitmap on plotter.
void updatePPI()
Definition: bitmap_base.cpp:94
const BOX2I GetBoundingBox() const
Return the orthogonal, bounding box of this object for display purposes.
bool m_isMirroredY
Definition: bitmap_base.h:274
bool m_isMirroredX
Definition: bitmap_base.h:273
void Mirror(bool aVertically)
Mirror image vertically (i.e.
KIID m_imageId
Definition: bitmap_base.h:272
bool SaveImageData(wxOutputStream &aOutStream) const
Write the bitmap data to aOutStream.
wxImage * m_originalImage
Definition: bitmap_base.h:265
void rebuildBitmap(bool aResetID=true)
Definition: bitmap_base.cpp:81
void DrawBitmap(wxDC *aDC, const VECTOR2I &aPos, const KIGFX::COLOR4D &aBackgroundColor=KIGFX::COLOR4D::UNSPECIFIED)
wxBitmapType m_imageType
Definition: bitmap_base.h:262
wxBitmap * m_bitmap
Definition: bitmap_base.h:266
void ImportData(BITMAP_BASE *aItem)
Copy aItem image to this object and update m_bitmap.
void ConvertToGreyscale()
void UpdateImageDataBuffer()
Resets the image data buffer using the current image data.
bool ReadImageFile(const wxString &aFullFilename)
Reads and stores in memory an image file.
wxImage * m_image
Definition: bitmap_base.h:264
double m_scale
Definition: bitmap_base.h:259
double m_pixelSizeIu
Definition: bitmap_base.h:267
BITMAP_BASE(const VECTOR2I &pos=VECTOR2I(0, 0))
Definition: bitmap_base.cpp:39
VECTOR2I GetSize() const
EDA_ANGLE m_rotation
Definition: bitmap_base.h:275
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:541
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
wxColour ToColour() const
Definition: color4d.cpp:220
Definition: kiid.h:49
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:105
virtual void PlotImage(const wxImage &aImage, const VECTOR2I &aPos, double aScaleFactor)
Only PostScript plotters can plot bitmaps.
Definition: plotter.cpp:259
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_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
bool GetGRForceBlackPenState(void)
Definition: gr_basic.cpp:165
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:118