diff --git a/bmpimage.cpp b/bmpimage.cpp index fe29d54..8156c9b 100644 --- a/bmpimage.cpp +++ b/bmpimage.cpp @@ -1,30 +1,35 @@ #include "bmpimage.h" -BMPImage::BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, Pixel **pixelArray) { +BMPImage::BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, PixelArray pixelArray) + : pixelArray(pixelArray) { this->fileHeader = fileHeader; this->infoHeader = infoHeader; - this->pixelArray = 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::write(const std::string &filename) { +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 - this->infoHeader.BitmapWidth % 4; - for (int i = 0; i < this->infoHeader.BitmapHeight; ++i) { - ofs.write((char *) pixelArray[i], byteByRow); + 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 } } } - Pixel **BMPImage::pixels(){ +const PixelArray BMPImage::pixels() { return this->pixelArray; } @@ -33,7 +38,7 @@ Pixel **BMPImage::pixels_copy() { 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]); + std::copy(this->pixelArray(i), this->pixelArray(i) + this->infoHeader.BitmapWidth, newPixelArray[i]); } return newPixelArray; } @@ -47,12 +52,45 @@ BITMAPINFOHEADER BMPImage::infoHeader_copy() { } BMPImage::~BMPImage() { - for (int i = 0; i < this->infoHeader.BitmapHeight; ++i) { - delete[] this->pixelArray[i]; - } - delete[] this->pixelArray; +// 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; @@ -86,8 +124,8 @@ BMPImage readBMPImage(const std::string &filename) { } 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) { + 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 @@ -95,3 +133,182 @@ BMPImage readBMPImage(const std::string &filename) { } 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; } diff --git a/bmpimage.h b/bmpimage.h index fa3bf76..543c3b7 100644 --- a/bmpimage.h +++ b/bmpimage.h @@ -8,21 +8,21 @@ const char PADDING_ZEROES[3] = {0, 0, 0}; #pragma pack(push, 1) struct BitmapFileHeader { - char signature[2] = {0, 0}; + char signature[2] = {'B', 'M'}; uint32_t fileSize = 0; uint16_t reserved1 = 0; uint16_t reserved2 = 0; - uint32_t imageDataOffset = 0; + uint32_t imageDataOffset = 54; }; #pragma pack(pop) #pragma pack(push, 1) struct BITMAPINFOHEADER { - uint32_t HeaderSize = 0; + uint32_t HeaderSize = 40; uint32_t BitmapWidth = 0; uint32_t BitmapHeight = 0; - uint16_t ColorPlanes = 0; - uint16_t BitsPerPixel = 0; + uint16_t ColorPlanes = 1; + uint16_t BitsPerPixel = 24; uint32_t CompressionMethod = 0; uint32_t ImageSize = 0; int32_t HorizontalPixelPerMetre = 0; @@ -34,18 +34,39 @@ struct BITMAPINFOHEADER { #pragma pack(push, 1) struct Pixel { - uint8_t r = 0; + uint8_t r = 255; uint8_t g = 0; - uint8_t b = 0; + uint8_t b = 255; }; #pragma pack(pop) +class PixelArray{ + Pixel **array; + uint32_t _width; + uint32_t _height; +public: + PixelArray(uint32_t width, uint32_t height); + PixelArray(const PixelArray &); + ~PixelArray(); + + [[nodiscard]] const uint32_t &width() const; + + [[nodiscard]] const uint32_t &height() const; + +// Pixel operator()(const uint32_t &, const uint32_t &); +// Pixel* operator()(const uint32_t &); + Pixel operator()(const uint32_t &, const uint32_t &); + Pixel* operator()(const uint32_t &); +}; + class BMPImage { BitmapFileHeader fileHeader; BITMAPINFOHEADER infoHeader; - Pixel **pixelArray; + PixelArray pixelArray; public: - BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, Pixel **pixelArray); + BMPImage(const BitmapFileHeader &fileHeader, const BITMAPINFOHEADER &infoHeader, PixelArray pixelArray); + + BMPImage(PixelArray pixelArray); ~BMPImage(); @@ -53,7 +74,7 @@ public: [[nodiscard]] const uint32_t &height() const; - [[nodiscard]] Pixel **pixels(); + [[nodiscard]] const PixelArray pixels(); [[nodiscard]] Pixel **pixels_copy(); @@ -61,7 +82,31 @@ public: [[nodiscard]] BITMAPINFOHEADER infoHeader_copy(); - void write(const std::string &); + void save(const std::string &); + + BMPImage *appendRight(const BMPImage &); }; BMPImage readBMPImage(const std::string &filename); + +Pixel operator/(const Pixel &, const uint8_t &); + +Pixel operator+(const Pixel &, const Pixel &); + +Pixel operator*(const Pixel &, const int &); + +Pixel operator-(const Pixel &, const uint8_t &); + +Pixel operator-(const uint8_t &, const Pixel &); + +BMPImage grayscale(BMPImage &); + +BMPImage invertColors(BMPImage &); + +BMPImage upscale2x(BMPImage &); + +BMPImage downscale2x(BMPImage &); + +BMPImage upscale1_5x(BMPImage &); + +uint8_t ui8_clamp(int value, uint8_t min = 0, uint8_t max = 255); \ No newline at end of file diff --git a/main.cpp b/main.cpp index 8444d6c..d215a77 100644 --- a/main.cpp +++ b/main.cpp @@ -3,33 +3,119 @@ const std::string FILENAME = "../elef.bmp"; const std::string FILENAME_OUT = "../elef_out.bmp"; +// +//void lab01() { +// auto og_image = readBMPImage("../elef.bmp"); +// auto pixels1 = og_image.pixels_copy(); +// auto pixels2 = og_image.pixels_copy(); +// for (int i = 0; i < og_image.height(); ++i) { +// for (int j = 0; j < og_image.width(); ++j) { +// pixels1[i][j] = pixels1[i][j] / 2; +// pixels2[i][j] = pixels2[i][j] * 2; +// } +// } +// auto darkenImg = BMPImage(pixels1, og_image.width(), og_image.height()); +// auto lighterImg = BMPImage(pixels2, og_image.width(), og_image.height()); +// og_image.appendRight(lighterImg)->appendRight(darkenImg)->appendRight(invertColors(og_image))->appendRight( +// grayscale(og_image))->save("../lab01/elef1.bmp"); +//} +// +//void lab02_01() { +// auto img = readBMPImage("../tea.bmp"); +// auto img_512 = downscale2x(img); +// auto img_256 = downscale2x(img_512); +// auto img_128 = downscale2x(img_256); +// img_512.save("../lab02/tea_512.bmp"); +// img_256.save("../lab02/tea_256.bmp"); +// img_128.save("../lab02/tea_128.bmp"); +// auto newImg = img.appendRight(img_512)->appendRight(img_256)->appendRight(img_128); +// newImg->save("../lab02/tea_downscale.bmp"); +// auto img_from_512 = upscale2x(img_512); +//// auto img_from_256 = upscale2x(upscale2x(img_256)); +//// auto img_from_128 = upscale2x(upscale2x(upscale2x(img_128))); +// auto tmp = upscale2x(img_256); +// auto img_from_256 = upscale2x(tmp); +// auto tmp2 = upscale2x(img_128); +// auto tmp3 = upscale2x(tmp2); +// auto img_from_128 = upscale2x(tmp3); +// img_from_512.save("../lab02/tea_from_512.bmp"); +// img_from_256.save("../lab02/tea_from_256.bmp"); +// img_from_128.save("../lab02/tea_from_128.bmp"); +// img.appendRight(img_from_512)->appendRight(img_from_256)->appendRight(img_from_128)->save("../lab02/tea_upscale.bmp"); +//// newImg2->save("../lab02/tea_upscale.bmp"); +//} +// +//void lab02_02() { +// auto img = readBMPImage("../tea.bmp"); +// auto gsImg = grayscale(img); +// gsImg.save("../lab02/tea_grayscale_256.bmp"); +// auto gsPixels = gsImg.pixels_copy(); +// auto width = gsImg.width(); +// auto height = gsImg.height(); +// for (int i = 0; i < height; ++i) { +// for (int j = 0; j < width; ++j) { +// gsPixels[i][j] = (gsPixels[i][j] / 2) * 2; +// } +// } +// auto gsImg128 = BMPImage(gsPixels, width, height); +// gsPixels = gsImg.pixels_copy(); +// for (int i = 0; i < height; ++i) { +// for (int j = 0; j < width; ++j) { +// gsPixels[i][j] = (gsPixels[i][j] / 4) * 4; +// } +// } +// auto gsImg64 = BMPImage(gsPixels, width, height); +// gsPixels = gsImg.pixels_copy(); +// for (int i = 0; i < height; ++i) { +// for (int j = 0; j < width; ++j) { +// gsPixels[i][j] = (gsPixels[i][j] / 8) * 8; +// } +// } +// auto gsImg32 = BMPImage(gsPixels, width, height); +// gsImg128.save("../lab02/tea_grayscale_128.bmp"); +// gsImg64.save("../lab02/tea_grayscale_64.bmp"); +// gsImg32.save("../lab02/tea_grayscale_32.bmp"); +// gsImg.appendRight(gsImg128)->appendRight(gsImg64)->appendRight(gsImg32)->save("../lab02/tea_grayscale.bmp"); +//} +// +//void lab02_03(){ +// auto img = readBMPImage("../tea.bmp"); +// auto img_1_5 = upscale1_5x(img); +// auto img_2 = upscale2x(img); +// auto img_3 = upscale2x(img_1_5); +// img_1_5.save("../lab02/lab02_03/tea_x1_5.bmp"); +// img_2.save("../lab02/lab02_03/tea_x2.bmp"); +// img_3.save("../lab02/lab02_03/tea_x3.bmp"); +//// img.appendRight(img_1_5)->appendRight(img_2)->appendRight(img_3)->save("../lab02/lab02_03/tea_upscale.bmp"); +// img.appendRight(img_1_5)->save("../lab02/lab02_03/tea_upscale.bmp"); +//} -void lab01() { - auto og_image = readBMPImage(FILENAME); - auto pixels = og_image.pixels_copy(); - for (int i = 0; i < og_image.height(); ++i) { - for (int j = 0; j < og_image.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}; - } - } - auto image1 = new BMPImage(og_image.fileHeader_copy(), og_image.infoHeader_copy(), pixels); - auto pixels2 = image1->pixels_copy(); - for (int i = 0; i < og_image.height(); ++i) { - for (int j = 0; j < og_image.width(); ++j) { - uint8_t gray = 0; - if (pixels2[i][j].r > 80) gray = 255; - pixels2[i][j] = {gray, gray, gray}; - } - } - auto image2 = new BMPImage(og_image.fileHeader_copy(), og_image.infoHeader_copy(), pixels2); - image1->write("../elef_gs.bmp"); - image2->write("../elef_bw.bmp"); - og_image.write("../elef_out.bmp"); -// image.write(FILENAME_OUT); +void test() { + Pixel **test; + test = new Pixel *[3]; + test[0] = new Pixel[3]; + test[1] = new Pixel[3]; + test[2] = new Pixel[3]; + test[0][0] = Pixel{0, 0, 0}; + test[0][1] = Pixel{127, 127, 127}; + test[0][2] = Pixel{255, 255, 255}; + test[1][0] = Pixel{127, 127, 127}; + test[1][1] = Pixel{63, 63, 63}; + test[1][2] = Pixel{127, 127, 127}; + test[2][0] = Pixel{255, 255, 255}; + test[2][1] = Pixel{127, 127, 127}; + test[2][2] = Pixel{0, 0, 0}; + auto test_img = BMPImage(test, 3, 3); + test_img.save("../test.bmp"); + auto test_scale = upscale2x(test_img); + test_scale.save("../test_scale.bmp"); } int main() { - lab01(); + //lab01(); + //lab02_01(); + //lab02_02(); +// lab02_03(); + test(); return 0; } diff --git a/tea.bmp b/tea.bmp new file mode 100644 index 0000000..01c78a5 Binary files /dev/null and b/tea.bmp differ