// It reads, modifies and writes 24 bit BMP files

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <cstdio>
#include <cmath>


typedef unsigned char tbyte;
typedef unsigned int t4bytes;


class Pixel24bit {
public:
  Pixel24bit(tbyte R, tbyte G, tbyte B) : R(R), G(G), B(B) { }
  tbyte R;
  tbyte G;
  tbyte B;
};

class RowPixel24bit {
public:
  RowPixel24bit() { }
  RowPixel24bit(t4bytes width, Pixel24bit pixel) : pixels(width, pixel) { }
  RowPixel24bit(std::vector<Pixel24bit> pixels) : pixels(pixels) { }
  std::vector<Pixel24bit> pixels;
};

class Image24bit {
public:
  Image24bit() : width(0), height(0) { }
  Image24bit(t4bytes width, t4bytes height, tbyte R = 255, tbyte G = 255, tbyte B = 255) : width(width), height(height) {
    rows = std::vector<RowPixel24bit>(height, RowPixel24bit(width, Pixel24bit(R, G, B)));
  }
  Image24bit(std::vector<RowPixel24bit> rows) : rows(rows) {
    height = rows.size();
    if (height > 0) {
      width = rows[0].pixels.size();
    } else {
      width = 0;
    }
  }
  bool writeToFile(const std::string & fileName);
  bool readFromFile(const std::string & fileName);
  bool isBmpFileFormatSupported(std::ifstream & fin, t4bytes & fileSize, t4bytes & ww, t4bytes & hh, t4bytes & restSize);
  void showPixelsInRectangle(t4bytes x1, t4bytes y1, t4bytes w, t4bytes h);
  std::vector<RowPixel24bit> rows;
  t4bytes width;
  t4bytes height;
};


int main(int argc, char** argv) {
  if (argc != 8) {
    std::fprintf(stderr, "Syntax: %s Width Height R G B outputFile1 outputFile2\n", argv[0]);
    return 1;
  }
  t4bytes width = std::stoull(argv[1]);
  t4bytes height = std::stoull(argv[2]);
  tbyte rr = static_cast<unsigned char>(std::stoi(argv[3]));
  tbyte gg = static_cast<unsigned char>(std::stoi(argv[4]));
  tbyte bb = static_cast<unsigned char>(std::stoi(argv[5]));
  std::string sfile1(argv[6]);
  std::string sfile2(argv[7]);

  Image24bit image1(width, height, rr, gg, bb);
  image1.writeToFile(sfile1);

  Image24bit image2;
  image2.readFromFile(sfile1);

  for (t4bytes yy = 0; yy < height; yy++) {
    for (t4bytes xx = 0; xx < width; xx++) {
      image2.rows[yy].pixels[xx].R = static_cast<tbyte>(150.0 + 50.0 * std::cos(2.0 * xx * yy));
      image2.rows[yy].pixels[xx].G = static_cast<tbyte>(50.0 + 50.0 * std::cos(3.0 * xx * yy));
      image2.rows[yy].pixels[xx].B = static_cast<tbyte>(150.0 + 50.0 * std::cos(2.0 * xx * yy));
    }
  }

//  image2.showPixelsInRectangle(0, 0, image2.width, image2.height);

  image2.writeToFile(sfile2);

  return 0;
}


