text: /**
   *  @filename   :   epdpaint.cpp
   *  @brief      :   Paint tools
   *  @author     :   Yehui from Waveshare
   *
   *  Copyright (C) Waveshare     September 9 2017
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documnetation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to  whom the Software is
   * furished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  //#include <avr/pgmspace.h>
  #include <pgmspace.h>
  #include "epdpaint.h"
  
  Paint::Paint(unsigned char *image, int width, int height) {
    this->rotate = ROTATE_0;
    this->image = image;
    /* 1 byte = 8 pixels, so the width should be the multiple of 8 */
    this->width = width % 8 ? width + 8 - (width % 8) : width;
    this->height = height;
  }
  
  Paint::~Paint() {}
  
  /**
   *  @brief: clear the image
   */
  void Paint::Clear(int colored) {
    for (int x = 0; x < this->width; x++) {
      for (int y = 0; y < this->height; y++) {
        DrawAbsolutePixel(x, y, colored);
      }
    }
  }
  
  /**
   *  @brief: this draws a pixel by absolute coordinates.
   *          this function won't be affected by the rotate parameter.
   */
  void Paint::DrawAbsolutePixel(int x, int y, int colored) {
    if (x < 0 || x >= this->width || y < 0 || y >= this->height) {
      return;
    }
    if (IF_INVERT_COLOR) {
      if (colored) {
        image[(x + y * this->width) / 8] |= 0x80 >> (x % 8);
      } else {
        image[(x + y * this->width) / 8] &= ~(0x80 >> (x % 8));
      }
    } else {
      if (colored) {
        image[(x + y * this->width) / 8] &= ~(0x80 >> (x % 8));
      } else {
        image[(x + y * this->width) / 8] |= 0x80 >> (x % 8);
      }
    }
  }
  
  /**
   *  @brief: Getters and Setters
   */
  unsigned char *Paint::GetImage(void) { return this->image; }
  
  int Paint::GetWidth(void) { return this->width; }
  
  void Paint::SetWidth(int width) {
    this->width = width % 8 ? width + 8 - (width % 8) : width;
  }
  
  int Paint::GetHeight(void) { return this->height; }
  
  void Paint::SetHeight(int height) { this->height = height; }
  
  int Paint::GetRotate(void) { return this->rotate; }
  
  void Paint::SetRotate(int rotate) { this->rotate = rotate; }
  
  /**
   *  @brief: this draws a pixel by the coordinates
   */
  void Paint::DrawPixel(int x, int y, int colored) {
    int point_temp;
    if (this->rotate == ROTATE_0) {
      if (x < 0 || x >= this->width || y < 0 || y >= this->height) {
        return;
      }
      DrawAbsolutePixel(x, y, colored);
    } else if (this->rotate == ROTATE_90) {
      if (x < 0 || x >= this->height || y < 0 || y >= this->width) {
        return;
      }
      point_temp = x;
      x = this->width - y;
      y = point_temp;
      DrawAbsolutePixel(x, y, colored);
    } else if (this->rotate == ROTATE_180) {
      if (x < 0 || x >= this->width || y < 0 || y >= this->height) {
        return;
      }
      x = this->width - x;
      y = this->height - y;
      DrawAbsolutePixel(x, y, colored);
    } else if (this->rotate == ROTATE_270) {
      if (x < 0 || x >= this->height || y < 0 || y >= this->width) {
        return;
      }
      point_temp = x;
      x = y;
      y = this->height - point_temp;
      DrawAbsolutePixel(x, y, colored);
    }
  }
  
  /**
   *  @brief: this draws a line on the frame buffer
   */
  void Paint::DrawLine(int x0, int y0, int x1, int y1, int colored) {
    /* Bresenham algorithm */
    int dx = x1 - x0 >= 0 ? x1 - x0 : x0 - x1;
    int sx = x0 < x1 ? 1 : -1;
    int dy = y1 - y0 <= 0 ? y1 - y0 : y0 - y1;
    int sy = y0 < y1 ? 1 : -1;
    int err = dx + dy;
  
    while ((x0 != x1) && (y0 != y1)) {
      DrawPixel(x0, y0, colored);
      if (2 * err >= dy) {
        err += dy;
        x0 += sx;
      }
      if (2 * err <= dx) {
        err += dx;
        y0 += sy;
      }
    }
  }
  
  /**
   *  @brief: this draws a horizontal line on the frame buffer
   */
  void Paint::DrawHorizontalLine(int x, int y, int line_width, int colored) {
    int i;
    for (i = x; i < x + line_width; i++) {
      DrawPixel(i, y, colored);
    }
  }
  
  /**
   *  @brief: this draws a vertical line on the frame buffer
   */
  void Paint::DrawVerticalLine(int x, int y, int line_height, int colored) {
    int i;
    for (i = y; i < y + line_height; i++) {
      DrawPixel(x, i, colored);
    }
  }
  
  /**
   *  @brief: this draws a rectangle
   */
  void Paint::DrawRectangle(int x0, int y0, int x1, int y1, int colored) {
    int min_x, min_y, max_x, max_y;
    min_x = x1 > x0 ? x0 : x1;
    max_x = x1 > x0 ? x1 : x0;
    min_y = y1 > y0 ? y0 : y1;
    max_y = y1 > y0 ? y1 : y0;
  
    DrawHorizontalLine(min_x, min_y, max_x - min_x + 1, colored);
    DrawHorizontalLine(min_x, max_y, max_x - min_x + 1, colored);
    DrawVerticalLine(min_x, min_y, max_y - min_y + 1, colored);
    DrawVerticalLine(max_x, min_y, max_y - min_y + 1, colored);
  }
  
  /**
   *  @brief: this draws a filled rectangle
   */
  void Paint::DrawFilledRectangle(int x0, int y0, int x1, int y1, int colored) {
    int min_x, min_y, max_x, max_y;
    int i;
    min_x = x1 > x0 ? x0 : x1;
    max_x = x1 > x0 ? x1 : x0;
    min_y = y1 > y0 ? y0 : y1;
    max_y = y1 > y0 ? y1 : y0;
  
    for (i = min_x; i <= max_x; i++) {
      DrawVerticalLine(i, min_y, max_y - min_y + 1, colored);
    }
  }
  
  /**
   *  @brief: this draws a circle
   */
  void Paint::DrawCircle(int x, int y, int radius, int colored) {
    /* Bresenham algorithm */
    int x_pos = -radius;
    int y_pos = 0;
    int err = 2 - 2 * radius;
    int e2;
  
    do {
      DrawPixel(x - x_pos, y + y_pos, colored);
      DrawPixel(x + x_pos, y + y_pos, colored);
      DrawPixel(x + x_pos, y - y_pos, colored);
      DrawPixel(x - x_pos, y - y_pos, colored);
      e2 = err;
      if (e2 <= y_pos) {
        err += ++y_pos * 2 + 1;
        if (-x_pos == y_pos && e2 <= x_pos) {
          e2 = 0;
        }
      }
      if (e2 > x_pos) {
        err += ++x_pos * 2 + 1;
      }
    } while (x_pos <= 0);
  }
  
  /**
   *  @brief: this draws a filled circle
   */
  void Paint::DrawFilledCircle(int x, int y, int radius, int colored) {
    /* Bresenham algorithm */
    int x_pos = -radius;
    int y_pos = 0;
    int err = 2 - 2 * radius;
    int e2;
  
    do {
      DrawPixel(x - x_pos, y + y_pos, colored);
      DrawPixel(x + x_pos, y + y_pos, colored);
      DrawPixel(x + x_pos, y - y_pos, colored);
      DrawPixel(x - x_pos, y - y_pos, colored);
      DrawHorizontalLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored);
      DrawHorizontalLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored);
      e2 = err;
      if (e2 <= y_pos) {
        err += ++y_pos * 2 + 1;
        if (-x_pos == y_pos && e2 <= x_pos) {
          e2 = 0;
        }
      }
      if (e2 > x_pos) {
        err += ++x_pos * 2 + 1;
      }
    } while (x_pos <= 0);
  }
  
  /* END OF FILE */
  