diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b0a302..10423a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,6 @@ project(image_test_cpp) set(CMAKE_CXX_STANDARD 17) -add_executable(image_test_cpp main.cpp) +add_executable(image_test_cpp main.cpp + bmpimage.h + bmpimage.cpp) diff --git a/bmpimage.cpp b/bmpimage.cpp new file mode 100644 index 0000000..87e9565 --- /dev/null +++ b/bmpimage.cpp @@ -0,0 +1,68 @@ +#include "bmpimage.h" + +BMPImage::BMPImage(BitmapFileHeader &fileHeader, BITMAPINFOHEADER &infoHeader, Pixel **pixelArray) { + this->fileHeader = fileHeader; + this->infoHeader = infoHeader; + this->pixelArray = pixelArray; +} + +const uint32_t &BMPImage::width() const { return this->infoHeader.BitmapWidth; } + +const uint32_t &BMPImage::height() const { return this->infoHeader.BitmapHeight; } + +void BMPImage::write(const std::string &filename) { + { + std::ofstream ofs(filename, std::ios_base::binary); + ofs.write((char *) &this->fileHeader, sizeof(this->fileHeader)); + ofs.write((char *) &this->infoHeader, sizeof(this->infoHeader)); + uint32_t byteByRow = this->infoHeader.BitmapWidth * 3; + uint8_t padding = 4 - this->infoHeader.BitmapWidth % 4; + for (int i = 0; i < this->infoHeader.BitmapHeight; ++i) { + ofs.write((char *) pixelArray[i], byteByRow); + if (padding != 4) ofs.write(PADDING_ZEROES, padding); // Write padding + } + } +} + +BMPImage readBMPImage(const std::string &filename) { + BitmapFileHeader bitmapFileHeader; + BITMAPINFOHEADER bitmapInfoHeader; + Pixel **pixelArray; + uint32_t DIB_Header_Size; + { + std::ifstream ifs(filename, std::ios_base::binary); + if (!ifs.good()) { + throw std::runtime_error("File read error"); + } + ifs.seekg(0, std::ios::beg); + ifs.read((char *) &bitmapFileHeader, sizeof(bitmapFileHeader)); + ifs.read((char *) &DIB_Header_Size, sizeof(DIB_Header_Size)); + } + if (DIB_Header_Size != 40) { + throw std::runtime_error("Invalid header"); + } + { + std::ifstream ifs(filename, std::ios_base::binary); + if (!ifs.good()) { + throw std::runtime_error("File read error"); + } + ifs.seekg(14, std::ios::beg); + ifs.read((char *) &bitmapInfoHeader, sizeof(bitmapInfoHeader)); + } + pixelArray = new Pixel *[bitmapInfoHeader.BitmapHeight]; + { + std::ifstream ifs(filename, std::ios_base::binary); + if (!ifs.good()) { + throw std::runtime_error("File read error"); + } + ifs.seekg(bitmapFileHeader.imageDataOffset, std::ios::beg); + uint32_t byteByRow = bitmapInfoHeader.BitmapWidth * 3; + uint8_t padding = 4 - bitmapInfoHeader.BitmapWidth % 4; + for (int i = 0; i < bitmapInfoHeader.BitmapHeight; ++i) { + pixelArray[i] = new Pixel[bitmapInfoHeader.BitmapWidth]; + ifs.read((char *) pixelArray[i], byteByRow); + if (padding != 4) ifs.seekg(padding, std::ios_base::cur); // Skip padding + } + } + return {bitmapFileHeader, bitmapInfoHeader, pixelArray}; +} diff --git a/bmpimage.h b/bmpimage.h new file mode 100644 index 0000000..468cf98 --- /dev/null +++ b/bmpimage.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +const char PADDING_ZEROES[3] = {0, 0, 0}; + +#pragma pack(push, 1) +struct BitmapFileHeader { + char signature[2] = {0, 0}; + uint32_t fileSize = 0; + uint16_t reserved1 = 0; + uint16_t reserved2 = 0; + uint32_t imageDataOffset = 0; +}; +#pragma pack(pop) + +#pragma pack(push, 1) +struct BITMAPINFOHEADER { + uint32_t HeaderSize = 0; + uint32_t BitmapWidth = 0; + uint32_t BitmapHeight = 0; + uint16_t ColorPlanes = 0; + uint16_t BitsPerPixel = 0; + uint32_t CompressionMethod = 0; + uint32_t ImageSize = 0; + int32_t HorizontalPixelPerMetre = 0; + int32_t VerticalPixelPerMetre = 0; + uint32_t NumberOfColors = 0; + uint32_t NumberOfImportantColors = 0; +}; +#pragma pack(pop) + +#pragma pack(push, 1) +struct Pixel { + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; +}; +#pragma pack(pop) + +class BMPImage { + BitmapFileHeader fileHeader; + BITMAPINFOHEADER infoHeader; + Pixel **pixelArray; +public: + BMPImage(BitmapFileHeader &fileHeader, BITMAPINFOHEADER &infoHeader, Pixel **pixelArray); + + [[nodiscard]] const uint32_t &width() const; + + [[nodiscard]] const uint32_t &height() const; + + void write(const std::string &); +}; + +BMPImage readBMPImage(const std::string &filename); diff --git a/main.cpp b/main.cpp index b7bd61f..d0ba901 100644 --- a/main.cpp +++ b/main.cpp @@ -1,27 +1,12 @@ #include -#include -#include -#include +#include "bmpimage.h" -struct BitmapFileHeader { - char signature[2] = {0, 0}; - uint32_t fileSize = 0; - uint16_t reserved1 = 0; - uint16_t reserved2 = 0; - uint32_t imageDataOffset = 0; -}; +const std::string FILENAME = "../elef.bmp"; +const std::string FILENAME_OUT = "../elef_out.bmp"; -const std::string FILENAME = "elef.bmp"; int main() { - BitmapFileHeader bitmapFileHeader; - { - std::ifstream ifs(FILENAME, std::ios_base::binary); - ifs.seekg(0, std::ios::beg); - ifs.read((char *) &bitmapFileHeader, 14); - } - std::cout << "Structure:\n" << "Signature: " << std::string(bitmapFileHeader.signature, 2) << "\nFile size: " - << bitmapFileHeader.fileSize << "\nReserved 1: " << bitmapFileHeader.reserved1 << "\nReserved 2: " - << bitmapFileHeader.reserved2 << "\nImage data offset: " << bitmapFileHeader.imageDataOffset << std::endl; + auto image = readBMPImage(FILENAME); + image.write(FILENAME_OUT); return 0; }