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 "wximage_test_utils.h"
31
32#include <wx/mstream.h>
33
34
35// Dummy PNG image 8x8, 4 tiles:
36//
37// green, black,
38// red, blue
39// (the black tile is a circle, otherwise older wx's seem to crash)
40static const std::vector<unsigned char> png_data_4tile = { //
41 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
42 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x06, 0x00, 0x00, 0x00, 0xC4, 0x0F, 0xBE,
43 0x8B, 0x00, 0x00, 0x00, 0x04, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, 0x08, 0x7C, 0x08, 0x64,
44 0x88, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC4, 0x00, 0x00, 0x0E,
45 0xC4, 0x01, 0x95, 0x2B, 0x0E, 0x1B, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F,
46 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0x6B, 0x73, 0x63,
47 0x61, 0x70, 0x65, 0x2E, 0x6F, 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0x00, 0x40, 0x49,
48 0x44, 0x41, 0x54, 0x18, 0x95, 0xA5, 0xCD, 0x4B, 0x0A, 0xC0, 0x20, 0x14, 0x43, 0xD1, 0x63, 0x77,
49 0xA7, 0xDD, 0xFF, 0x06, 0xD4, 0x7D, 0x3C, 0x07, 0x95, 0x42, 0x3F, 0xE0, 0xC0, 0x84, 0x0C, 0x02,
50 0xE1, 0x86, 0x78, 0x99, 0x13, 0x1D, 0x0D, 0xC5, 0xCF, 0xA0, 0x21, 0x66, 0xEA, 0x61, 0xA9, 0x2F,
51 0xA1, 0x4C, 0x4A, 0x45, 0x4E, 0x71, 0xA1, 0x6E, 0xA5, 0x67, 0xB5, 0xBC, 0xD8, 0x1F, 0x0C, 0xA7,
52 0xFD, 0x23, 0x67, 0x70, 0xDA, 0x89, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
53 0x42, 0x60, 0x82
54};
55
57static const VECTOR2I size_4tile{ 8, 8 };
58
59static const KIGFX::COLOR4D col_red{ 1.0, 0.0, 0.0, 1.0 };
60static const KIGFX::COLOR4D col_green{ 0.0, 1.0, 0.0, 1.0 };
61static const KIGFX::COLOR4D col_blue{ 0.0, 0.0, 1.0, 1.0 };
62static const KIGFX::COLOR4D col_black{ 0.0, 0.0, 0.0, 1.0 };
63
64
66{
67public:
69 {
70 wxMemoryInputStream mis( png_data_4tile.data(), png_data_4tile.size() );
71
72 bool ok = m_4tile.ReadImageFile( mis );
73
74 // this is needed for most tests and can fail if the image handlers
75 // are not initialised (e.g. with wxInitAllImageHandlers)
76 BOOST_REQUIRE( ok );
77
78 // use an easier scale for test purposes
79 m_4tile.SetPixelSizeIu( 2.0 );
80 m_4tile.SetScale( 5.0 );
81 }
82
83 /*
84 * Simple image of 4 coloured tiles
85 */
87};
88
89
93BOOST_FIXTURE_TEST_SUITE( BitmapBase, TEST_BITMAP_BASE_FIXTURE )
94
95
96
100{
102 // Const to test we can get this data from a const object
103 const BITMAP_BASE& cempty = empty;
104 const int expected_ppi = 300;
105
106 BOOST_CHECK_EQUAL( cempty.GetImageData(), nullptr );
107 BOOST_CHECK_EQUAL( cempty.GetPPI(), expected_ppi );
108 BOOST_CHECK_EQUAL( cempty.GetScale(), 1.0 );
109 BOOST_CHECK_EQUAL( cempty.GetPixelSizeIu(), 254000.0 / expected_ppi );
110
111 // can do this on an empty image
112 empty.Rotate( true );
114}
115
116
121{
124
125 // should still have nothing in it
126 BOOST_CHECK_EQUAL( copied.GetImageData(), nullptr );
127}
128
129
136
137
142{
143 // make sure we can do all this to a const img
144 const BITMAP_BASE& img = m_4tile;
145
146 // have "some" image data
147 BOOST_REQUIRE_NE( img.GetImageData(), nullptr );
148
149 // 3780 pixels/meter = 37.8 px/cm; 37.8 * 2.54 = 96.012 -> rounds to 96
150 const int expected_ppi = 96;
151 BOOST_CHECK_EQUAL( img.GetPPI(), expected_ppi );
152 BOOST_CHECK_EQUAL( img.GetScale(), 5.0 );
153
154 // we changed this, make sure it's right
155 BOOST_CHECK_EQUAL( img.GetPixelSizeIu(), 2.0 );
156
157 BOOST_CHECK( img.GetSizePixels() == size_4tile );
158 BOOST_CHECK( img.GetSize() == size_4tile * 10 );
159
160 const BOX2I bb = img.GetBoundingBox();
161 BOOST_CHECK( bb.GetPosition() == VECTOR2I( -40, -40 ) );
162 BOOST_CHECK( bb.GetEnd() == VECTOR2I( 40, 40 ) );
163}
164
165
174BOOST_AUTO_TEST_CASE( PPIFromNonIntegerPixelsPerCm )
175{
176 // Minimal 1x1 RGB PNG with pHYs chunk: 3780 pixels/meter in both axes.
177 // 3780 PPM -> 37.8 px/cm -> 37.8 * 2.54 = 96.012 -> rounds to 96 PPI.
178 // Generated with Python (zlib/struct), CRCs verified.
179 static const std::vector<unsigned char> png_with_phys = {
180 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
181 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
182 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC4, 0x00, 0x00, 0x0E,
183 0xC4, 0x01, 0x95, 0x2B, 0x0E, 0x1B, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA,
184 0x63, 0xF8, 0xCF, 0xC0, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0xF7, 0x03, 0x41, 0x43, 0x00, 0x00,
185 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82
186 };
187
188 BITMAP_BASE bmp;
189 wxMemoryInputStream mis( png_with_phys.data(), png_with_phys.size() );
190 bool ok = bmp.ReadImageFile( mis );
191
192 BOOST_REQUIRE( ok );
193
194 // 3780 PPM should give 96 PPI, not 94 (the old truncation result)
195 BOOST_CHECK_EQUAL( bmp.GetPPI(), 96 );
196
197 // GetLegacyPPI() reproduces the truncated value so pre-fix designs can be migrated by
198 // the GetPPI()/GetLegacyPPI() ratio. Here that is 96/94.
199 BOOST_CHECK_EQUAL( bmp.GetLegacyPPI(), 94 );
200}
201
202
207{
208 const wxImage* img_data = m_4tile.GetImageData();
209 BOOST_REQUIRE_NE( img_data, nullptr );
210
211 // green, black,
212 // red, blue
213 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
214 { 1, 1, col_green },
215 { 6, 1, col_black },
216 { 1, 6, col_red },
217 { 6, 6, col_blue },
218 };
219
220 for( const auto& c : exp_pixels )
221 {
223 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
224 }
225}
226
230BOOST_AUTO_TEST_CASE( RotateImageCCW )
231{
232 m_4tile.Rotate( true ); // true = CCW
233
234 const wxImage* img_data = m_4tile.GetImageData();
235 BOOST_REQUIRE_NE( img_data, nullptr );
236
237 // Original: After 90 CCW:
238 // green, black, black, blue,
239 // red, blue green, red
240 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
241 { 1, 1, col_black },
242 { 6, 1, col_blue },
243 { 1, 6, col_green },
244 { 6, 6, col_red },
245 };
246
247 for( const auto& c : exp_pixels )
248 {
250 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
251 }
252}
253
257BOOST_AUTO_TEST_CASE( RotateImageCW )
258{
259 m_4tile.Rotate( false ); // false = CW
260
261 const wxImage* img_data = m_4tile.GetImageData();
262 BOOST_REQUIRE_NE( img_data, nullptr );
263
264 // Original: After 90 CW:
265 // green, black, red, green,
266 // red, blue blue, black
267 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
268 { 1, 1, col_red },
269 { 6, 1, col_green },
270 { 1, 6, col_blue },
271 { 6, 6, col_black },
272 };
273
274 for( const auto& c : exp_pixels )
275 {
277 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
278 }
279}
280
285{
286 m_4tile.Mirror( FLIP_DIRECTION::TOP_BOTTOM );
287
288 const wxImage* img_data = m_4tile.GetImageData();
289 BOOST_REQUIRE_NE( img_data, nullptr );
290
291 // red, blue
292 // green, black
293 const std::vector<TEST_PIXEL_CASE> exp_pixels = {
294 { 1, 1, col_red },
295 { 6, 1, col_blue },
296 { 1, 6, col_green },
297 { 6, 6, col_black },
298 };
299
300 for( const auto& c : exp_pixels )
301 {
303 KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) );
304 }
305}
306
313BOOST_AUTO_TEST_CASE( SetImageOutputsData )
314{
315 BITMAP_BASE bitmap;
316 wxImage img( 2, 2 );
317 img.SetRGB( 0, 0, 0, 255, 0 );
318 img.SetRGB( 1, 0, 0, 0, 255 );
319 img.SetRGB( 0, 1, 255, 0, 0 );
320 img.SetRGB( 1, 1, 0, 0, 255 );
321
322 // Set the wxImage directly, not via a stream/file
323 // (this happens, e.g. when the clipboard gives you a wxImage)
324 bitmap.SetImage( img );
325
326 wxMemoryOutputStream mos;
327 BOOST_CHECK( bitmap.SaveImageData( mos ) );
328
329 BOOST_REQUIRE( mos.GetSize() > 0 );
330
331 // check the output is the same as the input
332 wxMemoryInputStream mis( mos );
333 wxImage img2;
334
335 BOOST_CHECK( img2.LoadFile( mis, wxBITMAP_TYPE_PNG ) );
336
338}
339
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
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:207
constexpr const Vec GetEnd() const
Definition box2.h:208
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
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
BOOST_AUTO_TEST_CASE(Empty)
Declare the test suite.
static const VECTOR2I size_4tile
static const KIGFX::COLOR4D col_red
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