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