cpp-bmp-reader/image-lib/bmpimage.cpp

383 lines
16 KiB
C++

#include <cmath>
#include <memory>
#include <algorithm>
#include <utility>
#include "bmpimage.h"
BMPImage::BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader,
std::shared_ptr<PixelArray> 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<PixelArray> BMPImage::pixels() const {
return this->pixelArray;
}
std::shared_ptr<PixelArray> BMPImage::pixels_copy() {
// Pixel **newPixelArray;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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> 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> BMPImage::appendRight(const std::shared_ptr<const BMPImage>& img) {
uint32_t newHeight = std::max(this->infoHeader.BitmapHeight, img->height());
uint32_t newWidth = this->infoHeader.BitmapWidth + img->width();
// Pixel **newPixelArray;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(newWidth, newHeight);
// std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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<BMPImage>(newPixelArray);
}
std::shared_ptr<BMPImage> BMPImage::overlay(const std::shared_ptr<const BMPImage>& 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<BMPImage>(pixels);
}
std::shared_ptr<BMPImage> BMPImage::applyFilter(const std::function<uint8_t(std::array<int, 9> &)> &filter) {
auto origPixels = this->pixels_copy();
std::shared_ptr<PixelArray> pixels = std::make_shared<PixelArray>(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<int, 9> red_channel = {p1.r, p2.r, p3.r, p4.r, p5.r, p6.r, p7.r, p8.r, p9.r};
std::array<int, 9> green_channel = {p1.g, p2.g, p3.g, p4.g, p5.g, p6.g, p7.g, p8.g, p9.g};
std::array<int, 9> 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<BMPImage>(pixels);
}
std::shared_ptr<BMPImage> 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> pixelArray = std::make_shared<PixelArray>(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<BMPImage>(bitmapFileHeader, bitmapInfoHeader, pixelArray);
}
std::shared_ptr<BMPImage> grayscale(const std::shared_ptr<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 std::make_shared<BMPImage>(img->fileHeader_copy(), img->infoHeader_copy(), pixels);
}
std::shared_ptr<BMPImage> invertColors(const std::shared_ptr<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 std::make_shared<BMPImage>(img->fileHeader_copy(), img->infoHeader_copy(), pixels);
}
std::shared_ptr<BMPImage> upscale2x(const std::shared_ptr<const BMPImage>& img) {
auto oldPixels = img->pixels();
const uint32_t newHeight = img->height() * 2;
const uint32_t newWidth = img->width() * 2;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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<BMPImage>(newPixelArray);
}
std::shared_ptr<BMPImage> downscale2x(const std::shared_ptr<const BMPImage>& img) {
auto oldPixels = img->pixels();
const uint32_t newHeight = img->height() / 2;
const uint32_t newWidth = img->width() / 2;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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<BMPImage>(newPixelArray);
}
std::shared_ptr<BMPImage> upscale1_5x(const std::shared_ptr<const BMPImage>& img) {
auto oldPixels = img->pixels();
const uint32_t newHeight = img->height() * 3 / 2;
const uint32_t newWidth = img->width() * 3 / 2;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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<BMPImage>(newPixelArray);
}
std::shared_ptr<BMPImage>
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<PixelArray> pixels = std::make_shared<PixelArray>(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<BMPImage>(pixels);
}
std::shared_ptr<BMPImage> upscale1_5x_ver2(const std::shared_ptr<const BMPImage>& img) {
auto oldPixels = img->pixels();
const uint32_t newHeight = img->height() * 3 / 2;
const uint32_t newWidth = img->width() * 3 / 2;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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<BMPImage>(newPixelArray);
}
std::shared_ptr<BMPImage> upscale2x_ver2(const std::shared_ptr<const BMPImage>& img) {
auto oldPixels = img->pixels();
const uint32_t newHeight = img->height() * 2;
const uint32_t newWidth = img->width() * 2;
std::shared_ptr<PixelArray> newPixelArray = std::make_shared<PixelArray>(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<BMPImage>(newPixelArray);
}