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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "bitmap_base.h"
22
23#include <algorithm> // for std::swap
24#include <cstring> // for memcpy
25#include <gr_basic.h>
26#include <math/util.h> // for KiROUND
27#include <memory> // for make_unique, unique_ptr
28#include <plotters/plotter.h>
29#include <render_utils.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_bitmapDirty = false;
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_bitmapDirty = false;
69 m_originalImage = nullptr;
70
71 if( aSchBitmap.m_image )
72 {
73 m_image = new wxImage( *aSchBitmap.m_image );
74 m_bitmap = new wxBitmap( *m_image );
75 m_originalImage = new wxImage( *aSchBitmap.m_originalImage );
76 m_imageType = aSchBitmap.m_imageType;
77 m_imageData = aSchBitmap.m_imageData;
78 m_imageId = aSchBitmap.m_imageId;
79 }
80}
81
82
83void BITMAP_BASE::rebuildBitmap( bool aResetID )
84{
85 if( m_bitmap )
86 delete m_bitmap;
87
88 m_bitmap = new wxBitmap( *m_image );
89 m_bitmapDirty = false;
90
91 if( aResetID )
92 m_imageId = KIID();
93
94}
95
96
98{
99 if( m_bitmapDirty && m_image )
100 {
101 if( m_bitmap )
102 delete m_bitmap;
103
104 m_bitmap = new wxBitmap( *m_image );
105 m_bitmapDirty = false;
106 }
107}
108
109
111{
112 // Todo: eventually we need to support dpi / scaling in both dimensions
113 //
114 // PNG stores resolution as pixels-per-meter in the pHYs chunk. wxWidgets converts this
115 // to pixels-per-cm as a floating-point string (e.g., 3780 PPM -> "37.8" px/cm).
116 // GetOptionInt() would truncate "37.8" to 37 before the * 2.54 multiply, causing ~2%
117 // error for common resolutions. ToCDouble is locale-independent unlike wxAtof.
118 wxString resStr = m_originalImage->GetOption( wxIMAGE_OPTION_RESOLUTIONX );
119 double dpiX = 0.0;
120 resStr.ToCDouble( &dpiX );
121
122 if( dpiX > 1.0 )
123 {
124 if( m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
125 m_ppi = KiROUND( dpiX * 2.54 );
126 else
127 m_ppi = KiROUND( dpiX );
128 }
129}
130
131
133{
134 if( !m_originalImage )
135 return m_ppi;
136
137 // Mirror the pre-fix path, which read pixels/cm via GetOptionInt() and so truncated
138 // "37.8" to 37 before the * 2.54 multiply.
139 int dpiX = m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONX );
140
141 if( dpiX > 1 )
142 {
143 if( m_originalImage->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT ) == wxIMAGE_RESOLUTION_CM )
144 return KiROUND( dpiX * 2.54 );
145 else
146 return dpiX;
147 }
148
149 return m_ppi;
150}
151
152
154{
155 *m_image = *aItem.m_image;
157 m_imageId = aItem.m_imageId;
158 m_scale = aItem.m_scale;
159 m_ppi = aItem.m_ppi;
163 m_rotation = aItem.m_rotation;
164 m_imageType = aItem.m_imageType;
165 m_imageData = aItem.m_imageData;
166
167 rebuildBitmap( false );
168}
169
170
171bool BITMAP_BASE::ReadImageFile( wxInputStream& aInStream )
172{
173 // Store the original image data in m_imageData
174 size_t dataSize = aInStream.GetLength();
175 m_imageData.SetBufSize( dataSize );
176 aInStream.Read( m_imageData.GetData(), dataSize );
177 m_imageData.SetDataLen( dataSize );
178
179 std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
180
181 // Load the image from the stream into new_image
182 wxMemoryInputStream mem_stream( m_imageData.GetData(), dataSize );
183 if( !new_image->LoadFile( mem_stream ) )
184 return false;
185
186 return SetImage( *new_image );
187}
188
189
190bool BITMAP_BASE::ReadImageFile( wxMemoryBuffer& aBuf )
191{
192 // Store the original image data in m_imageData
193 m_imageData = aBuf;
194
195 std::unique_ptr<wxImage> new_image = std::make_unique<wxImage>();
196
197 // Load the image from the buffer into new_image
198 wxMemoryInputStream mem_stream( m_imageData.GetData(), m_imageData.GetBufSize() );
199
200 if( !new_image->LoadFile( mem_stream ) )
201 return false;
202
203 return SetImage( *new_image );
204}
205
206
207bool BITMAP_BASE::ReadImageFile(const wxString& aFullFilename)
208{
209 wxFileInputStream file_stream(aFullFilename);
210
211 // Check if the file could be opened successfully
212 if (!file_stream.IsOk())
213 return false;
214
215 return ReadImageFile(file_stream);
216}
217
218
219bool BITMAP_BASE::SetImage( const wxImage& aImage )
220{
221 if( !aImage.IsOk() || aImage.GetWidth() == 0 || aImage.GetHeight() == 0 )
222 return false;
223
224 delete m_image;
225 m_image = new wxImage( aImage );
226
227 // Create a new wxImage object from m_image
228 delete m_originalImage;
229 m_originalImage = new wxImage( *m_image );
230
232 updatePPI();
233
234 return true;
235}
236
237
238bool BITMAP_BASE::SaveImageData( wxOutputStream& aOutStream ) const
239{
240 if( m_imageData.IsEmpty() )
241 {
242 // If m_imageData is empty, use wxImage::Save() method to write m_image contents to
243 // the stream.
244 wxBitmapType type = m_imageType == wxBITMAP_TYPE_JPEG ? wxBITMAP_TYPE_JPEG
245 : wxBITMAP_TYPE_PNG;
246
247 if( !m_image->SaveFile( aOutStream, type ) )
248 {
249 return false;
250 }
251 }
252 else
253 {
254 // Write the contents of m_imageData to the stream.
255 aOutStream.Write( m_imageData.GetData(), m_imageData.GetDataLen() );
256 }
257
258 return true;
259}
260
261
262bool BITMAP_BASE::LoadLegacyData( LINE_READER& aLine, wxString& aErrorMsg )
263{
264 wxMemoryOutputStream stream;
265 char* line;
266
267 while( true )
268 {
269 if( !aLine.ReadLine() )
270 {
271 aErrorMsg = wxT( "Unexpected end of data" );
272 return false;
273 }
274
275 line = aLine.Line();
276
277 if( strncasecmp( line, "EndData", 4 ) == 0 )
278 {
279 // all the PNG date is read.
280 // We expect here m_image and m_bitmap are void
281 m_image = new wxImage();
282 wxMemoryInputStream istream( stream );
283 m_image->LoadFile( istream, wxBITMAP_TYPE_ANY );
284 m_bitmap = new wxBitmap( *m_image );
285 m_originalImage = new wxImage( *m_image );
287 break;
288 }
289
290 // Read PNG data, stored in hexadecimal,
291 // each byte = 2 hexadecimal digits and a space between 2 bytes
292 // and put it in memory stream buffer
293 int len = strlen( line );
294
295 for( ; len > 0; len -= 3, line += 3 )
296 {
297 int value = 0;
298
299 if( sscanf( line, "%X", &value ) == 1 )
300 stream.PutC( (char) value );
301 else
302 break;
303 }
304 }
305
306 return true;
307}
308
309
311{
312 BOX2I bbox;
313 VECTOR2I size = GetSize();
314
315 bbox.Inflate( size.x / 2, size.y / 2 );
316
317 return bbox;
318}
319
320
321void BITMAP_BASE::DrawBitmap( wxDC* aDC, const VECTOR2I& aPos,
322 const KIGFX::COLOR4D& aBackgroundColor ) const
323{
325
326 if( m_bitmap == nullptr )
327 return;
328
329 VECTOR2I pos = aPos;
330 VECTOR2I size = GetSize();
331
332 // This fixes a bug in OSX that should be fixed in the 3.0.3 version or later.
333 if( ( size.x == 0 ) || ( size.y == 0 ) )
334 return;
335
336 // To draw the bitmap, pos is the upper left corner position
337 pos.x -= size.x / 2;
338 pos.y -= size.y / 2;
339
340 double scale;
341 int logicalOriginX, logicalOriginY;
342 aDC->GetUserScale( &scale, &scale );
343 aDC->GetLogicalOrigin( &logicalOriginX, &logicalOriginY );
344
345 // We already have issues to draw a bitmap on the wxDC, depending on wxWidgets version.
346 // Now we have an issue on wxWidgets 3.1.6 to fix the clip area
347 // and the bitmap position when using TransformMatrix
348 // So for version == 3.1.6 do not use it
349 // Be careful before changing the code.
350 bool useTransform = aDC->CanUseTransformMatrix();
351
352 wxAffineMatrix2D init_matrix = aDC->GetTransformMatrix();
353
354 // Note: clipping bitmap area was made to fix a minor issue in old versions of
355 // KiCad/wxWidgets (5.1 / wx 3.0)
356 // However SetClippingRegion creates a lot of issues (different ways to fix the
357 // position and size of the area, depending on wxWidgets version)because it changes with
358 // each versions of wxWidgets, so it is now disabled
359 // However the code is still here, just in case
360 // #define USE_CLIP_AREA
361
362 wxPoint clipAreaPos;
363
364 if( useTransform )
365 {
366 wxAffineMatrix2D matrix = aDC->GetTransformMatrix();
367 matrix.Translate( pos.x, pos.y );
368 matrix.Scale( GetScalingFactor(), GetScalingFactor() );
369 aDC->SetTransformMatrix( matrix );
370
371 // Needed on wx <= 3.1.5, and this is strange...
372 // Nevertheless, this code has problem (the bitmap is not seen)
373 // with wx version > 3.1.5
374 clipAreaPos.x = pos.x;
375 clipAreaPos.y = pos.y;
376
377 pos.x = pos.y = 0;
378 }
379 else
380 {
381 aDC->SetUserScale( scale * GetScalingFactor(), scale * GetScalingFactor() );
382 aDC->SetLogicalOrigin( logicalOriginX / GetScalingFactor(),
383 logicalOriginY / GetScalingFactor() );
384
385 pos.x = KiROUND( pos.x / GetScalingFactor() );
386 pos.y = KiROUND( pos.y / GetScalingFactor() );
387 size.x = KiROUND( size.x / GetScalingFactor() );
388 size.y = KiROUND( size.y / GetScalingFactor() );
389 clipAreaPos.x = pos.x;
390 clipAreaPos.y = pos.y;
391 }
392
393#ifdef USE_CLIP_AREA
394 aDC->DestroyClippingRegion();
395 aDC->SetClippingRegion( clipAreaPos, wxSize( size.x, size.y ) );
396#endif
397
398 if( aBackgroundColor != COLOR4D::UNSPECIFIED && m_bitmap->HasAlpha() )
399 {
400 // Most printers don't support transparent images properly,
401 // so blend the image with background color.
402
403 int w = m_bitmap->GetWidth();
404 int h = m_bitmap->GetHeight();
405
406 wxImage image( w, h );
407 wxColour bgColor = aBackgroundColor.ToColour();
408
409 image.SetRGB( wxRect( 0, 0, w, h ), bgColor.Red(), bgColor.Green(), bgColor.Blue() );
410 image.Paste( m_bitmap->ConvertToImage(), 0, 0, wxIMAGE_ALPHA_BLEND_COMPOSE );
411
413 image = image.ConvertToGreyscale();
414
415 aDC->DrawBitmap( wxBitmap( image ), pos.x, pos.y, true );
416 }
417 else if( GetGRForceBlackPenState() )
418 {
419 wxBitmap result( m_bitmap->ConvertToImage().ConvertToGreyscale() );
420 aDC->DrawBitmap( result, pos.x, pos.y, true );
421 }
422 else
423 {
424 aDC->DrawBitmap( *m_bitmap, pos.x, pos.y, true );
425 }
426
427 if( useTransform )
428 aDC->SetTransformMatrix( init_matrix );
429 else
430 {
431 aDC->SetUserScale( scale, scale );
432 aDC->SetLogicalOrigin( logicalOriginX, logicalOriginY );
433 }
434
435#ifdef USE_CLIP_AREA
436 aDC->DestroyClippingRegion();
437#endif
438}
439
440
442{
443 VECTOR2I size;
444
445 if( m_image )
446 {
447 size.x = KiROUND( m_image->GetWidth() * GetScalingFactor() );
448 size.y = KiROUND( m_image->GetHeight() * GetScalingFactor() );
449 }
450
451 return size;
452}
453
454
455void BITMAP_BASE::mirrorImageInPlace( wxImage& aImage, FLIP_DIRECTION aFlipDirection )
456{
457 const int w = aImage.GetWidth();
458 const int h = aImage.GetHeight();
459
460 if( w == 0 || h == 0 )
461 return;
462
463 // wxImage is reference-counted, so detach the data from other users
464 // before modifying it in place.
465 aImage.UnShare();
466
467 unsigned char* rgb = aImage.GetData();
468 unsigned char* alpha = aImage.HasAlpha() ? aImage.GetAlpha() : nullptr;
469 const int bpp = 3;
470
471 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
472 {
473 // Swap columns left-to-right within each row
474 for( int y = 0; y < h; ++y )
475 {
476 unsigned char* rowRgb = rgb + y * w * bpp;
477
478 for( int lo = 0, hi = w - 1; lo < hi; ++lo, --hi )
479 {
480 std::swap( rowRgb[lo * bpp + 0], rowRgb[hi * bpp + 0] );
481 std::swap( rowRgb[lo * bpp + 1], rowRgb[hi * bpp + 1] );
482 std::swap( rowRgb[lo * bpp + 2], rowRgb[hi * bpp + 2] );
483 }
484
485 if( alpha )
486 {
487 unsigned char* rowAlpha = alpha + y * w;
488
489 for( int lo = 0, hi = w - 1; lo < hi; ++lo, --hi )
490 std::swap( rowAlpha[lo], rowAlpha[hi] );
491 }
492 }
493 }
494 else
495 {
496 // Swap entire rows top-to-bottom
497 const size_t rowBytes = w * bpp;
498 std::vector<unsigned char> tmpRgb( rowBytes );
499
500 for( int lo = 0, hi = h - 1; lo < hi; ++lo, --hi )
501 {
502 unsigned char* rowLo = rgb + lo * rowBytes;
503 unsigned char* rowHi = rgb + hi * rowBytes;
504 memcpy( tmpRgb.data(), rowLo, rowBytes );
505 memcpy( rowLo, rowHi, rowBytes );
506 memcpy( rowHi, tmpRgb.data(), rowBytes );
507 }
508
509 if( alpha )
510 {
511 std::vector<unsigned char> tmpAlpha( w );
512
513 for( int lo = 0, hi = h - 1; lo < hi; ++lo, --hi )
514 {
515 unsigned char* aLo = alpha + lo * w;
516 unsigned char* aHi = alpha + hi * w;
517 memcpy( tmpAlpha.data(), aLo, w );
518 memcpy( aLo, aHi, w );
519 memcpy( aHi, tmpAlpha.data(), w );
520 }
521 }
522 }
523}
524
525
526void BITMAP_BASE::invertImageInPlace( wxImage& aImage )
527{
528 const int w = aImage.GetWidth();
529 const int h = aImage.GetHeight();
530
531 if( w == 0 || h == 0 )
532 return;
533
534 aImage.UnShare();
535
536 unsigned char* rgb = aImage.GetData();
537
538 for( int i = 0; i < w * h * 3; ++i )
539 rgb[i] = 255 - rgb[i];
540}
541
542
544{
545 if( m_image )
546 {
547 mirrorImageInPlace( *m_image, aFlipDirection );
548
549 if( aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
551 else
553
554 m_bitmapDirty = true;
555 m_imageData.Clear();
556 }
557}
558
559
560void BITMAP_BASE::Rotate( bool aRotateCCW )
561{
562 if( m_image )
563 {
564 // wxImage::Rotate90() clears resolution metadata, so preserve it.
565 // Use string form to avoid truncating fractional pixels/cm values.
566 wxString resX = m_image->GetOption( wxIMAGE_OPTION_RESOLUTIONX );
567 wxString resY = m_image->GetOption( wxIMAGE_OPTION_RESOLUTIONY );
568 int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
569
570 // wxImage::Rotate90 parameter is "clockwise", so invert for CCW rotation
571 *m_image = m_image->Rotate90( !aRotateCCW );
572
573 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT, unit );
574 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX );
575 m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY );
576
577 m_rotation += ( aRotateCCW ? ANGLE_90 : -ANGLE_90 );
578 m_bitmapDirty = true;
579 m_imageData.Clear();
580 }
581}
582
583
585{
586 if( m_image )
587 {
588 *m_image = m_image->ConvertToGreyscale();
589 *m_originalImage = m_originalImage->ConvertToGreyscale();
590 m_bitmapDirty = true;
591 m_imageData.Clear();
592 m_imageId = KIID();
593 }
594}
595
596
598{
599 if( m_image )
600 {
603 m_bitmapDirty = true;
604 m_imageData.Clear();
605 m_imageId = KIID();
606 }
607}
608
609
610void BITMAP_BASE::ConvertColourToAlpha( const wxColour& aColour )
611{
612 if( m_image )
613 {
616 m_bitmapDirty = true;
617 m_imageData.Clear();
618 m_imageId = KIID();
619 }
620}
621
622
623void BITMAP_BASE::PlotImage( PLOTTER* aPlotter, const VECTOR2I& aPos,
624 const COLOR4D& aDefaultColor,
625 int aDefaultPensize ) const
626{
627 if( m_image == nullptr )
628 return;
629
630 // These 2 lines are useful only for plotters that cannot plot a bitmap
631 // and plot a rectangle instead of.
632 aPlotter->SetColor( aDefaultColor );
633 aPlotter->SetCurrentLineWidth( aDefaultPensize );
634 aPlotter->PlotImage( *m_image, aPos, GetScalingFactor() );
635}
636
637
639{
640 if( m_image )
641 {
642 wxMemoryOutputStream stream;
643 wxBitmapType type = m_imageType == wxBITMAP_TYPE_JPEG ? wxBITMAP_TYPE_JPEG
644 : wxBITMAP_TYPE_PNG;
645
646 if( !m_image->SaveFile( stream, type ) )
647 return;
648
649 m_imageData.GetWriteBuf( stream.GetLength() );
650 stream.CopyTo( m_imageData.GetData(), stream.GetLength() );
651 m_imageData.SetDataLen( stream.GetLength() );
652 }
653}
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
void ensureBitmapUpToDate() const
Ensure the cached wxBitmap is up-to-date with the current wxImage.
wxMemoryBuffer m_imageData
Cached encoded image data (PNG/JPEG).
void Rotate(bool aRotateCCW)
Rotate image CW or CCW.
bool LoadLegacyData(LINE_READER &aLine, wxString &aErrorMsg)
Load an image data saved by #SaveData.
void updateImageDataBuffer()
Resets the image data buffer using the current image data.
double GetScalingFactor() const
This scaling factor depends on m_pixelSizeIu and m_scale.
Definition bitmap_base.h:90
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.
const BOX2I GetBoundingBox() const
Return the orthogonal, bounding box of this object for display purposes.
bool m_isMirroredY
void InvertColors()
Invert the colours of the image (e.g.
bool m_isMirroredX
void ImportData(BITMAP_BASE &aItem)
Copy aItem image to this object and update m_bitmap.
bool m_bitmapDirty
True when m_bitmap needs rebuilding from m_image.
bool SaveImageData(wxOutputStream &aOutStream) const
Write the bitmap data to aOutStream.
wxImage * m_originalImage
Raw image data, not transformed by rotate/mirror.
void rebuildBitmap(bool aResetID=true)
Rebuild the internal bitmap used to draw/plot image.
static void mirrorImageInPlace(wxImage &aImage, FLIP_DIRECTION aFlipDirection)
Mirror the wxImage pixel data in-place without allocating a new image.
wxBitmapType m_imageType
The image type (png, jpeg, etc.).
void ConvertColourToAlpha(const wxColour &aColour)
Convert aColour to transparent in the image, proportionally to the 'distance' of each pixel from aCol...
wxBitmap * m_bitmap
The bitmap used to draw/plot image.
void ConvertToGreyscale()
bool SetImage(const wxImage &aImage)
Set the image from an existing wxImage.
bool ReadImageFile(const wxString &aFullFilename)
Reads and stores in memory an image file.
wxImage * m_image
The raw, uncompressed image data.
double m_scale
The scaling factor of the bitmap with m_pixelSizeIu, controls the actual draw size.
double m_pixelSizeIu
The scaling factor of the bitmap to convert the bitmap size (in pixels) to internal KiCad units.
int m_ppi
The bitmap definition. The default is 300PPI.
void DrawBitmap(wxDC *aDC, const VECTOR2I &aPos, const KIGFX::COLOR4D &aBackgroundColor=KIGFX::COLOR4D::UNSPECIFIED) const
int GetLegacyPPI() const
Recompute the PPI the way it was computed before the pixels/cm truncation fix.
BITMAP_BASE(const VECTOR2I &pos=VECTOR2I(0, 0))
VECTOR2I GetSize() const
EDA_ANGLE m_rotation
static void invertImageInPlace(wxImage &aImage)
Invert the colour of every pixel of the wxImage in-place without allocating a new image.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:399
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
wxColour ToColour() const
Definition color4d.cpp:221
Definition kiid.h:46
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:65
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:101
Base plotter engine class.
Definition plotter.h:136
virtual void PlotImage(const wxImage &aImage, const VECTOR2I &aPos, double aScaleFactor)
Only PostScript plotters can plot bitmaps.
Definition plotter.cpp:258
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:422
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:424
bool GetGRForceBlackPenState(void)
Definition gr_basic.cpp:165
FLIP_DIRECTION
Definition mirror.h:23
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
void ConvertColourToAlphaInPlace(wxImage &aImage, const wxColour &aColour)
Apply the "Color to Alpha" algorithm in place, converting aColour to transparent.
const int scale
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683