#include "imageworker.h"
#include <QTime>
#include <QtDebug>

ImageWorker::ImageWorker() {
  this->preview = false;
  this->doInterpolation = false;
  this->interpolationDone = false;
  this->linearModeApp = true;
  this->line = 0;
  this->pendingLine = 0;
  this->x1 = 0;
  this->x2 = 0;
  this->c1enabled = false;
  this->c2enabled = false;
  this->online = false;
}

void ImageWorker::catchImg(Image *img) {
  qDebug() << "Image catched: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();
  // catch image
  this->currentImg.setParams(img->getWidth(), img->getHeight(),
                             img->getColors());
  this->currentImg.setData(*(img->getData()));
  this->interpolationDone = false;
  if (this->currentImg.getColors() && this->doInterpolation) {
    this->interpolate(true, true);
    this->interpolationDone = true;
  }
  // create QImage to show
  this->qimg = QImage(this->doInterpolation
                          ? this->currentImgInterpolated.getData()->data()
                          : this->currentImg.getData()->data(),
                      static_cast<int>(this->currentImg.getWidth()),
                      static_cast<int>(this->currentImg.getHeight()),
                      this->currentImg.getColors()
                          ? 3 * static_cast<int>(this->currentImg.getWidth())
                          : static_cast<int>(this->currentImg.getWidth()),
                      this->currentImg.getColors() ? QImage::Format_RGB888
                                                   : QImage::Format_Grayscale8);
  this->reDrawAll();
}

void ImageWorker::reDrawAll() {
  qDebug() << "Redraw request: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();
  this->reDrawAreaMode();
  if (this->linearModeApp) {
    this->reDrawLineArea();
  }
  this->reDrawLine();
  qDebug() << "Redraw done: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();
}

void ImageWorker::reDrawAreaMode() {
  if (!this->qimg.isNull()) {
    // scale
    int h = static_cast<int>(this->viewScale * this->currentImg.getHeight());
    int w = static_cast<int>(this->viewScale * this->currentImg.getWidth());
    QImage toShow = this->qimg.convertToFormat(QImage::Format_RGB888)
                        .scaled(w, h, Qt::KeepAspectRatio);
    if (this->preview) {
      this->drawRectangle(
          &toShow,
          static_cast<int>(this->viewScale *
                           static_cast<double>(this->ROIPreview.x())),
          static_cast<int>(this->viewScale *
                           static_cast<double>(this->ROIPreview.y())),
          static_cast<int>(this->viewScale *
                           static_cast<double>(this->ROIPreview.width())) > 0
              ? static_cast<int>(this->viewScale *
                                 static_cast<double>(this->ROIPreview.width()))
              : 1,
          static_cast<int>(this->viewScale *
                           static_cast<double>(this->ROIPreview.height())) > 0
              ? static_cast<int>(this->viewScale *
                                 static_cast<double>(this->ROIPreview.height()))
              : 1,
          QColor(255, 0, 0));
    }
    emit showImg(QPixmap::fromImage(toShow));
  }
}
void ImageWorker::reDrawLine() {
  if (!this->qimg.isNull()) {
    // scale
    int scale = 8;
    if (this->qimg.width() < 500) {
      scale = 40;
    } else if (this->qimg.width() < 1000) {
      scale = 20;
    } else if (this->qimg.width() < 1500) {
      scale = 16;
    } else if (this->qimg.width() < 2000) {
      scale = 14;
    } else if (this->qimg.width() < 2500) {
      scale = 10;
    }
    QImage toShow =
        this->qimg
            .copy(0, static_cast<int>(this->linearModeApp ? this->line : 0),
                  static_cast<int>(this->currentImg.getWidth()), 1)
            .scaled(scale * static_cast<int>(this->currentImg.getWidth()),
                    scale, Qt::KeepAspectRatio)
            .convertToFormat(QImage::Format_RGB888);
    if (this->qimg.width() > this->x1 && this->c1enabled) {
      for (int i = 0; i < scale; i++) {
        toShow.setPixelColor((scale * this->x1) + scale / 2, i, Qt::red);
      }
    }
    if (this->qimg.width() > this->x2 && this->c2enabled) {
      for (int i = 0; i < scale; i++) {
        toShow.setPixelColor((scale * this->x2) + scale / 2, i,
                             QColor(200, 55, 55));
      }
    }
    emit showImgLine(QPixmap::fromImage(toShow));

    if (this->doInterpolation) {
      emit showGraph(
          this->currentImgInterpolated,
          static_cast<uint32_t>(this->linearModeApp ? this->line : 0));
    } else {
      emit showGraph(
          this->currentImg,
          static_cast<uint32_t>(this->linearModeApp ? this->line : 0));
    }
    this->emitCursors(true, true);
  }
}
void ImageWorker::reDrawLineArea() {
  if (!this->qimg.isNull()) {
    int h =
        static_cast<int>(this->viewScaleLine * this->currentImg.getHeight());
    int w = static_cast<int>(this->viewScaleLine * this->currentImg.getWidth());
    QImage toShow = this->qimg.convertToFormat(QImage::Format_RGB888)
                        .scaled(w, h, Qt::KeepAspectRatio);

    this->drawRectangle(
        &toShow, 0, static_cast<int>(this->viewScaleLine * this->pendingLine),
        w, 1, QColor(255, 150, 0));

    this->drawRectangle(&toShow, 0,
                        static_cast<int>(this->viewScaleLine * this->line), w,
                        1, QColor(255, 0, 0));

    emit showImgL(QPixmap::fromImage(toShow));
  }
}

