#include #include #include #include #include "bmpimage.h" BMPImage::BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, std::shared_ptr pixelArray) { this->fileHeader = fileHeader; this->infoHeader = infoHeader; this->pixelArray = std::move(pixelArray); } //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) % 4; for (auto i = (int) this->infoHeader.BitmapHeight - 1; i > -1; --i) { ofs.write((char *) (*pixelArray)(i), byteByRow); if (padding != 4) ofs.write(PADDING_ZEROES, padding); // Write padding } } } std::shared_ptr BMPImage::pixels() const { return this->pixelArray; } std::shared_ptr BMPImage::pixels_copy() { // Pixel **newPixelArray; std::shared_ptr newPixelArray = std::make_shared(this->infoHeader.BitmapWidth, this->infoHeader.BitmapHeight); // 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)); // TODO for (int j = 0; j < this->infoHeader.BitmapWidth; ++j) { (*newPixelArray)(i, j) = (*this->pixelArray)(i, j); // TODO: Сделать конструктор копирования в PixelArray } } return newPixelArray; } BitmapFileHeader BMPImage::fileHeader_copy() { return this->fileHeader; } BITMAPINFOHEADER BMPImage::infoHeader_copy() { return this->infoHeader; } BMPImage::BMPImage(std::shared_ptr 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 * 3) % 4) % 4) * height; _fileHeader.fileSize = _infoHeader.ImageSize + _fileHeader.imageDataOffset; this->infoHeader = _infoHeader; this->fileHeader = _fileHeader; this->pixelArray = pixelArray; } std::shared_ptr BMPImage::appendRight(const std::shared_ptr& img) { uint32_t newHeight = std::max(this->infoHeader.BitmapHeight, img->height()); uint32_t newWidth = this->infoHeader.BitmapWidth + img->width(); // Pixel **newPixelArray; std::shared_ptr newPixelArray = std::make_shared(newWidth, newHeight); // std::shared_ptr newPixelArray = std::make_shared(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 std::make_shared(newPixelArray); } std::shared_ptr BMPImage::overlay(const std::shared_ptr& img, uint32_t pos_x, uint32_t pos_y) { if (pos_x + img->width() > this->width() || pos_y + img->height() > this->height()) throw std::runtime_error("The overlaid image is outside the image"); auto pixels = this->pixels_copy(); for (int i = 0; i < img->height(); ++i) { std::copy((*img->pixels())(i), (*img->pixels())(i) + img->width(), (*pixels)(pos_y + i) + pos_x); } return std::make_shared(pixels); } std::shared_ptr BMPImage::applyFilter(const std::function &)> &filter) { auto origPixels = this->pixels_copy(); std::shared_ptr pixels = std::make_shared(this->width(), this->height()); for (int y = 1; y < this->height() - 1; ++y) { for (int x = 1; x < this->width() - 1; ++x) { auto p1 = (*origPixels)(y - 1, x - 1); auto p2 = (*origPixels)(y - 1, x); auto p3 = (*origPixels)(y - 1, x + 1); auto p4 = (*origPixels)(y, x - 1); auto p5 = (*origPixels)(y, x); auto p6 = (*origPixels)(y, x + 1); auto p7 = (*origPixels)(y + 1, x - 1); auto p8 = (*origPixels)(y + 1, x); auto p9 = (*origPixels)(y + 1, x + 1); std::array red_channel = {p1.r, p2.r, p3.r, p4.r, p5.r, p6.r, p7.r, p8.r, p9.r}; std::array green_channel = {p1.g, p2.g, p3.g, p4.g, p5.g, p6.g, p7.g, p8.g, p9.g}; std::array blue_channel = {p1.b, p2.b, p3.b, p4.b, p5.b, p6.b, p7.b, p8.b, p9.b}; auto r = filter(red_channel); auto g = filter(green_channel); auto b = filter(blue_channel); (*pixels)(y, x) = {r, g, b}; } } return std::make_shared(pixels); } std::shared_ptr readBMPImage(const std::string &filename) { BitmapFileHeader bitmapFileHeader; BITMAPINFOHEADER bitmapInfoHeader; 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)); } std::shared_ptr pixelArray = std::make_shared(bitmapInfoHeader.BitmapWidth, 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) % 4; for (auto i = (int) 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 std::make_shared(bitmapFileHeader, bitmapInfoHeader, pixelArray); } std::shared_ptr grayscale(const std::shared_ptr& 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 std::make_shared(img->fileHeader_copy(), img->infoHeader_copy(), pixels); } std::shared_ptr invertColors(const std::shared_ptr& 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 std::make_shared(img->fileHeader_copy(), img->infoHeader_copy(), pixels); } std::shared_ptr upscale2x(const std::shared_ptr& img) { auto oldPixels = img->pixels(); const uint32_t newHeight = img->height() * 2; const uint32_t newWidth = img->width() * 2; std::shared_ptr newPixelArray = std::make_shared(newWidth, newHeight); for (int i = 0; i < newHeight; i += 2) { 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) { 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 std::make_shared(newPixelArray); } std::shared_ptr downscale2x(const std::shared_ptr& img) { auto oldPixels = img->pixels(); const uint32_t newHeight = img->height() / 2; const uint32_t newWidth = img->width() / 2; std::shared_ptr newPixelArray = std::make_shared(newWidth, newHeight); for (int i = 0; i < newHeight; ++i) { for (int j = 0; j < newWidth; ++j) { (*newPixelArray)(i, j) = (*oldPixels)(i * 2, j * 2); } } return std::make_shared(newPixelArray); } std::shared_ptr upscale1_5x(const std::shared_ptr& img) { auto oldPixels = img->pixels(); const uint32_t newHeight = img->height() * 3 / 2; const uint32_t newWidth = img->width() * 3 / 2; std::shared_ptr newPixelArray = std::make_shared(newWidth, newHeight); for (int i = 0; i < newHeight; ++i) { 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 std::make_shared(newPixelArray); } std::shared_ptr textImg(const std::u16string &str, Font *font, uint8_t scale, Pixel background_color, Pixel font_color) { auto strSize = str.size(); uint32_t glyphHeight = font->glyphHeight; uint32_t glyphWidth = font->glyphWidth; uint32_t imgWidth = strSize * glyphWidth * scale; uint32_t imgHeight = glyphHeight * scale; std::shared_ptr pixels = std::make_shared(imgWidth, imgHeight); for (int i = 0; i < strSize; ++i) { auto glyph = font->_glyphs[str[i]]; for (int j = 0; j < glyphHeight * scale; ++j) { for (int l = 0; l < glyphWidth * scale; ++l) { if (glyph->glyph[j / scale][l / scale]) (*pixels)(j, glyphWidth * scale * i + l) = font_color; else (*pixels)(j, glyphWidth * scale * i + l) = background_color; } } } return std::make_shared(pixels); } std::shared_ptr upscale1_5x_ver2(const std::shared_ptr& img) { auto oldPixels = img->pixels(); const uint32_t newHeight = img->height() * 3 / 2; const uint32_t newWidth = img->width() * 3 / 2; std::shared_ptr newPixelArray = std::make_shared(newWidth, newHeight); for (int i = 0; i < newHeight; ++i) { auto oldi = (int)std::round(i * 2 / 3.); if ((i + 1) % 3 == 0) { if (i == newHeight - 1) (*newPixelArray)(i, 0) = (*oldPixels)(oldi, 0) / 2; else { (*newPixelArray)(i, 0) = (*oldPixels)(oldi, 0) / 2 + (*oldPixels)(oldi + 1, 0) / 2; } continue; } for (int j = 0; j < newWidth; ++j) { auto oldj = (int)std::round(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 = 1; j < newWidth; ++j) { if (j == newWidth - 1) { (*newPixelArray)(i, j) = (*newPixelArray)(i, j - 1) / 2; continue; } auto f00 = (*newPixelArray)(i - 1, j - 1); auto f10 = (*newPixelArray)(i - 1, j + 1); auto f01 = (*newPixelArray)(i + 1, j - 1); auto f11 = (*newPixelArray)(i + 1, j + 1); (*newPixelArray)(i, j) = f10 / 4 + f00 / 4 + f01 / 4 + f11 / 4; } } return std::make_shared(newPixelArray); } std::shared_ptr upscale2x_ver2(const std::shared_ptr& img) { auto oldPixels = img->pixels(); const uint32_t newHeight = img->height() * 2; const uint32_t newWidth = img->width() * 2; std::shared_ptr newPixelArray = std::make_shared(newWidth, newHeight); for (int i = 0; i < newHeight; ++i) { if ((i + 1) % 2 == 0) { if (i == newHeight - 1) (*newPixelArray)(i, 0) = (*oldPixels)(i / 2, 0) / 2; else (*newPixelArray)(i, 0) = (*oldPixels)(i / 2, 0) / 2 + (*oldPixels)(i / 2 + 1, 0) / 2; continue; } 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) { if (i == newHeight - 1) for (int j = 1; j < newWidth; ++j) { (*newPixelArray)(i, j) = (*newPixelArray)(i - 1, j) / 2; } else for (int j = 1; j < newWidth; ++j) { if (j == newWidth - 1) { (*newPixelArray)(i, j) = (*newPixelArray)(i, j - 1) / 2; continue; } auto f00 = (*newPixelArray)(i - 1, j - 1); auto f10 = (*newPixelArray)(i - 1, j + 1); auto f01 = (*newPixelArray)(i + 1, j - 1); auto f11 = (*newPixelArray)(i + 1, j + 1); (*newPixelArray)(i, j) = f10 / 4 + f00 / 4 + f01 / 4 + f11 / 4; } } return std::make_shared(newPixelArray); }