diff --git a/Src/BMP.cpp b/Src/BMP.cpp index 8494c6d..894102e 100644 --- a/Src/BMP.cpp +++ b/Src/BMP.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "BmpHeader.h" +#include "BmpWriter.h" #define CHECK_IF_INITIALIZED if (!isInitialized) throw std::runtime_error("Image not initialized!"); @@ -53,118 +53,19 @@ namespace Leonetienne::BmpPP { bool BMP::Write(const std::string &filename) const { CHECK_IF_INITIALIZED - // Create the bmp header - BmpHeader bmpHeader; + return BmpWriter::Write(*this, filename); + } - // Populate file header - bmpHeader.fileHeader.addressPixelBuffer = - BmpHeader::FileHeader::NBYTES + - BmpHeader::DibHeader::NBYTES - + 2; // We have to put two padding-bytes behind the headers. - // The pixel data must start at an address that is a multiple of 4. + bool BMP::Read(const std::string &filename) { - // The size of the bmp file is not yet known... - - // Populate dib header - bmpHeader.dibHeader.imageWidth = size.x; - bmpHeader.dibHeader.imageHeight = size.y; - bmpHeader.dibHeader.numBitsPerPixel = GetNumColorChannels() * 8; - // The size of the pixel array is not known yet (because rows require to be padded) - - - // Pack pixel values - std::vector packedPixels; - packedPixels.reserve(pixelBuffer.size()); - - // How many channels do we have? - const std::size_t numChannels = GetNumColorChannels(); - - // Calculate how many padding bytes to add per row - std::size_t paddingBytesPerRow = (4 - ((size.x * numChannels) % 4)) % 4; - - // Iterate over all pixel rows - for (std::size_t y = 0; y < size.y; y++) { - const std::size_t rowIndex = y * size.x * numChannels; - - for (std::size_t x = 0; x < size.x; x++) { - const std::size_t pixelIndex = rowIndex + x * numChannels; - - // Write actual pixel values to row - switch (colormode) { - case Colormode::RGBA: - // Write B byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 2] - ); - // Write G byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 1] - ); - // Write R byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 0] - ); - // Write A byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 3] - ); - break; - - case Colormode::RGB: - // Write B byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 2] - ); - // Write G byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 1] - ); - // Write R byte - packedPixels.insert( - packedPixels.cend(), - pixelBuffer[pixelIndex + 0] - ); - break; - } - - // Add row padding - // Since we have to pad to a multiple of 4, the padding will never be more than four bytes - for (std::size_t i = 0; i < paddingBytesPerRow; i++) - packedPixels.insert(packedPixels.cend(), 0x0); - } - } - - // Now we can finally set the fileSize field in the file header, - // and the pixelArraySize in the dib header - bmpHeader.fileHeader.filesize = - bmpHeader.fileHeader.addressPixelBuffer + - packedPixels.size(); - - bmpHeader.dibHeader.pixelArraySize = packedPixels.size(); - - // Write to file - std::ofstream ofs(filename, std::ofstream::binary); - if (!ofs.good()) + std::ifstream ifs(filename, std::ifstream::binary); + if (!ifs.good()) return false; + // When reading the image, we don't care about most of the header. + // All we want is the images resolutions, bits-per-pixel, and pixelbuffer address... + // This is because we do not support ALL of bmp, but just the most common formats... - // Write the header - const std::vector headerBytes = bmpHeader.ToBytes(); - ofs.write((const char*)headerBytes.data(), headerBytes.size()); - - // Write two padding bytes - ofs.write("\0\0", 2); - - // Write the pixel data - ofs.write((const char*)packedPixels.data(), packedPixels.size()); - - ofs.close(); return true; } diff --git a/Src/BMP.h b/Src/BMP.h index 9019f16..59ff50b 100644 --- a/Src/BMP.h +++ b/Src/BMP.h @@ -8,6 +8,8 @@ namespace Leonetienne::BmpPP { + class BmpWriter; + class BMP { public: BMP(); @@ -53,11 +55,17 @@ namespace Leonetienne::BmpPP { //! Returns false, if unable to open the file bool Write(const std::string& filename) const; + //! Will read a bmp image from a file. + //! Returns false, if unable to open, or parse, file + bool Read(const std::string& filename); + private: Eule::Vector2i size; Colormode colormode; std::vector pixelBuffer; bool isInitialized = false; + + friend class BmpWriter; }; } diff --git a/Src/BmpWriter.cpp b/Src/BmpWriter.cpp new file mode 100644 index 0000000..e2d5fff --- /dev/null +++ b/Src/BmpWriter.cpp @@ -0,0 +1,124 @@ +#include "BmpWriter.h" +#include "BmpHeader.h" +#include "Bmp.h" +#include + +namespace Leonetienne::BmpPP { + + bool BmpWriter::Write(const BMP &image, const std::string& filename) { + // Create the bmp header + BmpHeader bmpHeader; + + // Populate file header + bmpHeader.fileHeader.addressPixelBuffer = + BmpHeader::FileHeader::NBYTES + + BmpHeader::DibHeader::NBYTES + + 2; // We have to put two padding-bytes behind the headers. + // The pixel data must start at an address that is a multiple of 4. + + // The size of the bmp file is not yet known... + + // Populate dib header + bmpHeader.dibHeader.imageWidth = image.size.x; + bmpHeader.dibHeader.imageHeight = image.size.y; + bmpHeader.dibHeader.numBitsPerPixel = image.GetNumColorChannels() * 8; + // The size of the pixel array is not known yet (because rows require to be padded) + + + // Pack pixel values + std::vector packedPixels; + packedPixels.reserve(image.pixelBuffer.size()); + + // How many channels do we have? + const std::size_t numChannels = image.GetNumColorChannels(); + + // Calculate how many padding bytes to add per row + std::size_t paddingBytesPerRow = (4 - ((image.size.x * numChannels) % 4)) % 4; + + // Iterate over all pixel rows + for (std::size_t y = 0; y < image.size.y; y++) { + const std::size_t rowIndex = y * image.size.x * numChannels; + + for (std::size_t x = 0; x < image.size.x; x++) { + const std::size_t pixelIndex = rowIndex + x * numChannels; + + // Write actual pixel values to row + switch (image.colormode) { + case Colormode::RGBA: + // Write B byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 2] + ); + // Write G byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 1] + ); + // Write R byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 0] + ); + // Write A byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 3] + ); + break; + + case Colormode::RGB: + // Write B byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 2] + ); + // Write G byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 1] + ); + // Write R byte + packedPixels.insert( + packedPixels.cend(), + image.pixelBuffer[pixelIndex + 0] + ); + break; + } + + // Add row padding + // Since we have to pad to a multiple of 4, the padding will never be more than four bytes + for (std::size_t i = 0; i < paddingBytesPerRow; i++) + packedPixels.insert(packedPixels.cend(), 0x0); + } + } + + // Now we can finally set the fileSize field in the file header, + // and the pixelArraySize in the dib header + bmpHeader.fileHeader.filesize = + bmpHeader.fileHeader.addressPixelBuffer + + packedPixels.size(); + + bmpHeader.dibHeader.pixelArraySize = packedPixels.size(); + + // Write to file + std::ofstream ofs(filename, std::ofstream::binary); + if (!ofs.good()) + return false; + + + // Write the header + const std::vector headerBytes = bmpHeader.ToBytes(); + ofs.write((const char*)headerBytes.data(), headerBytes.size()); + + // Write two padding bytes + ofs.write("\0\0", 2); + + // Write the pixel data + ofs.write((const char*)packedPixels.data(), packedPixels.size()); + + ofs.close(); + + return true; + } +} diff --git a/Src/BmpWriter.h b/Src/BmpWriter.h new file mode 100644 index 0000000..05ccfbb --- /dev/null +++ b/Src/BmpWriter.h @@ -0,0 +1,17 @@ +#ifndef TEST_BMPWRITER_H +#define TEST_BMPWRITER_H + +#include + +namespace Leonetienne::BmpPP { + + class BMP; + + class BmpWriter { + public: + static bool Write(const BMP& image, const std::string& filename); + }; + +} + +#endif //TEST_BMPWRITER_H diff --git a/Src/CMakeLists.txt b/Src/CMakeLists.txt index f66c0aa..6960093 100644 --- a/Src/CMakeLists.txt +++ b/Src/CMakeLists.txt @@ -11,5 +11,6 @@ add_library(BmpPP ${Eule} BMP.cpp + BmpWriter.cpp BmpHeader.cpp )