#include "bmpimage.h" BMPImage::BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, PixelArray pixelArray) : pixelArray(pixelArray) { this->fileHeader = fileHeader; this->infoHeader = infoHeader; } //BMPImage::BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, PixelArray pixelArray) { // //} const uint32_t &BMPImage::width() const { return this->infoHeader.BitmapWidth; } const uint32_t &BMPImage::height() const { return this->infoHeader.BitmapHeight; } void BMPImage::save(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 - byteByRow % 4; for (int i = this->infoHeader.BitmapHeight - 1; i > -1; --i) { ofs.write((char *) pixelArray(i), byteByRow); if (padding != 4) ofs.write(PADDING_ZEROES, padding); // Write padding } } } const PixelArray BMPImage::pixels() { return this->pixelArray; } Pixel **BMPImage::pixels_copy() { Pixel **newPixelArray; newPixelArray = new Pixel *[this->infoHeader.BitmapHeight]; for (int i = 0; i < this->infoHeader.BitmapHeight; ++i) { newPixelArray[i] = new Pixel[this->infoHeader.BitmapWidth]; std::copy(this->pixelArray(i), this->pixelArray(i) + this->infoHeader.BitmapWidth, newPixelArray[i]); } return newPixelArray; } BitmapFileHeader BMPImage::fileHeader_copy() { return this->fileHeader; } BITMAPINFOHEADER BMPImage::infoHeader_copy() { return this->infoHeader; } BMPImage::~BMPImage() { // delete pixelArray; // for (int i = 0; i < this->infoHeader.BitmapHeight; ++i) { // delete[] this->pixelArray(i); // } // delete[] this->pixelArray; } BMPImage::BMPImage(PixelArray pixelArray): pixelArray(pixelArray) { uint32_t width = pixelArray.width(); uint32_t height = pixelArray.height(); BitmapFileHeader fileHeader; BITMAPINFOHEADER infoHeader; infoHeader.BitmapWidth = width; infoHeader.BitmapHeight = height; infoHeader.ImageSize = width * height * 3 + ((4 - width % 4) % 4) * height; fileHeader.fileSize = infoHeader.ImageSize + fileHeader.imageDataOffset; this->infoHeader = infoHeader; this->fileHeader = fileHeader; // this->pixelArray = pixelArray; } BMPImage *BMPImage::appendRight(const BMPImage &img) { uint32_t newHeight = std::max(this->infoHeader.BitmapHeight, img.height()); uint32_t newWidth = this->infoHeader.BitmapWidth + img.width(); // Pixel **newPixelArray; PixelArray newPixelArray(newWidth, newHeight); // newPixelArray = new Pixel *[newHeight]; for (int i = 0; i < this->infoHeader.BitmapHeight; ++i) { // newPixelArray[i] = new Pixel[newWidth]; std::copy(this->pixelArray(i), this->pixelArray(i) + this->infoHeader.BitmapWidth, newPixelArray(i)); } for (int i = 0; i < img.height(); ++i) { std::copy(img.pixels()(i), img.pixelArray(i) + img.width(), newPixelArray(i) + this->infoHeader.BitmapWidth); } return new BMPImage(newPixelArray); } 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 - byteByRow % 4; for (int i = bitmapInfoHeader.BitmapHeight - 1; i > -1; --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}; } Pixel operator+(const Pixel &p1, const Pixel &p2) { const uint8_t r = p1.r + p2.r; const uint8_t g = p1.g + p2.g; const uint8_t b = p1.b + p2.b; return {r, g, b}; } Pixel operator/(const Pixel &p1, const uint8_t &n) { const uint8_t r = p1.r / n; const uint8_t g = p1.g / n; const uint8_t b = p1.b / n; return {r, g, b}; } BMPImage grayscale(BMPImage &img) { auto pixels = img.pixels_copy(); for (int i = 0; i < img.height(); ++i) { for (int j = 0; j < img.width(); ++j) { uint8_t gray = pixels[i][j].r / 3 + pixels[i][j].g / 3 + pixels[i][j].b / 3; pixels[i][j] = {gray, gray, gray}; } } return BMPImage(img.fileHeader_copy(), img.infoHeader_copy(), pixels); } Pixel operator*(const Pixel &p, const int &n) { uint8_t r = ui8_clamp(n * p.r); uint8_t g = ui8_clamp(n * p.g); uint8_t b = ui8_clamp(n * p.b); return {r, g, b}; } uint8_t ui8_clamp(int value, uint8_t min, uint8_t max) { if (value < min) { return min; } if (value > max) { return max; } return value; } BMPImage invertColors(BMPImage &img) { auto pixels = img.pixels_copy(); for (int i = 0; i < img.height(); ++i) { for (int j = 0; j < img.width(); ++j) { pixels[i][j] = 255 - pixels[i][j]; } } return BMPImage(img.fileHeader_copy(), img.infoHeader_copy(), pixels); } Pixel operator-(const uint8_t &n, const Pixel &p) { uint8_t r = ui8_clamp(n - p.r); uint8_t g = ui8_clamp(n - p.g); uint8_t b = ui8_clamp(n - p.b); return {r, g, b}; } Pixel operator-(const Pixel &p, const uint8_t &n) { uint8_t r = ui8_clamp(p.r - n); uint8_t g = ui8_clamp(p.g - n); uint8_t b = ui8_clamp(p.b - n); return {r, g, b}; } BMPImage upscale2x(BMPImage &img) { auto oldPixels = img.pixels(); Pixel **newPixelArray; const uint32_t newHeight = img.height() * 2; const uint32_t newWidth = img.width() * 2; newPixelArray = new Pixel *[newHeight]; for (int i = 0; i < newHeight; i += 2) { newPixelArray[i] = new Pixel[newWidth]; for (int j = 0; j < newWidth; ++j) { if (j % 2 == 0) newPixelArray[i][j] = oldPixels[i / 2][j / 2]; else if (j == newWidth - 1) newPixelArray[i][j] = oldPixels[i / 2][j / 2] / 2; else newPixelArray[i][j] = oldPixels[i / 2][j / 2] / 2 + oldPixels[i / 2][j / 2 + 1] / 2; } } for (int i = 1; i < newHeight; i += 2) { newPixelArray[i] = new Pixel[newWidth]; if (i == newHeight - 1) for (int j = 0; j < newWidth; ++j) { newPixelArray[i][j] = newPixelArray[i - 1][j] / 2; } else for (int j = 0; j < newWidth; ++j) { newPixelArray[i][j] = newPixelArray[i - 1][j] / 2 + newPixelArray[i + 1][j] / 2; } } return BMPImage(newPixelArray, newWidth, newHeight); } BMPImage downscale2x(BMPImage &img) { Pixel **newPixelArray; auto oldPixels = img.pixels_copy(); const uint32_t newHeight = img.height() / 2; const uint32_t newWidth = img.width() / 2; newPixelArray = new Pixel *[newHeight]; for (int i = 0; i < newHeight; ++i) { newPixelArray[i] = new Pixel[newWidth]; for (int j = 0; j < newWidth; ++j) { newPixelArray[i][j] = oldPixels[i * 2][j * 2]; } } return BMPImage(newPixelArray, newWidth, newHeight); } BMPImage upscale1_5x(BMPImage &img) { auto oldPixels = img.pixels(); Pixel **newPixelArray; const uint32_t newHeight = img.height() * 3 / 2; const uint32_t newWidth = img.width() * 3 / 2; newPixelArray = new Pixel *[newHeight]; for (int i = 0; i < newHeight; ++i) { newPixelArray[i] = new Pixel[newWidth]; if ((i + 1) % 3 == 0) continue; for (int j = 0; j < newWidth; ++j) { int oldi = i * 2 / 3; int oldj = j * 2 / 3; if ((j + 1) % 3 != 0) newPixelArray[i][j] = oldPixels[oldi][oldj]; else if (j == newWidth - 1) newPixelArray[i][j] = oldPixels[oldi][oldj] / 2; else newPixelArray[i][j] = oldPixels[oldi][oldj] / 2 + oldPixels[oldi][oldj + 1] / 2; } } for (int i = 2; i < newHeight; i += 3) { if (i == newHeight - 1) for (int j = 0; j < newWidth; ++j) { newPixelArray[i][j] = newPixelArray[i - 1][j] / 2; } else for (int j = 0; j < newWidth; ++j) { newPixelArray[i][j] = newPixelArray[i - 1][j] / 2 + newPixelArray[i + 1][j] / 2; } } return BMPImage(newPixelArray, newWidth, newHeight); } PixelArray::PixelArray(uint32_t width, uint32_t height) { this->_width = width; this->_height = height; this->array = new Pixel *[height]; for (int i = 0; i < height; ++i) { this->array[i] = new Pixel[width]; } } Pixel PixelArray::operator()(const uint32_t &i, const uint32_t &j) { return this->array[i][j]; } PixelArray::~PixelArray() { for (int i = 0; i < this->_height; ++i) { delete[] this->array[i]; } delete[] this->array; } PixelArray::PixelArray(const PixelArray &pA) { this->_width = pA.width(); this->_height = pA.height(); this->array = pA.array; } Pixel* PixelArray::operator()(const uint32_t &i) { return this->array[i]; } const uint32_t &PixelArray::width() const { return this->_width; } const uint32_t &PixelArray::height() const { return this->_height; }