void ImageWorker::cursor1Enabled(int state) {
  if (state > 1) {
    this->c1enabled = true;
    this->emitCursors(true, false);
  } else {
    this->c1enabled = false;
    emit cursor1y("");
  }
  reDrawLine();
}
void ImageWorker::cursor2Enabled(int state) {
  if (state > 1) {
    this->c2enabled = true;
    this->emitCursors(false, true);
  } else {
    this->c2enabled = false;
    emit cursor2y("");
  }
  reDrawLine();
}
void ImageWorker::cursor1x(int val) {
  this->x1 = val;
  if (this->c1enabled) {
    reDrawLine();
    this->emitCursors(true, false);
  }
  emit x1mx2(QString::number(x1 - x2));
}
void ImageWorker::cursor2x(int val) {
  this->x2 = val;
  if (this->c2enabled) {
    reDrawLine();
    this->emitCursors(false, true);
  }
  emit x1mx2(QString::number(x1 - x2));
}

void ImageWorker::emitCursors(bool c1, bool c2) {
  int y1 = -1;
  int y2 = -1;
  int r1 = -1;
  int g1 = -1;
  int b1 = -1;
  int r2 = -1;
  int g2 = -1;
  int b2 = -1;
  if (c1) {
    if (!this->qimg.isNull() && this->qimg.width() > this->x1 &&
        this->c1enabled) {
      if (this->currentImg.getColors()) {
        QString toEmit = "R:";
        r1 = this->doInterpolation
                 ? this->currentImgInterpolated(
                       r, static_cast<uint32_t>(x1),
                       static_cast<uint32_t>(this->line))
                 : this->currentImg(r, static_cast<uint32_t>(x1),
                                    static_cast<uint32_t>(this->line));
        toEmit.append(QString::number(r1));
        toEmit.append(" G:");
        g1 = this->doInterpolation
                 ? this->currentImgInterpolated(
                       g, static_cast<uint32_t>(x1),
                       static_cast<uint32_t>(this->line))
                 : this->currentImg(g, static_cast<uint32_t>(x1),
                                    static_cast<uint32_t>(this->line));
        toEmit.append(QString::number(g1));
        toEmit.append(" B:");
        b1 = this->doInterpolation
                 ? this->currentImgInterpolated(
                       b, static_cast<uint32_t>(x1),
                       static_cast<uint32_t>(this->line))
                 : this->currentImg(b, static_cast<uint32_t>(x1),
                                    static_cast<uint32_t>(this->line));
        toEmit.append(QString::number(b1));
        emit cursor1y(toEmit);
      } else {
        y1 = this->currentImg(static_cast<uint32_t>(x1),
                              static_cast<uint32_t>(this->line));
        emit cursor1y(QString::number(y1));
      }
    }
  }
  if (c2) {
    if (!this->qimg.isNull() && this->qimg.width() > this->x2 &&
        this->c2enabled) {
      if (this->currentImg.getColors()) {
        QString toEmit = "R:";
        r2 = this->doInterpolation
                 ? this->currentImgInterpolated(
                       r, static_cast<uint32_t>(x2),
                       static_cast<uint32_t>(this->line))
                 : this->currentImg(r, static_cast<uint32_t>(x2),
                                    static_cast<uint32_t>(this->line));
        toEmit.append(QString::number(r2));
        toEmit.append(" G:");
        g2 = this->doInterpolation
                 ? this->currentImgInterpolated(
                       g, static_cast<uint32_t>(x2),
                       static_cast<uint32_t>(this->line))
                 : this->currentImg(g, static_cast<uint32_t>(x2),
                                    static_cast<uint32_t>(this->line));
        toEmit.append(QString::number(g2));
        toEmit.append(" B:");
        b2 = this->doInterpolation
                 ? this->currentImgInterpolated(
                       b, static_cast<uint32_t>(x2),
                       static_cast<uint32_t>(this->line))
                 : this->currentImg(b, static_cast<uint32_t>(x2),
                                    static_cast<uint32_t>(this->line));
        toEmit.append(QString::number(b2));
        emit cursor2y(toEmit);
      } else {
        y2 = this->currentImg(static_cast<uint32_t>(x2),
                              static_cast<uint32_t>(this->line));
        emit cursor2y(QString::number(y2));
      }
    }
  }
  if (y1 >= 0 && y2 >= 0) {
    emit y1my2(QString::number(y1 - y2));
  }
  if (r1 >= 0 && r2 >= 0 && g1 >= 0 && g2 >= 0 && b1 >= 0 && b2 >= 0) {
    QString toEmit = "R:";
    toEmit.append(QString::number(r1 - r2));
    toEmit.append(" G:");
    toEmit.append(QString::number(g1 - g2));
    toEmit.append(" B:");
    toEmit.append(QString::number(b1 - b2));
    emit y1my2(toEmit);
  }
}

