From 94b46ce176d7b25112af81360b8a5db121b39d88 Mon Sep 17 00:00:00 2001 From: John Beard Date: Wed, 24 Apr 2019 16:35:01 +0100 Subject: [PATCH] QA common: add some BITMAP_BASE tests Also: * Init wx image handlers for qa_common * Break out COLOR4D test predicates * Fix some bitmap_base.h headers/forwards --- common/bitmap_base.cpp | 17 +++ include/bitmap_base.h | 28 +++- qa/common/CMakeLists.txt | 3 + qa/common/color4d_test_utils.h | 64 +++++++++ qa/common/test_bitmap_base.cpp | 228 +++++++++++++++++++++++++++++++ qa/common/test_color4d.cpp | 35 +---- qa/common/test_module.cpp | 11 +- qa/common/wximage_test_utils.cpp | 78 +++++++++++ qa/common/wximage_test_utils.h | 59 ++++++++ 9 files changed, 486 insertions(+), 37 deletions(-) create mode 100644 qa/common/color4d_test_utils.h create mode 100644 qa/common/test_bitmap_base.cpp create mode 100644 qa/common/wximage_test_utils.cpp create mode 100644 qa/common/wximage_test_utils.h diff --git a/common/bitmap_base.cpp b/common/bitmap_base.cpp index dc7d2888a2..54b1d2627b 100644 --- a/common/bitmap_base.cpp +++ b/common/bitmap_base.cpp @@ -78,6 +78,23 @@ void BITMAP_BASE::ImportData( BITMAP_BASE* aItem ) } +bool BITMAP_BASE::ReadImageFile( wxInputStream& aInStream ) +{ + auto new_image = std::make_unique(); + + if( !new_image->LoadFile( aInStream ) ) + { + return false; + } + + delete m_image; + m_image = new_image.release(); + m_bitmap = new wxBitmap( *m_image ); + + return true; +} + + bool BITMAP_BASE::ReadImageFile( const wxString& aFullFilename ) { wxImage* new_image = new wxImage(); diff --git a/include/bitmap_base.h b/include/bitmap_base.h index e34cb7b588..175d5f4cdc 100644 --- a/include/bitmap_base.h +++ b/include/bitmap_base.h @@ -27,13 +27,15 @@ #include -/** - * @file bitmap_base.h - * - */ +#include +#include + +class COLOR4D; +class LINE_READER; class PLOTTER; + /** * This class handle bitmap images in KiCad. * It is not intended to be used alone, but inside another class, @@ -163,17 +165,31 @@ public: void DrawBitmap( wxDC* aDC, const wxPoint& aPos ); /** - * Function ReadImageFile * Reads and stores in memory an image file. + * * Init the bitmap format used to draw this item. * supported images formats are format supported by wxImage * if all handlers are loaded - * by default, .png, .jpeg are alway loaded + * by default, .png, .jpeg are always loaded + * * @param aFullFilename The full filename of the image file to read. * @return bool - true if success reading else false. */ bool ReadImageFile( const wxString& aFullFilename ); + /** + * Reads and stores in memory an image file. + * + * Init the bitmap format used to draw this item. + * supported images formats are format supported by wxImage + * if all handlers are loaded + * by default, .png, .jpeg are always loaded + * + * @param aInStream an input stream containing the file data + * @return bool - true if success reading else false. + */ + bool ReadImageFile( wxInputStream& aInStream ); + /** * writes the bitmap data to aFile * The format is png, in Hexadecimal form: diff --git a/qa/common/CMakeLists.txt b/qa/common/CMakeLists.txt index 5f8e4b67c9..61816ff360 100644 --- a/qa/common/CMakeLists.txt +++ b/qa/common/CMakeLists.txt @@ -36,8 +36,11 @@ set( common_srcs ../../common/colors.cpp ../../common/observable.cpp + wximage_test_utils.cpp + test_array_axis.cpp test_array_options.cpp + test_bitmap_base.cpp test_color4d.cpp test_coroutine.cpp test_format_units.cpp diff --git a/qa/common/color4d_test_utils.h b/qa/common/color4d_test_utils.h new file mode 100644 index 0000000000..f7026377be --- /dev/null +++ b/qa/common/color4d_test_utils.h @@ -0,0 +1,64 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file + * Test utilities for COLOUR4D objects + */ + +#ifndef QA_COMMON_COLOR4D_TEST_UTILS__H +#define QA_COMMON_COLOR4D_TEST_UTILS__H + +#include + +#include + +namespace KI_TEST +{ +/** + * Checks if a COLOR4D is close enough to another + */ +inline bool IsColorNear( const KIGFX::COLOR4D& aCol, const KIGFX::COLOR4D aOther, double aTol ) +{ + return KI_TEST::IsWithin( aCol.r, aOther.r, aTol ) + && KI_TEST::IsWithin( aCol.g, aOther.g, aTol ) + && KI_TEST::IsWithin( aCol.b, aOther.b, aTol ) + && KI_TEST::IsWithin( aCol.a, aOther.a, aTol ); +} + +/** + * Checks if a COLOR4D is close enough to a given RGB char value + */ +inline bool IsColorNearHex( const KIGFX::COLOR4D& aCol, unsigned char r, unsigned char g, + unsigned char b, unsigned char a ) +{ + const double tol = 0.5 / 255.0; // One bit of quantised error + + return KI_TEST::IsWithin( aCol.r, r / 255.0, tol ) + && KI_TEST::IsWithin( aCol.g, g / 255.0, tol ) + && KI_TEST::IsWithin( aCol.b, b / 255.0, tol ) + && KI_TEST::IsWithin( aCol.a, a / 255.0, tol ); +} +} // namespace KI_TEST + +#endif // QA_COMMON_COLOR4D_TEST_UTILS__H \ No newline at end of file diff --git a/qa/common/test_bitmap_base.cpp b/qa/common/test_bitmap_base.cpp new file mode 100644 index 0000000000..6163edcc09 --- /dev/null +++ b/qa/common/test_bitmap_base.cpp @@ -0,0 +1,228 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * @file + * Test suite for BITMAP_BASE + */ + +#include + +// Code under test +#include + +#include "wximage_test_utils.h" + +#include + + +// Dummy PNG image 8x8, 4 tiles: +// +// green, black, +// red, blue +static const std::vector png_data_4tile = { // + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x02, 0x00, 0x00, 0x00, 0x4B, 0x6D, 0x29, + 0xDC, 0x00, 0x00, 0x00, 0x03, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, 0xDB, 0xE1, 0x4F, 0xE0, + 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC4, 0x00, 0x00, 0x0E, 0xC4, + 0x01, 0x95, 0x2B, 0x0E, 0x1B, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F, 0x66, + 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0x6B, 0x73, 0x63, 0x61, + 0x70, 0x65, 0x2E, 0x6F, 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0x00, 0x18, 0x49, 0x44, + 0x41, 0x54, 0x08, 0x5B, 0x63, 0x60, 0xF8, 0xCF, 0x80, 0x40, 0x28, 0x80, 0x7A, 0x12, 0xA8, 0xE2, + 0x48, 0x3C, 0xEA, 0x49, 0x00, 0x00, 0xF1, 0xA9, 0x2F, 0xD1, 0xB5, 0xC6, 0x95, 0xD0, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 +}; + +///< 4tile is an 8x8 image +static const wxSize size_4tile{ 8, 8 }; + +static const KIGFX::COLOR4D col_red{ 1.0, 0.0, 0.0, 1.0 }; +static const KIGFX::COLOR4D col_green{ 0.0, 1.0, 0.0, 1.0 }; +static const KIGFX::COLOR4D col_blue{ 0.0, 0.0, 1.0, 1.0 }; +static const KIGFX::COLOR4D col_black{ 0.0, 0.0, 0.0, 1.0 }; + + +class TEST_BITMAP_BASE_FIXTURE +{ +public: + TEST_BITMAP_BASE_FIXTURE() + { + wxMemoryInputStream mis( png_data_4tile.data(), png_data_4tile.size() ); + + bool ok = m_4tile.ReadImageFile( mis ); + + // this is needed for most tests and can fail if the image handlers + // are not initialised (e.g. with wxInitAllImageHandlers) + BOOST_REQUIRE( ok ); + + // use an easier scale for test purposes + m_4tile.SetPixelScaleFactor( 2.0 ); + m_4tile.SetScale( 5.0 ); + } + + /* + * Simple image of 4 coloured tiles + */ + BITMAP_BASE m_4tile; +}; + + +/** + * Declare the test suite + */ +BOOST_FIXTURE_TEST_SUITE( BitmapBase, TEST_BITMAP_BASE_FIXTURE ) + + +/** + * Check basic properties of a populated image + */ +BOOST_AUTO_TEST_CASE( Empty ) +{ + BITMAP_BASE empty; + // Const to test we can get this data from a const object + const BITMAP_BASE& cempty = empty; + + BOOST_CHECK_EQUAL( cempty.GetImageData(), nullptr ); + BOOST_CHECK_EQUAL( cempty.GetPPI(), 300 ); + BOOST_CHECK_EQUAL( cempty.GetScale(), 1.0 ); + BOOST_CHECK_EQUAL( cempty.GetPixelScaleFactor(), 1000.0 / 300.0 ); + + // can do this on an empty image + empty.Rotate( true ); + empty.Mirror( true ); +} + + +struct TEST_PIXEL_CASE +{ + int m_x; + int m_y; + KIGFX::COLOR4D m_color; +}; + + +/** + * Check basic properties of the populated images + */ +BOOST_AUTO_TEST_CASE( BasicProps ) +{ + // make sure we can do all this to a const img + const BITMAP_BASE& img = m_4tile; + + // have "some" image data + BOOST_REQUIRE_NE( img.GetImageData(), nullptr ); + + // still default props here + BOOST_CHECK_EQUAL( img.GetPPI(), 300 ); + BOOST_CHECK_EQUAL( img.GetScale(), 5.0 ); + + // we changed this, make sure it's right + BOOST_CHECK_EQUAL( img.GetPixelScaleFactor(), 2.0 ); + + BOOST_CHECK( img.GetSizePixels() == size_4tile ); + BOOST_CHECK( img.GetSize() == size_4tile * 10 ); + + const EDA_RECT bb = img.GetBoundingBox(); + BOOST_CHECK( bb.GetPosition() == wxPoint( -40, -40 ) ); + BOOST_CHECK( bb.GetEnd() == wxPoint( 40, 40 ) ); +} + + +/** + * Check the image is right + */ +BOOST_AUTO_TEST_CASE( BasicImage ) +{ + const wxImage* img_data = m_4tile.GetImageData(); + BOOST_REQUIRE_NE( img_data, nullptr ); + + // green, black, + // red, blue + const std::vector exp_pixels = { + { 1, 1, col_green }, + { 7, 1, col_black }, + { 1, 7, col_red }, + { 7, 7, col_blue }, + }; + + for( const auto& c : exp_pixels ) + { + BOOST_CHECK_PREDICATE( + KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) ); + } +} + +/** + * Check the image is right after rotating + */ +BOOST_AUTO_TEST_CASE( RotateImage ) +{ + // Note the parameter name is wrong here, true is clockwise + m_4tile.Rotate( false ); + + const wxImage* img_data = m_4tile.GetImageData(); + BOOST_REQUIRE_NE( img_data, nullptr ); + + // black, blue, + // green, red + const std::vector exp_pixels = { + { 1, 1, col_black }, + { 7, 1, col_blue }, + { 1, 7, col_green }, + { 7, 7, col_red }, + }; + + for( const auto& c : exp_pixels ) + { + BOOST_CHECK_PREDICATE( + KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) ); + } +} + +/** + * Check the image is right after mirroring + */ +BOOST_AUTO_TEST_CASE( MirrorImage ) +{ + m_4tile.Mirror( true ); + + const wxImage* img_data = m_4tile.GetImageData(); + BOOST_REQUIRE_NE( img_data, nullptr ); + + // red, blue + // green, black + const std::vector exp_pixels = { + { 1, 1, col_red }, + { 7, 1, col_blue }, + { 1, 7, col_green }, + { 7, 7, col_black }, + }; + + for( const auto& c : exp_pixels ) + { + BOOST_CHECK_PREDICATE( + KI_TEST::IsImagePixelOfColor, ( *img_data )( c.m_x )( c.m_y )( c.m_color ) ); + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/qa/common/test_color4d.cpp b/qa/common/test_color4d.cpp index 0afeb18d76..b27a940f3f 100644 --- a/qa/common/test_color4d.cpp +++ b/qa/common/test_color4d.cpp @@ -24,7 +24,8 @@ #include #include -#include +#include "color4d_test_utils.h" + #include #include @@ -37,32 +38,6 @@ using namespace KIGFX; -/** - * Checks if a COLOR4D is close enough to another - */ -bool pred_colour_is_near( const COLOR4D& aCol, const COLOR4D aOther, double aTol ) -{ - return KI_TEST::IsWithin( aCol.r, aOther.r, aTol ) - && KI_TEST::IsWithin( aCol.g, aOther.g, aTol ) - && KI_TEST::IsWithin( aCol.b, aOther.b, aTol ) - && KI_TEST::IsWithin( aCol.a, aOther.a, aTol ); -} - -/** - * Checks if a COLOR4D is close enough to a given RGB char value - */ -bool pred_colour_is_near_hex( - const COLOR4D& aCol, unsigned char r, unsigned char g, unsigned char b, unsigned char a ) -{ - const double tol = 0.5 / 255.0; // One bit of quantised error - - return KI_TEST::IsWithin( aCol.r, r / 255.0, tol ) - && KI_TEST::IsWithin( aCol.g, g / 255.0, tol ) - && KI_TEST::IsWithin( aCol.b, b / 255.0, tol ) - && KI_TEST::IsWithin( aCol.a, a / 255.0, tol ); -} - - /** * Declares a struct as the Boost test fixture. */ @@ -235,7 +210,7 @@ BOOST_AUTO_TEST_CASE( FromHsv ) col.ToHSV( new_h, new_s, new_v ); const unsigned char alpha = 0xFF; - BOOST_CHECK_PREDICATE( pred_colour_is_near_hex, ( col )( c.r )( c.g )( c.b )( alpha ) ); + BOOST_CHECK_PREDICATE( KI_TEST::IsColorNearHex, ( col )( c.r )( c.g )( c.b )( alpha ) ); BOOST_CHECK_CLOSE( c.h, new_h, 0.0001 ); BOOST_CHECK_CLOSE( c.s, new_s, 0.0001 ); BOOST_CHECK_CLOSE( c.v, new_v, 0.0001 ); @@ -277,7 +252,7 @@ BOOST_AUTO_TEST_CASE( FromHsl ) col.ToHSL( new_h, new_s, new_l ); const unsigned char alpha = 0xFF; - BOOST_CHECK_PREDICATE( pred_colour_is_near_hex, ( col )( c.r )( c.g )( c.b )( alpha ) ); + BOOST_CHECK_PREDICATE( KI_TEST::IsColorNearHex, ( col )( c.r )( c.g )( c.b )( alpha ) ); BOOST_CHECK_CLOSE( c.h, new_h, 0.0001 ); BOOST_CHECK_CLOSE( c.s, new_s, 0.0001 ); BOOST_CHECK_CLOSE( c.l, new_l, 0.0001 ); @@ -331,7 +306,7 @@ BOOST_AUTO_TEST_CASE( FromWx ) { const auto col = COLOR4D{ c.wx }; - BOOST_CHECK_PREDICATE( pred_colour_is_near, ( col )( c.c4d )( tol ) ); + BOOST_CHECK_PREDICATE( KI_TEST::IsColorNear, ( col )( c.c4d )( tol ) ); } } diff --git a/qa/common/test_module.cpp b/qa/common/test_module.cpp index 36183b2db7..73bd937a21 100644 --- a/qa/common/test_module.cpp +++ b/qa/common/test_module.cpp @@ -26,13 +26,22 @@ */ #include +#include #include bool init_unit_test() { boost::unit_test::framework::master_test_suite().p_name.value = "Common library module tests"; - return wxInitialize(); + bool ok = wxInitialize(); + + if( ok ) + { + // need these for library image functions + wxInitAllImageHandlers(); + } + + return ok; } diff --git a/qa/common/wximage_test_utils.cpp b/qa/common/wximage_test_utils.cpp new file mode 100644 index 0000000000..13126a481d --- /dev/null +++ b/qa/common/wximage_test_utils.cpp @@ -0,0 +1,78 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wximage_test_utils.h" + +#include "color4d_test_utils.h" + + +namespace KI_TEST +{ +/** + * Predicate to check an image pixel matches color and alpha + * + * @param aImage the image to check + * @param aX pixel x-coordinate + * @param aY pixel y-coordinate + * @param aColor expected color (alpha is 1.0 if image doesn't support alpha) + * @return true if colour match + */ +bool IsImagePixelOfColor( const wxImage& aImage, int aX, int aY, const KIGFX::COLOR4D& aColor ) +{ + const wxSize imageSize = aImage.GetSize(); + + if( imageSize.x < aX || imageSize.y < aY ) + { + BOOST_TEST_INFO( "Pixel (" << aX << ", " << aY << "is not in image of size (" << imageSize.x + << ", " << imageSize.y << ")" ); + return false; + } + + const int r = aImage.GetRed( aX, aY ); + const int g = aImage.GetGreen( aX, aY ); + const int b = aImage.GetBlue( aX, aY ); + + const int a = aImage.HasAlpha() ? aImage.GetAlpha( aX, aY ) : 255; + + if( !KI_TEST::IsColorNearHex( aColor, r, g, b, a ) ) + { + BOOST_TEST_INFO( "Colour doesn't match: got rgba(" << r << ", " << g << ", " << b << ", " + << a << "), expected " << aColor ); + return false; + } + + return true; +} + +} // namespace KI_TEST + + +namespace BOOST_TEST_PRINT_NAMESPACE_OPEN +{ +void print_log_value::operator()( std::ostream& os, wxImage const& aImage ) +{ + const wxSize size = aImage.GetSize(); + os << "wxImage[" << size.x << "x" << size.y << "]"; +} +} // namespace BOOST_TEST_PRINT_NAMESPACE_OPEN +BOOST_TEST_PRINT_NAMESPACE_CLOSE diff --git a/qa/common/wximage_test_utils.h b/qa/common/wximage_test_utils.h new file mode 100644 index 0000000000..eefc2976b9 --- /dev/null +++ b/qa/common/wximage_test_utils.h @@ -0,0 +1,59 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef QA_COMMON_WXIMAGE_TEST_UTILS__H +#define QA_COMMON_WXIMAGE_TEST_UTILS__H + +#include + +#include + +#include + +namespace KI_TEST +{ +/** + * Predicate to check an image pixel matches color and alpha + * + * @param aImage the image to check + * @param aX pixel x-coordinate + * @param aY pixel y-coordinate + * @param aColor expected color (alpha is 1.0 if image doesn't support alpha) + * @return true if colour match + */ +bool IsImagePixelOfColor( const wxImage& aImage, int aX, int aY, const KIGFX::COLOR4D& aColor ); + +} // namespace KI_TEST + + +namespace BOOST_TEST_PRINT_NAMESPACE_OPEN +{ +template <> +struct print_log_value +{ + void operator()( std::ostream& os, wxImage const& aImage ); +}; +} // namespace BOOST_TEST_PRINT_NAMESPACE_OPEN +BOOST_TEST_PRINT_NAMESPACE_CLOSE + +#endif \ No newline at end of file