KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_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 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, see <https://www.gnu.org/licenses/>.
18 */
19
24
26
27// Code under test
28#include <bitmap_base.h>
29
30#include "gal/color4d.h"
31#include "wximage_test_utils.h"
32
33#include <wx/mstream.h>
34
35
36// Dummy PNG image 8x8, 4 tiles:
37//
38// green, black,
39// red, blue
40// (the black tile is a circle, otherwise older wx's seem to crash)
41static const std::vector<unsigned char> png_data_4tile = { //
42 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
43 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, 0x00, 0x00, 0x00, 0xC4, 0x0F, 0xBE,
44 0x8B, 0x00, 0x00, 0x00, 0x04, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, 0x08, 0x7C, 0x08, 0x64,
45 0x88, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC4, 0x00, 0x00, 0x0E,
46 0xC4, 0x01, 0x95, 0x2B, 0x0E, 0x1B, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F,
47 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0x6B, 0x73, 0x63,
48 0x61, 0x70, 0x65, 0x2E, 0x6F, 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0x00, 0x40, 0x49,
49 0x44, 0x41, 0x54, 0x18, 0x95, 0xA5, 0xCD, 0x4B, 0x0A, 0xC0, 0x20, 0x14, 0x43, 0xD1, 0x63, 0x77,
50 0xA7, 0xDD, 0xFF, 0x06, 0xD4, 0x7D, 0x3C, 0x07, 0x95, 0x42, 0x3F, 0xE0, 0xC0, 0x84, 0x0C, 0x02,
51 0xE1, 0x86, 0x78, 0x99, 0x13, 0x1D, 0x0D, 0xC5, 0xCF, 0xA0, 0x21, 0x66, 0xEA, 0x61, 0xA9, 0x2F,
52 0xA1, 0x4C, 0x4A, 0x45, 0x4E, 0x71, 0xA1, 0x6E, 0xA5, 0x67, 0xB5, 0xBC, 0xD8, 0x1F, 0x0C, 0xA7,
53 0xFD, 0x23, 0x67, 0x70, 0xDA, 0x89, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
54 0x42, 0x60, 0x82
55};
56
58static const VECTOR2I size_4tile{ 8, 8 };
59
60static const KIGFX::COLOR4D col_red{ 1.0, 0.0, 0.0, 1.0 };
61static const KIGFX::COLOR4D col_green{ 0.0, 1.0, 0.0, 1.0 };
62static const KIGFX::COLOR4D col_blue{ 0.0, 0.0, 1.0, 1.0 };
63static const KIGFX::COLOR4D col_black{ 0.0, 0.0, 0.0, 1.0 };
64static const KIGFX::COLOR4D col_white{ 1.0, 1.0, 1.0, 1.0 };
65static const KIGFX::COLOR4D col_magenta{ 1.0, 0.0, 1.0, 1.0 };
66static const KIGFX::COLOR4D col_cyan{ 0.0, 1.0, 1.0, 1.0 };
67static const KIGFX::COLOR4D col_yellow{ 1.0, 1.0, 0.0, 1.0 };
68
69
71{
72public:
74 {
75 wxMemoryInputStream mis( png_data_4tile.data(), png_data_4tile.size() );
76
77 bool ok = m_4tile.ReadImageFile( mis );
78
79 // this is needed for most tests and can fail if the image handlers
80 // are not initialised (e.g. with wxInitAllImageHandlers)
81 BOOST_REQUIRE( ok );
82
83 // use an easier scale for test purposes
84 m_4tile.SetPixelSizeIu( 2.0 );
85 m_4tile.SetScale( 5.0 );
86 }
87
88 /*
89 * Simple image of 4 coloured tiles
90 */
92};
93
94
98BOOST_FIXTURE_TEST_SUITE( BitmapBase, TEST_BITMAP_BASE_FIXTURE )
99
100
101
105{
107 // Const to test we can get this data from a const object
108 const BITMAP_BASE& cempty = empty;
109 const int expected_ppi = 300;
110
111 BOOST_CHECK_EQUAL( cempty.GetImageData(), nullptr );
112 BOOST_CHECK_EQUAL( cempty.GetPPI(), expected_ppi );
113 BOOST_CHECK_EQUAL( cempty.GetScale(), 1.0 );
114 BOOST_CHECK_EQUAL( cempty.GetPixelSizeIu(), 254000.0 / expected_ppi );
115
116 // can do this on an empty image
117 empty.Rotate( true );
119}
120
121
126{
129
130 // should still have nothing in it
131 BOOST_CHECK_EQUAL( copied.GetImageData(), nullptr );
132}
133
134
141
142
147{
148 // make sure we can do all this to a const img
149 const BITMAP_BASE& img = m_4tile;
150
151 // have "some" image data
152 BOOST_REQUIRE_NE( img.GetImageData(), nullptr );
153
154 // 3780 pixels/meter = 37.8 px/cm; 37.8 * 2.54 = 96.012 -> rounds to 96
155 const int expected_ppi = 96;
156 BOOST_CHECK_EQUAL( img.GetPPI(), expected_ppi );
157 BOOST_CHECK_EQUAL( img.GetScale(), 5.0 );
158
159 // we changed this, make sure it's right
160 BOOST_CHECK_EQUAL( img.GetPixelSizeIu(), 2.0 );
161
162 BOOST_CHECK( img.GetSizePixels() == size_4tile );
163 BOOST_CHECK( img.GetSize() == size_4tile * 10 );
164
165 const BOX2I bb = img.GetBoundingBox();
166 BOOST_CHECK( bb.GetPosition() == VECTOR2I( -40, -40 ) );
167 BOOST_CHECK( bb.GetEnd() == VECTOR2I( 40, 40 ) );
168}
169
170
179BOOST_AUTO_TEST_CASE( PPIFromNonIntegerPixelsPerCm )
180{
181 // Minimal 1x1 RGB PNG with pHYs chunk: 3780 pixels/meter in both axes.
182 // 3780 PPM -> 37.8 px/cm -> 37.8 * 2.54 = 96.012 -> rounds to 96 PPI.
183 // Generated with Python (zlib/struct), CRCs verified.
184 static const std::vector<unsigned char> png_with_phys = {
185 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
186 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
187 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC4, 0x00, 0x00, 0x0E,
188 0xC4, 0x01, 0x95, 0x2B, 0x0E, 0x1B, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA,
189 0x63, 0xF8, 0xCF, 0xC0, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0xF7, 0x03, 0x41, 0x43, 0x00, 0x00,
190 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
191 };
192
193 BITMAP_BASE bmp;
194 wxMemoryInputStream mis( png_with_phys.data(), png_with_phys.size() );
195 bool ok = bmp.ReadImageFile( mis );
196
197 BOOST_REQUIRE( ok );
198
199 // 3780 PPM should give 96 PPI, not 94 (the old truncation result)
200 BOOST_CHECK_EQUAL( bmp.GetPPI(), 96 );
201
202 // GetLegacyPPI() reproduces the truncated value so pre-fix designs can be migrated by
203 // the GetPPI()/GetLegacyPPI() ratio. Here that is 96/94.
204 BOOST_CHECK_EQUAL( bmp.GetLegacyPPI(), 94 );
205}
206
207
212{
213 const wxImage* img_data = m_4tile.GetImageData();
214 BOOST_REQUIRE_NE( img_data, nullptr );
215
216 // green, black,
217 // red, blue
218 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
219 { 1, 1, col_green },
220 { 6, 1, col_black },
221 { 1, 6, col_red },
222 { 6, 6, col_blue },
223 };
224
225 for( const auto& c : exp_pixels )
226 {
228 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
229 }
230}
231
235BOOST_AUTO_TEST_CASE( RotateImageCCW )
236{
237 m_4tile.Rotate( true ); // true = CCW
238
239 const wxImage* img_data = m_4tile.GetImageData();
240 BOOST_REQUIRE_NE( img_data, nullptr );
241
242 // Original: After 90 CCW:
243 // green, black, black, blue,
244 // red, blue green, red
245 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
246 { 1, 1, col_black },
247 { 6, 1, col_blue },
248 { 1, 6, col_green },
249 { 6, 6, col_red },
250 };
251
252 for( const auto& c : exp_pixels )
253 {
255 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
256 }
257}
258
262BOOST_AUTO_TEST_CASE( RotateImageCW )
263{
264 m_4tile.Rotate( false ); // false = CW
265
266 const wxImage* img_data = m_4tile.GetImageData();
267 BOOST_REQUIRE_NE( img_data, nullptr );
268
269 // Original: After 90 CW:
270 // green, black, red, green,
271 // red, blue blue, black
272 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
273 { 1, 1, col_red },
274 { 6, 1, col_green },
275 { 1, 6, col_blue },
276 { 6, 6, col_black },
277 };
278
279 for( const auto& c : exp_pixels )
280 {
282 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
283 }
284}
285
290{
291 m_4tile.Mirror( FLIP_DIRECTION::TOP_BOTTOM );
292
293 const wxImage* img_data = m_4tile.GetImageData();
294 BOOST_REQUIRE_NE( img_data, nullptr );
295
296 // red, blue
297 // green, black
298 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
299 { 1, 1, col_red },
300 { 6, 1, col_blue },
301 { 1, 6, col_green },
302 { 6, 6, col_black },
303 };
304
305 for( const auto& c : exp_pixels )
306 {
308 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
309 }
310}
311
316{
317 // Set one of the pixels to non-opaque to make sure the alpha channel is not modified
318 wxImage* img_data = m_4tile.GetImageData();
319 BOOST_REQUIRE_NE( img_data, nullptr );
320 img_data->SetAlpha( 1, 1, 42 );
321
322 m_4tile.InvertColors();
323
324 KIGFX::COLOR4D col_1_1 = col_magenta;
325 col_1_1.a = 42.0 / 255.0; // alpha channel should not be modified
326
327 // Original: After inverting:
328 // green, black, magenta, white,
329 // red, blue cyan, yellow
330 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
331 { 1, 1, col_1_1 },
332 { 6, 1, col_white },
333 { 1, 6, col_cyan },
334 { 6, 6, col_yellow },
335 };
336
337 for( const auto& c : exp_pixels )
338 {
339 BOOST_CHECK_PREDICATE( KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
340 }
341}
342
343
350BOOST_AUTO_TEST_CASE( SetImageOutputsData )
351{
352 BITMAP_BASE bitmap;
353 wxImage img( 2, 2 );
354 img.SetRGB( 0, 0, 0, 255, 0 );
355 img.SetRGB( 1, 0, 0, 0, 255 );
356 img.SetRGB( 0, 1, 255, 0, 0 );
357 img.SetRGB( 1, 1, 0, 0, 255 );
358
359 // Set the wxImage directly, not via a stream/file
360 // (this happens, e.g. when the clipboard gives you a wxImage)
361 bitmap.SetImage( img );
362
363 wxMemoryOutputStream mos;
364 BOOST_CHECK( bitmap.SaveImageData( mos ) );
365
366 BOOST_REQUIRE( mos.GetSize() > 0 );
367
368 // check the output is the same as the input
369 wxMemoryInputStream mis( mos );
370 wxImage img2;
371
372 BOOST_CHECK( img2.LoadFile( mis, wxBITMAP_TYPE_PNG ) );
373
375}
376
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
This class handle bitmap images in KiCad.
Definition bitmap_base.h:45
VECTOR2I GetSizePixels() const
const BOX2I GetBoundingBox() const
Return the orthogonal, bounding box of this object for display purposes.
double GetPixelSizeIu() const
Definition bitmap_base.h:61
double GetScale() const
Definition bitmap_base.h:69
bool SaveImageData(wxOutputStream &aOutStream) const
Write the bitmap data to aOutStream.
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.
int GetPPI() const
int GetLegacyPPI() const
Recompute the PPI the way it was computed before the pixels/cm truncation fix.
wxImage * GetImageData()
Definition bitmap_base.h:64
VECTOR2I GetSize() const
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr const Vec GetEnd() const
Definition box2.h:209
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double a
Alpha component.
Definition color4d.h:393
static bool empty(const wxTextEntryBase *aCtrl)
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
bool IsImagePixelOfColor(const wxImage &aImage, int aX, int aY, const KIGFX::COLOR4D &aColor)
Predicate to check an image pixel matches color and alpha.
bool ImagesHaveSamePixels(const wxImage &aImgA, const wxImage &aImgB)
Check if an image is identical to another image, pixel by pixel.
KIGFX::COLOR4D m_color
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
static const KIGFX::COLOR4D col_blue
static const KIGFX::COLOR4D col_magenta
static const KIGFX::COLOR4D col_cyan
BOOST_AUTO_TEST_CASE(Empty)
Declare the test suite.
static const VECTOR2I size_4tile
static const KIGFX::COLOR4D col_white
static const KIGFX::COLOR4D col_red
static const KIGFX::COLOR4D col_yellow
static const KIGFX::COLOR4D col_green
static const std::vector< unsigned char > png_data_4tile
4tile is an 8x8 image
static const KIGFX::COLOR4D col_black
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
bool copied
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683