bool ImageWorker::save(QString name) {
  if (!this->qimg.isNull()) {
    return this->qimg.save(name);
  } else {
    return false;
  }
}

bool ImageWorker::saveLine(QString name) {
  if (!this->qimg.isNull()) {
    return this->qimg
        .copy(0, static_cast<int>(this->line),
              static_cast<int>(this->currentImg.getWidth()), 1)
        .save(name);
  } else {
    return false;
  }
}

bool ImageWorker::saveLineData(QString name) {
  if (!this->qimg.isNull()) {
    QFile file(name);
    if (!file.open(QIODevice::WriteOnly)) {
      return false;
    }
    QTextStream out(&file);
    if (this->currentImg.getColors()) {
      out << "R, G, B\n";
      for (uint32_t i = 0; i < this->currentImg.getWidth(); i++) {
        if (this->doInterpolation) {
          out << this->currentImgInterpolated(r, i, static_cast<uint32_t>(line))
              << ", ";
          out << this->currentImgInterpolated(g, i, static_cast<uint32_t>(line))
              << ", ";
          out << this->currentImgInterpolated(b, i, static_cast<uint32_t>(line))
              << "\n";
        } else {
          out << this->currentImg(r, i, static_cast<uint32_t>(line)) << ", ";
          out << this->currentImg(g, i, static_cast<uint32_t>(line)) << ", ";
          out << this->currentImg(b, i, static_cast<uint32_t>(line)) << "\n";
        }
      }
    } else {
      out << "GRAY\n";
      for (uint32_t i = 0; i < this->currentImg.getWidth(); i++) {
        out << this->currentImg(i, static_cast<uint32_t>(line)) << "\n";
      }
    }
    file.close();
    return true;
  } else {
    return false;
  }
}

void ImageWorker::setInterpolateState(int state) {
  if (state == 0) {
    this->doInterpolation = false;
  } else {
    this->doInterpolation = true;
    if (!this->interpolationDone && this->currentImg.getData()->size() != 0) {
      this->interpolate(true, true);
      this->interpolationDone = true;
    }
  }
  this->qimg = QImage(this->doInterpolation
                          ? this->currentImgInterpolated.getData()->data()
                          : this->currentImg.getData()->data(),
                      static_cast<int>(this->currentImg.getWidth()),
                      static_cast<int>(this->currentImg.getHeight()),
                      this->currentImg.getColors()
                          ? 3 * static_cast<int>(this->currentImg.getWidth())
                          : static_cast<int>(this->currentImg.getWidth()),
                      this->currentImg.getColors() ? QImage::Format_RGB888
                                                   : QImage::Format_Grayscale8);
  this->reDrawAll();
}