bool Image24bit::writeToFile(const std::string & fileName) {
  t4bytes hh = rows.size();
  if (hh < 1) {
    return false;
  }
  t4bytes ww = rows[0].pixels.size();
  if (ww < 1) {
    return false;
  }

  int nBytesPaddingPerRow = (3 * ww % 4 == 0) ? 0 : (4 - 3 * ww % 4);
  t4bytes restSize = 3 * ww * hh + hh*nBytesPaddingPerRow;
  t4bytes fileSize = restSize + 54;

  unsigned char header[54] = {0};
  header[0] = 66;
  header[1] = 77;
  header[10] = 54;
  header[14] = 40;
  header[26] = 1;
  header[28] = 24;

  header[2] = *(reinterpret_cast<unsigned char *>(&fileSize) + 0); // File Size byte 0 (LSB)
  header[3] = *(reinterpret_cast<unsigned char *>(&fileSize) + 1); // File Size byte 1
  header[4] = *(reinterpret_cast<unsigned char *>(&fileSize) + 2); // File Size byte 2
  header[5] = *(reinterpret_cast<unsigned char *>(&fileSize) + 3); // File Size byte 3 (MSB)

  header[18] = *(reinterpret_cast<unsigned char *>(&ww) + 0); // Width byte 0 (LSB)
  header[19] = *(reinterpret_cast<unsigned char *>(&ww) + 1); // Width byte 1
  header[20] = *(reinterpret_cast<unsigned char *>(&ww) + 2); // Width byte 2
  header[21] = *(reinterpret_cast<unsigned char *>(&ww) + 3); // Width byte 3 (MSB)

  header[22] = *(reinterpret_cast<unsigned char *>(&hh) + 0); // Height byte 0 (LSB)
  header[23] = *(reinterpret_cast<unsigned char *>(&hh) + 1); // Height byte 1
  header[24] = *(reinterpret_cast<unsigned char *>(&hh) + 2); // Height byte 2
  header[25] = *(reinterpret_cast<unsigned char *>(&hh) + 3); // Height byte 3 (MSB)

  header[34] = *(reinterpret_cast<unsigned char *>(&restSize) + 0); // Rest Size byte 0 (LSB)
  header[35] = *(reinterpret_cast<unsigned char *>(&restSize) + 1); // Rest Size byte 1
  header[36] = *(reinterpret_cast<unsigned char *>(&restSize) + 2); // Rest Size byte 2
  header[37] = *(reinterpret_cast<unsigned char *>(&restSize) + 3); // Rest Size byte 3 (MSB)

  std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);

  fout.write(reinterpret_cast<char *>(&header[0]), 54);

  tbyte * rest = new tbyte[restSize];

  t4bytes yy;
  t4bytes xx;
  t4bytes pos = 0;
  for (yy = 0; yy < hh; yy++) {
    for (xx = 0; xx < ww; xx++) {
      rest[pos++] = rows[yy].pixels[xx].B;
      rest[pos++] = rows[yy].pixels[xx].G;
      rest[pos++] = rows[yy].pixels[xx].R;
    }
    for (xx = 0; xx < nBytesPaddingPerRow; xx++) {
      rest[pos++] = '\x0';
    }
  }

  fout.write(reinterpret_cast<char *>(rest), restSize);

  fout.close();

  delete[] rest;

  return true;
}


bool Image24bit::readFromFile(const std::string & fileName) {
  std::ifstream fin(fileName.c_str(), std::ios::in | std::ios::binary);

  t4bytes fileSize = 0;
  t4bytes ww = 0;
  t4bytes hh = 0;
  t4bytes restSize = 0;
  if (!isBmpFileFormatSupported(fin, fileSize, ww, hh, restSize)) {
    return false;
  }

  tbyte * rest = new tbyte[restSize];

  fin.read(reinterpret_cast<char *>(rest), restSize);

  fin.close();

  rows = std::vector<RowPixel24bit>(hh, RowPixel24bit(ww, Pixel24bit(255, 255, 255)));

  int nBytesPaddingPerRow = (3 * ww % 4 == 0) ? 0 : (4 - 3 * ww % 4);

  t4bytes yy;
  t4bytes xx;
  t4bytes pos = 0;
  tbyte padding;
  for (yy = 0; yy < hh; yy++) {
    for (xx = 0; xx < ww; xx++) {
      rows[yy].pixels[xx].B = rest[pos++];
      rows[yy].pixels[xx].G = rest[pos++];
      rows[yy].pixels[xx].R = rest[pos++];
    }
    for (xx = 0; xx < nBytesPaddingPerRow; xx++) {
      padding = rest[pos++];
      if (padding != '\x0') {
        std::cerr << "Error reading BMP file (padding not null)" << std::endl;
        return false;
      }
    }
  }

  delete[] rest;

  width = ww;
  height = hh;

  return true;
}


