text: #include "display_ch.h"
  //#define _ASC16_ 
  //#define _HZK16_ 
  DisplayCh::DisplayCh(){
    // Set default highlight color
    highlightcolor = TFT_RED;
    highlighted = false;
  
    // HZK16 is not used by default
    hzk16Used = false;
    hzk16Type = DontUsedHzk16;
    //pHzk16File = nullptr;
    //pAsc16File = nullptr;
  
    textwrap = true;  
  
    _width  = 320;
    _height = 240;
  }
  /***************************************************************************************
  
  ** Function name:           setTextWrap
  
  ** Description:             Define if text should wrap at end of line
  
  ***************************************************************************************/
  
  void DisplayCh::setTextWrap(boolean w) { textwrap = w; }
  /***************************************************************************************
  
  ** Function name:           setTextSize
  
  ** Description:             Set the text size multiplier
  
  ***************************************************************************************/
  
  void DisplayCh::setTextSize(uint8_t s) {
  
    if (s > 7)
  
      s = 7; // Limit the maximum size multiplier so byte variables can be used
  
             // for rendering
  
    textsize = (s > 0) ? s : 1; // Don't allow font size 0
  
  
    // Calculate the text width and height.
  
    // It's 8 pixels wide and 16 pixels high in for ASCII characters,
  
    // and 16 pixels both wide and high for GBK characters in HZK16 mode.
  
    ascCharWidth = 8 * textsize;
  
    ascCharHeigth = 16 * textsize;
  
    gbkCharWidth = ascCharHeigth;
  
    gbkCharHeight = gbkCharWidth;
  
  }
  
  
  /***************************************************************************************
  
  ** Function name:           setCursor
  
  ** Description:             Set the text cursor x,y position
  
  ***************************************************************************************/
  
  void DisplayCh::setCursor(int16_t x, int16_t y) {
  
    cursor_x = x;
  
    cursor_y = y;
  
  }
  
  
  
  /***************************************************************************************
  
  ** Function name:           setCursor
  
  ** Description:             Set the text cursor x,y position and font
  
  ***************************************************************************************/
  
  void DisplayCh::setCursor(int16_t x, int16_t y, uint8_t font) {
  
    textfont = font;
  
    cursor_x = x;
  
    cursor_y = y;
  
  }
  /***************************************************************************************
  
  ** Function name:           setTextColor
  
  ** Description:             Set the font foreground colour (background is
  
  *transparent)
  
  ***************************************************************************************/
  
  void DisplayCh::setTextColor(uint16_t c) {
  
    // For 'transparent' background, we'll set the bg
  
    // to the same as fg instead of using a flag
  
    textcolor = textbgcolor = c;
  
  }
  
  /***************************************************************************************
  
  ** Function name:           fillScreen
  
  ** Description:             Clear the screen to defined colour
  
  ***************************************************************************************/
  
  void DisplayCh::fillScreen(uint32_t color) {
  
    MA.Lcd.fillRect(0, 0, _width, _height, color);
  
  }
  
  /***************************************************************************************
  
  ** Function name:           setTextColor
  
  ** Description:             Set the font foreground and background colour
  
  ***************************************************************************************/
  
  void DisplayCh::setTextColor(uint16_t c, uint16_t b) {
  
    textcolor = c;
  
    textbgcolor = b;
  
  }
  
  /***************************************************************************************
  
  ** Function name:           loadHzk16
  
  ** Description:             loadHzk16 fonts
  
  ** Params:
  
  **  HZK16Path: HZK16 file path on TF card. e.g. /HZK16, means file on the
  
  *root dir of TF card. * ASC16Path: ASC16 file path on TF card.
  
  ***************************************************************************************/
  
  void DisplayCh::loadHzk16(Hzk16Types hzkTypes,const char *HZK16Path, const char *ASC16Path) {
  
    if (hzk16Used)
  
      return;
  
  
  //Hzk16Types hzkTypes = InternalHzk16
  if(hzkTypes == InternalHzk16){
  //#if defined(_ASC16_) && defined(_HZK16_)
  
    pAscCharMatrix = (uint8_t *)&ASC16[0];
  
    pGbkCharMatrix = (uint8_t *)&HZK16[0];
  
    Serial.println("HZK16 path: Internal");
  
    Serial.println("ASC16 path: Internal");
  
    hzk16Used = initHzk16(true, nullptr, nullptr);
  }
  else
  {
    pAscCharMatrix = NULL;
  
    pGbkCharMatrix = NULL;
  
  
  
    Serial.print("HZK16 path: /SD");
  
    Serial.println(HZK16Path);
  
    Serial.print("ASC16 path: /SD");
  
    Serial.println(ASC16Path);
  
    hzk16Used = initHzk16(true, HZK16Path, ASC16Path);
  }
  
  
  
    Serial.print("HZK16 init result: ");
  
    Serial.println(isHzk16Used());
  
  }
  
  /***************************************************************************************
  
  ** Function name:           disableHzk16
  
  ** Description:             disable Hzk16 fonts
  
  ** Params:
  
  ***************************************************************************************/
  
  void DisplayCh::disableHzk16() {
  
    if (hzk16Used) {
  
      hzk16Used = initHzk16(false);
  
    }
  
  }
  
  bool DisplayCh::initHzk16(boolean use, const char *HZK16Path,
                          const char *ASC16Path) {
  
    bool result = false;
  
    if (use == false) { // Do not use HZK16 and ASC16 fonts
  
      hzk16Type = DontUsedHzk16;
  
      Serial.println("Use default font.");
  
    } else if (pAscCharMatrix == NULL ||
  
               pGbkCharMatrix ==
  
                   NULL) { // Use external HZK16 and ASC16 font on TF card.
  
  
  
      // Check if HZK16 and ASC16 files exist on TF card.
  
      if (SD.exists(HZK16Path) && SD.exists(ASC16Path)) { // Exists
  
        hzk16Type = ExternalHzk16;
  
        Serial.println("Use external HZK16 and ASC16 font.");
  
      } else { // Not exists
  
        hzk16Type = DontUsedHzk16;
  
        Serial.println("External font file HZK16/ASC16 lost, use default font.");
  
      }
  
    } else { // Use internal HZK16 and ASC16 fonts
  
      hzk16Type = InternalHzk16;
  
      Serial.println("Use internal HZK16 and ASC16 font.");
  
    }
  
  
  
    switch (hzk16Type) {
  
    case ExternalHzk16: {
  
      if (pHzk16File == NULL) {
  
        Hzk16File = SD.open(HZK16Path);
  
        pHzk16File = &Hzk16File;
  
      }
  
      if (pAsc16File == NULL) {
  
        Asc16File = SD.open(ASC16Path);
  
        pAsc16File = &Asc16File;
  
      }
  
      hzkBufCount = 0;
  
      break;
  
    }
  
    case InternalHzk16: {
  
      if (pAscCharMatrix == NULL || pGbkCharMatrix == NULL) {
  
        hzk16Type = DontUsedHzk16;
  
      }
  
  
  
      if (pHzk16File != NULL) {
  
        pHzk16File->close();
  
        pHzk16File = NULL;
  
      }
  
      if (pAsc16File != NULL) {
  
        pAsc16File->close();
  
        pAsc16File = NULL;
  
      }
  
      hzkBufCount = 0;
  
      break;
  
    }
  
    case DontUsedHzk16: {
  
      if (pHzk16File != NULL) {
  
        pHzk16File->close();
  
        pHzk16File = NULL;
  
      }
  
      if (pAsc16File != NULL) {
  
        pAsc16File->close();
  
        pAsc16File = NULL;
  
      }
  
      break;
  
    }
  
    }
  
    return hzk16Type != DontUsedHzk16;
  
  }
  void DisplayCh::writeHzkAsc(const char c) {
  
    if (c == '\n') {
  
      cursor_x = 0;
  
      cursor_y += ascCharHeigth;
  
    } else if (c != '\r') {
  
      uint32_t offset;
  
      uint8_t mask;
  
      uint16_t posX = cursor_x, posY = cursor_y;
  
      uint8_t charMatrix[16];
  
      uint8_t *pCharMatrix;
  
  
  
      offset = (uint32_t)c * 16;
  
  
  
      if (hzk16Type == ExternalHzk16) {
  
        pAsc16File->seek(offset, SeekSet);
  
        pAsc16File->readBytes((char *)&charMatrix[0], 16);
  
        pCharMatrix = &charMatrix[0];
  
      } else {
  
        if (pAscCharMatrix == NULL) {
  
          return;
  
        }
  
        pCharMatrix = pAscCharMatrix + offset;
  
      }
  
  
  
      // startWrite();
  
  
  
      if (highlighted) {
  
        MA.Lcd.fillRect(cursor_x, cursor_y, ascCharWidth, ascCharHeigth, highlightcolor);
  
      } else if (istransparent == false) {
  
        MA.Lcd.fillRect(cursor_x, cursor_y, ascCharWidth, ascCharHeigth, textbgcolor);
  
      }
  
  
  
      for (uint8_t row = 0; row < 16; row++) {
  
        mask = 0x80;
  
        posX = cursor_x;
  
        for (uint8_t col = 0; col < 8; col++) {
  
          if ((*pCharMatrix & mask) != 0) {
  
            if (textsize == 1) {
  
              MA.Lcd.drawPixel(posX, posY, textcolor);
  
            } else {
  
              MA.Lcd.fillRect(posX, posY, textsize, textsize, textcolor);
  
            }
  
          }
  
          posX += textsize;
  
          mask >>= 1;
  
        }
  
        posY += textsize;
  
        pCharMatrix++;
  
      }
  
  
  
      // endWrite();
  
  
  
      cursor_x += ascCharWidth;
  
      if (textwrap && ((cursor_x + ascCharWidth) > _width)) {
  
        cursor_x = 0;
  
        cursor_y += ascCharHeigth;
  
      }
  
    }
  
  }
  
  void DisplayCh::writeHzk(char* c){
  
    char *ret  = c;
    if(ret == NULL) return;
    while(*ret != '\0'){
      while(*ret <= 0xA0){
        if(*ret == '\0') return;
        writeHzkAsc(*ret);
        ret++;  
      }
      writeHzkGbk(ret);
      ret++;
      ret++;
    }
  }
  
  
  void DisplayCh::writeHzkGbk(const char *c) {
  
    uint32_t offset;
  
    uint8_t mask;
  
    uint16_t posX = cursor_x, posY = cursor_y;
  
    uint8_t charMatrix[32];
  
    uint8_t *pCharMatrix;
  
  
  
    offset = (uint32_t)(94 * (uint32_t)(c[0] - 0xA1) + (uint32_t)(c[1] - 0xA1)) * 32;
  
    if (hzk16Type == ExternalHzk16) {
  
      pHzk16File->seek(offset, SeekSet);
  
      pHzk16File->readBytes((char *)&charMatrix[0], 32);
  
      pCharMatrix = &charMatrix[0];
  
    } else {
  
      if (pGbkCharMatrix == NULL) {
  
        return;
  
      }
  
      pCharMatrix = pGbkCharMatrix + offset;
  
    }
  
  
  
    // startWrite();
  
  
  
    if (highlighted) {
  
      MA.Lcd.fillRect(cursor_x, cursor_y, gbkCharWidth, gbkCharHeight, highlightcolor);
  
    } else if (istransparent == false) {
  
      MA.Lcd.fillRect(cursor_x, cursor_y, gbkCharWidth, gbkCharHeight, textbgcolor);
  
    }
  
  
  
    for (uint8_t row = 0; row < 16; row++) {
  
      posX = cursor_x;
  
      mask = 0x80;
  
      for (uint8_t col = 0; col < 8; col++) {
  
        if ((*pCharMatrix & mask) != 0) {
  
          if (textsize == 1) {
  
            MA.Lcd.drawPixel(posX, posY, textcolor);
  
          } else {
  
            MA.Lcd.fillRect(posX, posY, textsize, textsize, textcolor);
  
          }
  
        }
  
        if ((*(pCharMatrix + 1) & mask) != 0) {
  
          if (textsize == 1) {
  
            MA.Lcd.drawPixel(posX + ascCharWidth, posY, textcolor);
  
          } else {
  
            MA.Lcd.fillRect(posX + ascCharWidth, posY, textsize, textsize, textcolor);
  
          }
  
        }
  
        mask >>= 1;
  
        posX += textsize;
  
      }
  
      posY += textsize;
  
      pCharMatrix += 2;
  
    }
  
    // endWrite();
  
    cursor_x += gbkCharWidth;
  
    if (textwrap && ((cursor_x + gbkCharWidth) > _width)) {
  
      cursor_x = 0;
  
      cursor_y += gbkCharHeight;
  
    }
  
  }
  