void ImageWorker::drawRectangle(QImage *image, int beginX, int beginY,
                                int widhtX, int heightY, QColor color) {
  // qDebug() << "Draw rectangle begin: " << QTime::currentTime().second() <<
  // "."
  //          << QTime::currentTime().msec();
  beginX = beginX < image->width() ? beginX : image->width() - 1;
  beginY = beginY < image->height() ? beginY : image->height() - 1;
  beginX = beginX >= 0 ? beginX : 0;
  beginY = beginY >= 0 ? beginY : 0;
  int endX =
      beginX + widhtX < image->width() ? beginX + widhtX : image->width() - 1;
  int endY = beginY + heightY < image->height() ? beginY + heightY
                                                : image->height() - 1;
  endX = endX >= 0 ? endX : 0;
  endY = endY >= 0 ? endY : 0;
  for (int i = beginX; i < endX; i++) {
    image->setPixelColor(i, beginY, color);
  }
  if (endY == beginY + heightY) {
    for (int i = beginX; i < endX; i++) {
      image->setPixelColor(i, endY - 1, color);
    }
  }
  for (int i = beginY; i < endY; i++) {
    image->setPixelColor(beginX, i, color);
  }
  if (endX == beginX + widhtX) {
    for (int i = beginY; i < endY; i++) {
      image->setPixelColor(endX - 1, i, color);
    }
  }
  // qDebug() << "Draw rectangle end: " << QTime::currentTime().second() << "."
  //        << QTime::currentTime().msec();
}

void ImageWorker::interpolate(bool beginWithRG, bool beginWithG) {
  if (this->online) {
    this->interpolateNN();
  } else {
    this->interpolate4Q(beginWithRG, beginWithG);
  }
}

void ImageWorker::interpolateNN() {
  qDebug() << "Interpolate NN begin: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();

  this->currentImgInterpolated = this->currentImg;
  // uint32_t pos;
  uint32_t height = this->currentImg.getHeight();
  uint32_t width = this->currentImg.getWidth();

  uint32_t size = 3 * height * width;

  uint32_t count = 0;
  // R
  for (uint32_t i = 3; i < size - 3 * width; i += 6) {
    this->currentImgInterpolated.setDataAt(i - 3, this->currentImg(i));
    this->currentImgInterpolated.setDataAt(i + 3 * width, this->currentImg(i));
    this->currentImgInterpolated.setDataAt(i - 3 + 3 * width,
                                           this->currentImg(i));
    count++;
    if (count * 2 == width) {
      count = 0;
      i += 3 * width;
    }
  }

  // G
  count = 0;
  for (uint32_t i = 1; i < size - 3 * width; i += 6) {
    // RG
    this->currentImgInterpolated.setDataAt(i + 3, this->currentImg(i));
    // BG
    this->currentImgInterpolated.setDataAt(i + 3 * width,
                                           this->currentImg(i + 3 + 3 * width));
    count++;
    if (count * 2 == width) {
      count = 0;
      i += 3 * width;
    }
  }

  // B
  count = 0;
  for (uint32_t i = 2 + 3 * width; i < size; i += 6) {
    this->currentImgInterpolated.setDataAt(i + 3, this->currentImg(i));
    this->currentImgInterpolated.setDataAt(i - 3 * width, this->currentImg(i));
    this->currentImgInterpolated.setDataAt(i + 3 - 3 * width,
                                           this->currentImg(i));
    count++;
    if (count * 2 == width) {
      count = 0;
      i += 3 * width;
    }
  }

  qDebug() << "Interpolate NN end: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();
}