bool Image24bit::isBmpFileFormatSupported(std::ifstream & fin, t4bytes & fileSize, t4bytes & ww, t4bytes & hh, t4bytes & restSize) {
  unsigned char header[54] = {0};

  fin.seekg(0, std::ios_base::end);
  std::size_t fileSizeT = fin.tellg();
  fin.seekg(0, std::ios_base::beg);

  if (fileSizeT < 54) {
    std::cerr << "BMP file format not supported (fileSizeT < 54)" << std::endl;
    return false;
  }

  fin.read(reinterpret_cast<char *>(&header[0]), 54);

  if (header[10] != 54 || header[28] != 24) {
    std::cerr << "BMP file format not supported (header[10] != 54 || header[28] != 24)" << std::endl;
    return false;
  }

  *(reinterpret_cast<unsigned char *>(&fileSize) + 0) = header[2]; // File Size byte 0 (LSB)
  *(reinterpret_cast<unsigned char *>(&fileSize) + 1) = header[3]; // File Size byte 1
  *(reinterpret_cast<unsigned char *>(&fileSize) + 2) = header[4]; // File Size byte 2
  *(reinterpret_cast<unsigned char *>(&fileSize) + 3) = header[5]; // File Size byte 3 (MSB)

  if (fileSize != fileSizeT) {
    std::cerr << "BMP file format not supported (fileSize != fileSizeT)" << std::endl;
    return false;
  }

  *(reinterpret_cast<unsigned char *>(&ww) + 0) = header[18]; // Width byte 0 (LSB)
  *(reinterpret_cast<unsigned char *>(&ww) + 1) = header[19]; // Width byte 1
  *(reinterpret_cast<unsigned char *>(&ww) + 2) = header[20]; // Width byte 2
  *(reinterpret_cast<unsigned char *>(&ww) + 3) = header[21]; // Width byte 3 (MSB)

  *(reinterpret_cast<unsigned char *>(&hh) + 0) = header[22]; // Height byte 0 (LSB)
  *(reinterpret_cast<unsigned char *>(&hh) + 1) = header[23]; // Height byte 1
  *(reinterpret_cast<unsigned char *>(&hh) + 2) = header[24]; // Height byte 2
  *(reinterpret_cast<unsigned char *>(&hh) + 3) = header[25]; // Height byte 3 (MSB)

  *(reinterpret_cast<unsigned char *>(&restSize) + 0) = header[34]; // Rest Size byte 0 (LSB)
  *(reinterpret_cast<unsigned char *>(&restSize) + 1) = header[35]; // Rest Size byte 1
  *(reinterpret_cast<unsigned char *>(&restSize) + 2) = header[36]; // Rest Size byte 2
  *(reinterpret_cast<unsigned char *>(&restSize) + 3) = header[37]; // Rest Size byte 3 (MSB)

  if (fileSize != (restSize + 54)) {
    std::cerr << "BMP file format not supported (fileSize != (restSize + 54))" << std::endl;
    return false;
  }

  return true;
}


void Image24bit::showPixelsInRectangle(t4bytes x1, t4bytes y1, t4bytes w, t4bytes h) {
  std::cout << "(x, y) : (R, G, B)" << std::endl;
  for (t4bytes yy = y1; yy < (y1 + h); yy++) {
    for (t4bytes xx = x1; xx < (x1 + w); xx++) {
      std::cout << "(" << xx << ", " << yy << ") : (" << static_cast<int>(rows.at(yy).pixels.at(xx).R) << ", " << static_cast<int>(rows.at(yy).pixels.at(xx).G) << ", " << static_cast<int>(rows.at(yy).pixels.at(xx).B) << ")" << std::endl;
    }
  }
}
      

// Generated by xformulas.net on 20190911_184950