diff --git a/Exec/main.cpp b/Exec/main.cpp index 6e0fde2..38d2fde 100644 --- a/Exec/main.cpp +++ b/Exec/main.cpp @@ -6,7 +6,7 @@ using namespace Leonetienne::BmpPP; int main() { - /* + BMP bmp({800, 600}, Colormode::RGB); for (int x = 0; x < 800; x++) @@ -20,7 +20,9 @@ int main() { ); } - */ + bmp.Write("write.bmp"); + + /* BMP bmp; @@ -32,7 +34,7 @@ int main() { if (!bmp.Write("testwrite.bmp")) std::cerr << "What the hell" << std::endl; - +*/ return 0; } diff --git a/Src/BMP.h b/Src/BMP.h index fab1963..0e52054 100644 --- a/Src/BMP.h +++ b/Src/BMP.h @@ -2,6 +2,7 @@ #define BMPPP_BMP_H #include +#include #include #include #include "Colormodes.h" @@ -87,6 +88,9 @@ namespace Leonetienne::BmpPP { //! Will swap two channels. Useful for, for example, easy BGR to RGB conversion. void SwapChannels(const std::size_t& channel1, const std::size_t& channel2); + //! Will copy the specified rectangle-area, and return it as a new image + BMP Crop(const Eule::Rect& area); + private: Eule::Vector2i size; Colormode colormode; diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 7b85765..d4d3853 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -21,4 +21,12 @@ add_executable(Test BmpHeader.cpp ReInitialize.cpp Uninitialized.cpp + Read.cpp +) + +# Move test images to build dir +ADD_CUSTOM_COMMAND( + TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/TestAssets/ $ ) diff --git a/Test/Read.cpp b/Test/Read.cpp new file mode 100644 index 0000000..08a0e21 --- /dev/null +++ b/Test/Read.cpp @@ -0,0 +1,131 @@ +#include +#include "Catch2.h" +#include +#include + +using namespace Leonetienne::BmpPP; +using namespace Eule; + +#define IMSIZE Vector2i(800, 600) + +namespace { + inline std::tuple + ColorGradient(const Vector2i& pos) + { + std::uint8_t r, g, b, a; + + // This assumes IMSIZE.x >= IMSIZE.y + + r = ((float)pos.x / (float)IMSIZE.x) * 255.0f; + g = (1.0f - (float)pos.x / (float)IMSIZE.x) * 255.0f; + b = (1.0f - (float)pos.y / (float)IMSIZE.x) * 255.0f; + a = Math::Clamp(((float)pos.y / (float)IMSIZE.x) * 2 * 255.0f, 0.0, 255.0); + + return std::make_tuple(r, g, b, a); + } +} + +// Tests that reading an image works at all, without throwing an exception or crashing the program +TEST_CASE(__FILE__"/ReadingDoesntCrash", "[Read]") +{ + SECTION("RGB image with BITMAPV5HEADER") { + // Read RGB gradient image + BMP bmp("base_gradient.bmp"); + } + + SECTION("RGBA image with BITMAPV5HEADER") { + // Read RGBA gradient image + BMP bmp("basea_gradient.bmp"); + } + + return; +} + +// Tests that an image is initialized after being read +TEST_CASE(__FILE__"/InitializedAfterReading", "[Read]") +{ + // Read RGB gradient image + BMP bmp("base_gradient.bmp"); + + REQUIRE(bmp.IsInitialized()); + + return; +} + +// Tests that images read the correct metadata (bit depth and resolution) +TEST_CASE(__FILE__"/MetadataIsCorrect", "[Read]") +{ + // Read RGB gradient image + SECTION("RGB image with BITMAPV5HEADER") { + // Read RGB gradient image + BMP bmp("base_gradient.bmp"); + + REQUIRE(bmp.GetDimensions() == IMSIZE); + REQUIRE(bmp.GetColormode() == Colormode::RGB); + } + + SECTION("RGBA image with BITMAPV5HEADER") { + // Read RGBA gradient image + BMP bmp("basea_gradient.bmp"); + + REQUIRE(bmp.GetDimensions() == IMSIZE); + REQUIRE(bmp.GetColormode() == Colormode::RGBA); + } + + return; +} + +// Tests that reading an image works, by comparing the resulting pixel +// values with the algorithm that created them +TEST_CASE(__FILE__"/CompareRGB", "[Read]") +{ + // Read RGB gradient image + BMP bmp("base_gradient.bmp"); + + // Compare each pixel to the algorithm that has once created it + for (std::size_t x = 0; x < bmp.GetDimensions().x; x++) + for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) { + + const auto expected_pixel_values = ColorGradient(Vector2i(x, y)); + const uint8_t* pxBase = bmp.GetPixel(Vector2i(x, y)); + const auto actual_pixel_values = + std::make_tuple( + *(pxBase + 0), + *(pxBase + 1), + *(pxBase + 2), + std::get<3>(expected_pixel_values) // Since our image doesn't contain an ALPHA channel, let's copy it from the expected value + ); + + REQUIRE(actual_pixel_values == expected_pixel_values); + } + + return; +} + +// Tests that reading an image works, by comparing the resulting pixel +// values with the algorithm that created them +TEST_CASE(__FILE__"/CompareRGBA", "[Read]") +{ + // Read RGBA gradient image + BMP bmp("basea_gradient.bmp"); + + // Compare each pixel to the algorithm that has once created it + for (std::size_t x = 0; x < bmp.GetDimensions().x; x++) + for (std::size_t y = 0; y < bmp.GetDimensions().y; y++) { + + const auto expected_pixel_values = ColorGradient(Vector2i(x, y)); + const uint8_t* pxBase = bmp.GetPixel(Vector2i(x, y)); + const auto actual_pixel_values = + std::make_tuple( + *(pxBase + 0), + *(pxBase + 1), + *(pxBase + 2), + *(pxBase + 3) + ); + + REQUIRE(actual_pixel_values == expected_pixel_values); + } + + return; +} +#undef IMSIZE diff --git a/Test/TestAssets/.gitignore b/Test/TestAssets/.gitignore new file mode 100644 index 0000000..f10f2a3 --- /dev/null +++ b/Test/TestAssets/.gitignore @@ -0,0 +1,2 @@ +# Allow images used for tests +!*.bmp diff --git a/Test/TestAssets/base_complex.bmp b/Test/TestAssets/base_complex.bmp new file mode 100644 index 0000000..b82fdbb Binary files /dev/null and b/Test/TestAssets/base_complex.bmp differ diff --git a/Test/TestAssets/base_gradient.bmp b/Test/TestAssets/base_gradient.bmp new file mode 100644 index 0000000..d93ba97 Binary files /dev/null and b/Test/TestAssets/base_gradient.bmp differ diff --git a/Test/TestAssets/basea_complex.bmp b/Test/TestAssets/basea_complex.bmp new file mode 100644 index 0000000..40d55b1 Binary files /dev/null and b/Test/TestAssets/basea_complex.bmp differ diff --git a/Test/TestAssets/basea_gradient.bmp b/Test/TestAssets/basea_gradient.bmp new file mode 100644 index 0000000..1aeb39e Binary files /dev/null and b/Test/TestAssets/basea_gradient.bmp differ diff --git a/Test/TestAssets/readme.md b/Test/TestAssets/readme.md new file mode 100644 index 0000000..b078ae1 --- /dev/null +++ b/Test/TestAssets/readme.md @@ -0,0 +1 @@ +base*.bmp is always RGB, basea*.bmp is always RGBA. diff --git a/Test/Uninitialized.cpp b/Test/Uninitialized.cpp index fb73221..3a8a090 100644 --- a/Test/Uninitialized.cpp +++ b/Test/Uninitialized.cpp @@ -3,6 +3,7 @@ #include "Catch2.h" using namespace Leonetienne::BmpPP; +using namespace Eule; // Tests that trying to interrogate any getter/Write() on an uninitialized image results in a runtime error TEST_CASE(__FILE__"/RuntimeErrorOnUninitialized", "[Uninitialized]") @@ -65,7 +66,7 @@ TEST_CASE(__FILE__"/UninitializedImageIsUninitialized", "[Uninitialized]") // Tests that an image constructed via dimensions is initialized TEST_CASE(__FILE__"/ConstructedByDimensionsIsInitialized", "[Uninitialized]") { - BMP bmp({800, 600}); + BMP bmp(Vector2i(800, 600)); REQUIRE(bmp.IsInitialized()); return;