void ImageWorker::interpolate4Q(bool beginWithRG, bool beginWithG) {
  qDebug() << "Interpolate 4Q begin: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();

  this->currentImgInterpolated = this->currentImg;
  bool rg = !beginWithRG;
  bool green = beginWithG;
  int tmp;
  // uint32_t pos;
  uint32_t height = this->currentImg.getHeight() - 1;
  uint32_t width = this->currentImg.getWidth() - 1;

  //--------------------- inside -------------------------------//
  for (uint32_t j = 1; j < height; j++) {
    for (uint32_t i = 1; i < width; i++) {
      // pos = 3 * (i + j * width);
      if (rg) {
        if (green) {
          // G1
          // red
          tmp = this->currentImg(r, i - 1, j);
          tmp += this->currentImg(r, i + 1, j);
          this->currentImgInterpolated.setDataAt(r, i, j,
                                                 static_cast<uint8_t>(tmp / 2));
          // blue
          tmp = this->currentImg(b, i, j - 1);
          tmp += this->currentImg(b, i, j + 1);
          this->currentImgInterpolated.setDataAt(b, i, j,
                                                 static_cast<uint8_t>(tmp / 2));
        } else {
          // RED
          // green
          tmp = this->currentImg(g, i - 1, j);
          tmp += this->currentImg(g, i + 1, j);
          tmp += this->currentImg(g, i, j - 1);
          tmp += this->currentImg(g, i, j + 1);
          this->currentImgInterpolated.setDataAt(g, i, j,
                                                 static_cast<uint8_t>(tmp / 4));
          // blue
          tmp = this->currentImg(b, i - 1, j - 1);
          tmp += this->currentImg(b, i - 1, j + 1);
          tmp += this->currentImg(b, i + 1, j - 1);
          tmp += this->currentImg(b, i + 1, j + 1);
          this->currentImgInterpolated.setDataAt(b, i, j,
                                                 static_cast<uint8_t>(tmp / 4));
        }
      } else {
        if (green) {
          // G2
          // red
          tmp = this->currentImg(r, i, j - 1);
          tmp += this->currentImg(r, i, j + 1);
          this->currentImgInterpolated.setDataAt(r, i, j,
                                                 static_cast<uint8_t>(tmp / 2));
          // blue
          tmp = this->currentImg(b, i - 1, j);
          tmp += this->currentImg(b, i + 1, j);
          this->currentImgInterpolated.setDataAt(b, i, j,
                                                 static_cast<uint8_t>(tmp / 2));
        } else {
          // BLUE
          // green
          tmp = this->currentImg(g, i - 1, j);
          tmp += this->currentImg(g, i + 1, j);
          tmp += this->currentImg(g, i, j - 1);
          tmp += this->currentImg(g, i, j + 1);
          this->currentImgInterpolated.setDataAt(g, i, j,
                                                 static_cast<uint8_t>(tmp / 4));
          // red
          tmp = this->currentImg(r, i - 1, j - 1);
          tmp += this->currentImg(r, i - 1, j + 1);
          tmp += this->currentImg(r, i + 1, j - 1);
          tmp += this->currentImg(r, i + 1, j + 1);
          this->currentImgInterpolated.setDataAt(r, i, j,
                                                 static_cast<uint8_t>(tmp / 4));
        }
      }
      green = !green;
    }
    rg = !rg;
    /* XNOR
       bgRG | bgG |   | rg | green |
         1  |  1  | 1 | 1  |  1    |
         1  |  0  | 0 | 1  |  0    |
         0  |  1  | 0 | 0  |  1    |
         0  |  0  | 1 | 0  |  0    |
  */
    if (rg == ((!beginWithRG) == beginWithG)) {
      green = true;
    } else {
      green = false;
    }
  }
  //------------------------------------------------------------//

  //--------------------- last line ----------------------------//
  green = !green;
  // first pixel
  if (rg) {
    if (green) {
      // G1
      // red
      tmp = this->currentImg(r, 1, height);
      this->currentImgInterpolated.setDataAt(r, 0, height,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, 0, height - 1);
      this->currentImgInterpolated.setDataAt(b, 0, height,
                                             static_cast<uint8_t>(tmp));
    } else {
      // RED
      // green
      tmp = this->currentImg(g, 1, height);
      tmp += this->currentImg(g, 0, height - 1);
      this->currentImgInterpolated.setDataAt(g, 0, height,
                                             static_cast<uint8_t>(tmp / 2));
      // blue
      tmp = this->currentImg(b, 1, height - 1);
      this->currentImgInterpolated.setDataAt(b, 0, height,
                                             static_cast<uint8_t>(tmp));
    }
  } else {
    if (green) {
      // G2
      // red
      tmp = this->currentImg(r, 0, height - 1);
      this->currentImgInterpolated.setDataAt(r, 0, height,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, 1, height);
      this->currentImgInterpolated.setDataAt(b, 0, height,
                                             static_cast<uint8_t>(tmp));
    } else {
      // BLUE
      // green
      tmp = this->currentImg(g, 1, height);
      tmp += this->currentImg(g, 0, height - 1);
      this->currentImgInterpolated.setDataAt(g, 0, height,
                                             static_cast<uint8_t>(tmp / 2));
      // red
      tmp = this->currentImg(r, 1, height - 1);
      this->currentImgInterpolated.setDataAt(r, 0, height,
                                             static_cast<uint8_t>(tmp));
    }
  }
  green = !green;

  // line
  for (uint32_t i = 1; i < this->currentImg.getWidth() - 1; i++) {
    if (rg) {
      if (green) {
        // G1
        // red
        tmp = this->currentImg(r, i - 1, height);
        tmp += this->currentImg(r, i + 1, height);
        this->currentImgInterpolated.setDataAt(r, i, height,
                                               static_cast<uint8_t>(tmp / 2));
        // blue
        tmp = this->currentImg(b, i, height - 1);
        this->currentImgInterpolated.setDataAt(b, i, height,
                                               static_cast<uint8_t>(tmp));
      } else {
        // RED
        // green
        tmp = this->currentImg(g, i - 1, height);
        tmp += this->currentImg(g, i + 1, height);
        tmp += this->currentImg(g, i, height - 1);
        this->currentImgInterpolated.setDataAt(g, i, height,
                                               static_cast<uint8_t>(tmp / 3));
        // blue
        tmp = this->currentImg(b, i - 1, height - 1);
        tmp += this->currentImg(b, i + 1, height - 1);
        this->currentImgInterpolated.setDataAt(b, i, height,
                                               static_cast<uint8_t>(tmp / 2));
      }
    } else {
      if (green) {
        // G2
        // red
        tmp = this->currentImg(r, i, height - 1);
        this->currentImgInterpolated.setDataAt(r, i, height,
                                               static_cast<uint8_t>(tmp));
        // blue
        tmp = this->currentImg(b, i - 1, height);
        tmp += this->currentImg(b, i + 1, height);
        this->currentImgInterpolated.setDataAt(b, i, height,
                                               static_cast<uint8_t>(tmp / 2));
      } else {
        // BLUE
        // green
        tmp = this->currentImg(g, i - 1, height);
        tmp += this->currentImg(g, i + 1, height);
        tmp += this->currentImg(g, i, height - 1);
        this->currentImgInterpolated.setDataAt(g, i, height,
                                               static_cast<uint8_t>(tmp / 3));
        // red
        tmp = this->currentImg(r, i - 1, height - 1);
        tmp += this->currentImg(r, i + 1, height - 1);
        this->currentImgInterpolated.setDataAt(r, i, height,
                                               static_cast<uint8_t>(tmp / 2));
      }
    }
    green = !green;
  }

  // last pixel
  if (rg) {
    if (green) {
      // G1
      // red
      tmp = this->currentImg(r, width - 1, height);
      this->currentImgInterpolated.setDataAt(r, width, height,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, width, height - 1);
      this->currentImgInterpolated.setDataAt(b, width, height,
                                             static_cast<uint8_t>(tmp));
    } else {
      // RED
      // green
      tmp = this->currentImg(g, width - 1, height);
      tmp += this->currentImg(g, width, height - 1);
      this->currentImgInterpolated.setDataAt(g, width, height,
                                             static_cast<uint8_t>(tmp / 2));
      // blue
      tmp = this->currentImg(b, width - 1, height - 1);
      this->currentImgInterpolated.setDataAt(b, width, height,
                                             static_cast<uint8_t>(tmp));
    }
  } else {
    if (green) {
      // G2
      // red
      tmp = this->currentImg(r, width, height - 1);
      this->currentImgInterpolated.setDataAt(r, width, height,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, width - 1, height);
      this->currentImgInterpolated.setDataAt(b, width, height,
                                             static_cast<uint8_t>(tmp));
    } else {
      // BLUE
      // green
      tmp = this->currentImg(g, width - 1, height);
      tmp += this->currentImg(g, width, height - 1);
      this->currentImgInterpolated.setDataAt(g, width, height,
                                             static_cast<uint8_t>(tmp / 2));
      // red
      tmp = this->currentImg(r, width - 1, height - 1);
      this->currentImgInterpolated.setDataAt(r, width, height,
                                             static_cast<uint8_t>(tmp));
    }
  }

  //------------------------------------------------------------//

  //-------------------- first line ----------------------------//
  rg = beginWithRG;
  green = beginWithG;
  // first pixel
  if (rg) {
    if (green) {
      // G1
      // red
      tmp = this->currentImg(r, 1, 0);
      this->currentImgInterpolated.setDataAt(r, 0, 0,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, 0, 1);
      this->currentImgInterpolated.setDataAt(b, 0, 0,
                                             static_cast<uint8_t>(tmp));
    } else {
      // RED
      // green
      tmp = this->currentImg(g, 1, 0);
      tmp += this->currentImg(g, 0, 1);
      this->currentImgInterpolated.setDataAt(g, 0, 0,
                                             static_cast<uint8_t>(tmp / 2));
      // blue
      tmp = this->currentImg(b, 1, 1);
      this->currentImgInterpolated.setDataAt(b, 0, 0,
                                             static_cast<uint8_t>(tmp));
    }
  } else {
    if (green) {
      // G2
      // red
      tmp = this->currentImg(r, 0, 1);
      this->currentImgInterpolated.setDataAt(r, 0, 0,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, 1, 0);
      this->currentImgInterpolated.setDataAt(b, 0, 0,
                                             static_cast<uint8_t>(tmp));
    } else {
      // BLUE
      // green
      tmp = this->currentImg(g, 1, 0);
      tmp += this->currentImg(g, 0, 1);
      this->currentImgInterpolated.setDataAt(g, 0, 0,
                                             static_cast<uint8_t>(tmp / 2));
      // red
      tmp = this->currentImg(r, 1, 1);
      this->currentImgInterpolated.setDataAt(r, 0, 0,
                                             static_cast<uint8_t>(tmp));
    }
  }
  green = !green;

  // line
  for (uint32_t i = 1; i < this->currentImg.getWidth() - 1; i++) {
    if (rg) {
      if (green) {
        // G1
        // red
        tmp = this->currentImg(r, i - 1, 0);
        tmp += this->currentImg(r, i + 1, 0);
        this->currentImgInterpolated.setDataAt(r, i, 0,
                                               static_cast<uint8_t>(tmp / 2));
        // blue
        tmp = this->currentImg(b, i, 1);
        this->currentImgInterpolated.setDataAt(b, i, 0,
                                               static_cast<uint8_t>(tmp));
      } else {
        // RED
        // green
        tmp = this->currentImg(g, i - 1, 0);
        tmp += this->currentImg(g, i + 1, 0);
        tmp += this->currentImg(g, i, 1);
        this->currentImgInterpolated.setDataAt(g, i, 0,
                                               static_cast<uint8_t>(tmp / 3));
        // blue
        tmp = this->currentImg(b, i - 1, 1);
        tmp += this->currentImg(b, i + 1, 1);
        this->currentImgInterpolated.setDataAt(b, i, 0,
                                               static_cast<uint8_t>(tmp / 2));
      }
    } else {
      if (green) {
        // G2
        // red
        tmp = this->currentImg(r, i, 1);
        this->currentImgInterpolated.setDataAt(r, i, 0,
                                               static_cast<uint8_t>(tmp));
        // blue
        tmp = this->currentImg(b, i - 1, 0);
        tmp += this->currentImg(b, i + 1, 0);
        this->currentImgInterpolated.setDataAt(b, i, 0,
                                               static_cast<uint8_t>(tmp / 2));
      } else {
        // BLUE
        // green
        tmp = this->currentImg(g, i - 1, 0);
        tmp += this->currentImg(g, i + 1, 0);
        tmp += this->currentImg(g, i, 1);
        this->currentImgInterpolated.setDataAt(g, i, 0,
                                               static_cast<uint8_t>(tmp / 3));
        // red
        tmp = this->currentImg(r, i - 1, 1);
        tmp += this->currentImg(r, i + 1, 1);
        this->currentImgInterpolated.setDataAt(r, i, 0,
                                               static_cast<uint8_t>(tmp / 2));
      }
    }
    green = !green;
  }

  // last pixel
  if (rg) {
    if (green) {
      // G1
      // red
      tmp = this->currentImg(r, width - 1, 0);
      this->currentImgInterpolated.setDataAt(r, width, 0,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, width, 1);
      this->currentImgInterpolated.setDataAt(b, width, 0,
                                             static_cast<uint8_t>(tmp));
    } else {
      // RED
      // green
      tmp = this->currentImg(g, width - 1, 0);
      tmp += this->currentImg(g, width, 1);
      this->currentImgInterpolated.setDataAt(g, width, 0,
                                             static_cast<uint8_t>(tmp / 2));
      // blue
      tmp = this->currentImg(b, width - 1, 1);
      this->currentImgInterpolated.setDataAt(b, width, 0,
                                             static_cast<uint8_t>(tmp));
    }
  } else {
    if (green) {
      // G2
      // red
      tmp = this->currentImg(r, width, 1);
      this->currentImgInterpolated.setDataAt(r, width, 0,
                                             static_cast<uint8_t>(tmp));
      // blue
      tmp = this->currentImg(b, width - 1, 0);
      this->currentImgInterpolated.setDataAt(b, width, 0,
                                             static_cast<uint8_t>(tmp));
    } else {
      // BLUE
      // green
      tmp = this->currentImg(g, width - 1, 0);
      tmp += this->currentImg(g, width, 1);
      this->currentImgInterpolated.setDataAt(g, width, 0,
                                             static_cast<uint8_t>(tmp / 2));
      // red
      tmp = this->currentImg(r, width - 1, 1);
      this->currentImgInterpolated.setDataAt(r, width, 0,
                                             static_cast<uint8_t>(tmp));
    }
  }
  //------------------------------------------------------------//

  //-------------------- first column --------------------------//
  green = !beginWithG;
  rg = !beginWithRG;
  for (uint32_t j = 1; j < height; j++) {
    if (rg) {
      if (green) {
        // G1
        // red
        tmp = this->currentImg(r, 1, j);
        this->currentImgInterpolated.setDataAt(r, 0, j,
                                               static_cast<uint8_t>(tmp));
        // blue
        tmp = this->currentImg(b, 0, j - 1);
        tmp += this->currentImg(b, 0, j + 1);
        this->currentImgInterpolated.setDataAt(b, 0, j,
                                               static_cast<uint8_t>(tmp / 2));
      } else {
        // RED
        // green
        tmp = this->currentImg(g, 1, j);
        tmp += this->currentImg(g, 0, j - 1);
        tmp += this->currentImg(g, 0, j + 1);
        this->currentImgInterpolated.setDataAt(g, 0, j,
                                               static_cast<uint8_t>(tmp / 3));
        // blue
        tmp = this->currentImg(b, 1, j - 1);
        tmp += this->currentImg(b, 1, j + 1);
        this->currentImgInterpolated.setDataAt(b, 0, j,
                                               static_cast<uint8_t>(tmp / 2));
      }
    } else {
      if (green) {
        // G2
        // red
        tmp = this->currentImg(r, 0, j - 1);
        tmp += this->currentImg(r, 0, j + 1);
        this->currentImgInterpolated.setDataAt(r, 0, j,
                                               static_cast<uint8_t>(tmp / 2));
        // blue
        tmp = this->currentImg(b, 1, j);
        this->currentImgInterpolated.setDataAt(b, 0, j,
                                               static_cast<uint8_t>(tmp));
      } else {
        // BLUE
        // green
        tmp = this->currentImg(g, 1, j);
        tmp += this->currentImg(g, 0, j - 1);
        tmp += this->currentImg(g, 0, j + 1);
        this->currentImgInterpolated.setDataAt(g, 0, j,
                                               static_cast<uint8_t>(tmp / 3));
        // red
        tmp = this->currentImg(r, 1, j - 1);
        tmp += this->currentImg(r, 1, j + 1);
        this->currentImgInterpolated.setDataAt(r, 0, j,
                                               static_cast<uint8_t>(tmp / 2));
      }
    }
    rg = !rg;
    if (rg == ((beginWithRG) == beginWithG)) {
      green = true;
    } else {
      green = false;
    }
  }
  //------------------------------------------------------------//

  //-------------------- last column --------------------------//
  green = beginWithG;
  rg = (width % 2) == 0 ? beginWithRG : !beginWithRG;
  for (uint32_t j = 1; j < height; j++) {
    if (rg) {
      if (green) {
        // G1
        // red
        tmp = this->currentImg(r, width - 1, j);
        this->currentImgInterpolated.setDataAt(r, width, j,
                                               static_cast<uint8_t>(tmp));
        // blue
        tmp = this->currentImg(b, width, j - 1);
        tmp += this->currentImg(b, width, j + 1);
        this->currentImgInterpolated.setDataAt(b, width, j,
                                               static_cast<uint8_t>(tmp / 2));
      } else {
        // RED
        // green
        tmp = this->currentImg(g, width - 1, j);
        tmp += this->currentImg(g, width, j - 1);
        tmp += this->currentImg(g, width, j + 1);
        this->currentImgInterpolated.setDataAt(g, width, j,
                                               static_cast<uint8_t>(tmp / 3));
        // blue
        tmp = this->currentImg(b, width - 1, j - 1);
        tmp += this->currentImg(b, width - 1, j + 1);
        this->currentImgInterpolated.setDataAt(b, width, j,
                                               static_cast<uint8_t>(tmp / 2));
      }
    } else {
      if (green) {
        // G2
        // red
        tmp = this->currentImg(r, width, j - 1);
        tmp += this->currentImg(r, width, j + 1);
        this->currentImgInterpolated.setDataAt(r, width, j,
                                               static_cast<uint8_t>(tmp / 2));
        // blue
        tmp = this->currentImg(b, width - 1, j);
        this->currentImgInterpolated.setDataAt(b, width, j,
                                               static_cast<uint8_t>(tmp));
      } else {
        // BLUE
        // green
        tmp = this->currentImg(g, width - 1, j);
        tmp += this->currentImg(g, width, j - 1);
        tmp += this->currentImg(g, width, j + 1);
        this->currentImgInterpolated.setDataAt(g, width, j,
                                               static_cast<uint8_t>(tmp / 3));
        // red
        tmp = this->currentImg(r, width - 1, j - 1);
        tmp += this->currentImg(r, width - 1, j + 1);
        this->currentImgInterpolated.setDataAt(r, width, j,
                                               static_cast<uint8_t>(tmp / 2));
      }
    }
    rg = !rg;
    if (rg == ((!beginWithRG) == beginWithG)) {
      green = true;
    } else {
      green = false;
    }
  }
  //------------------------------------------------------------//
  qDebug() << "Interpolate 4Q end: " << QTime::currentTime().second() << "."
           << QTime::currentTime().msec();
}
