diff --git a/Src/BMP.cpp b/Src/BMP.cpp index 07a1e1b..d7162a6 100644 --- a/Src/BMP.cpp +++ b/Src/BMP.cpp @@ -3,6 +3,7 @@ #include #include "BmpWriter.h" #include "BmpReader.h" +#include #define CHECK_IF_INITIALIZED if (!isInitialized) throw std::runtime_error("Image not initialized!"); @@ -99,6 +100,36 @@ namespace Leonetienne::BmpPP { return pixelBuffer.data() + pixelIndex; } + void BMP::SetPixel(const Eule::Vector2i &position, + const std::uint8_t v) + { + CHECK_IF_INITIALIZED + + std::uint8_t* pixel = GetPixel(position); + + pixel[0] = v; + pixel[1] = v; + pixel[2] = v; + + return; + } + + void BMP::SetPixel(const Eule::Vector2i &position, + const std::uint8_t r, + const std::uint8_t g, + const std::uint8_t b) + { + CHECK_IF_INITIALIZED + + std::uint8_t* pixel = GetPixel(position); + + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + + return; + } + void BMP::SetPixel(const Eule::Vector2i &position, const std::uint8_t r, const std::uint8_t g, @@ -380,6 +411,61 @@ namespace Leonetienne::BmpPP { return bmp; } + void BMP::ConvertColormode(const Colormode &colormode) { + CHECK_IF_INITIALIZED + + // Do we already have the target color mode? + if (this->colormode == colormode) + return; + + // So, we actually have to do something. Darn it. + // Move the old pixel buffer into a a.. well.. buffer. We're gonna need it + const std::vector oldPixelBuffer = std::move(pixelBuffer); + + // Re-initialize our image with our new color mode + ReInitialize(size, colormode); + + // What should we convert then? + switch (colormode) { // this is the PARAMETER! + // Convert RGBA to RGB. + // Just drop the alpha channel. + case Colormode::RGB: { + // Jump through the old pixel buffer, four bytes at a time + for (std::size_t i = 0; i < oldPixelBuffer.size(); i += 4) { + // Per jump, copy three bytes into the new one. These are r,g,b. + std::copy( + oldPixelBuffer.cbegin() + i, + oldPixelBuffer.cbegin() + i + 3, + pixelBuffer.begin() + i - (i/4) // Subtract one byte per pixel, because the new pixelBuffer is RGB, not RGBA. + ); + } + + // Done. + } + return; + + // Convert RGB to RGBA. + // Just fill the new alpha channel with 0xFF. + case Colormode::RGBA: { + // Jump through the old pixel buffer, three bytes at a time + for (std::size_t i = 0; i < oldPixelBuffer.size(); i += 3) { + // Per jump, copy these three bytes into the new one. These are r,g,b. + std::copy( + oldPixelBuffer.cbegin() + i, + oldPixelBuffer.cbegin() + i + 3, + pixelBuffer.begin() + i + (i/3) // Add one byte per pixel, because the new pixelBuffer is RGBA, not RGB. + ); + } + + // The alpha channel should already be set to 0xFF, by ReInitialize(). + + // Done. + } + break; + } + + return; + } } #undef CHECK_IF_INITIALIZED diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index ace6929..c3b2ed7 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(Test MirrorVertically.cpp SwapChannels.cpp Rotate.cpp + ConvertColormode.cpp ) # Move test images to build dir diff --git a/Test/ConvertColormode.cpp b/Test/ConvertColormode.cpp new file mode 100644 index 0000000..d4718cc --- /dev/null +++ b/Test/ConvertColormode.cpp @@ -0,0 +1,68 @@ +#include +#include +#include "Catch2.h" + +using namespace Leonetienne::BmpPP; +using namespace Eule; + +// Tests converting between color modes works +TEST_CASE(__FILE__"/Converting_between_color_modes_works", "[Conversion]") +{ + SECTION("RGB to RGB") { + // Read an RGB image + BMP bmp("base_kyokucho.bmp"); + + // Convert it to RGB + bmp.ConvertColormode(Colormode::RGB); + + // Read reference image + const BMP reference("base_kyokucho.bmp"); + + // Assert that they are equal + REQUIRE(bmp == reference); + } + + SECTION("RGB to RGBA") { + // Read an RGB image + BMP bmp("base_kyokucho.bmp"); + + // Convert it to RGBA + bmp.ConvertColormode(Colormode::RGBA); + + // Read reference image + const BMP reference("basea_kyokucho.bmp"); + + // Assert that they are equal + REQUIRE(bmp == reference); + } + + SECTION("RGBA to RGB") { + // Read an RGBA image + BMP bmp("basea_kyokucho.bmp"); + + // Convert it to RGB + bmp.ConvertColormode(Colormode::RGB); + + // Read reference image + const BMP reference("base_kyokucho.bmp"); + + // Assert that they are equal + REQUIRE(bmp == reference); + } + + SECTION("RGBA to RGBA") { + // Read an RGBA image + BMP bmp("basea_kyokucho.bmp"); + + // Convert it to RGBA + bmp.ConvertColormode(Colormode::RGBA); + + // Read reference image + const BMP reference("basea_kyokucho.bmp"); + + // Assert that they are equal + REQUIRE(bmp == reference); + } + + return; +} diff --git a/Test/TestAssets/base_kyokucho.bmp b/Test/TestAssets/base_kyokucho.bmp new file mode 100644 index 0000000..48285ad Binary files /dev/null and b/Test/TestAssets/base_kyokucho.bmp differ diff --git a/Test/TestAssets/basea_kyokucho.bmp b/Test/TestAssets/basea_kyokucho.bmp new file mode 100644 index 0000000..e28bf62 Binary files /dev/null and b/Test/TestAssets/basea_kyokucho.bmp differ