/***************************************************
  Arduino TFT graphics library for the ILI9341

  modified to work with ESP32

  Derived from the TFT_ILI9341 library which was
  derived from the Adafruit_GFX library and the
  associated driver library.

 ****************************************************/

#include "SimpleILI9341.h"

#include <SPI.h>

int16_t  Cursorx, Cursory, win_xe, win_ye;

uint16_t tft_width = 320;
uint16_t tft_height = 240;

uint8_t pen_width = 1; // if pen_width > 1 then draws with a disc of radius pen_width-1 // the overall width is pen_width*2-1

uint8_t letter_gap = 1;

uint16_t addr_row, addr_col;
//uint8_t  mySPCR;

bool execDrawChar = true;
bool ILI9341fast = false;

uint8_t tft_CS   = 10; // Chip select control pin
uint8_t tft_DC   = 8;  // Data Command control pin
uint8_t tft_RST  = 7;  // Reset pin (could connect to Arduino RESET pin)

int touch_xmin = 340;
int touch_ymin = 340;
int touch_xmax = 3800;
int touch_ymax = 3800;
uint8_t touch_Rotation = 1;
uint8_t touch_CS = 2; // Chip select control pin

void tft_fastPixel(uint16_t x, uint16_t y, uint16_t color);
void tft_setAddrWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1);

#define ILI9341_INIT_DELAY 0x80

// ILI9341 control registers
#define ILI9341_NOP     0x00
#define ILI9341_SWRESET 0x01
#define ILI9341_RDDID   0x04
#define ILI9341_RDDST   0x09
#define ILI9341_RDIMGFMT  0x0A
#define ILI9341_RDMADCTL  0x0B
#define ILI9341_RDPIXFMT  0x0C
#define ILI9341_RD_DISP_IMG_FMT 0x0D
#define ILI9341_RD_DISP_SIG_MODE 0x0E
#define ILI9341_RDSELFDIAG  0x0F
#define ILI9341_SLPIN   0x10
#define ILI9341_SLPOUT  0x11
#define ILI9341_PTLON   0x12
#define ILI9341_NORON   0x13
#define ILI9341_INVOFF  0x20
#define ILI9341_INVON   0x21
#define ILI9341_GAMMASET 0x26
#define ILI9341_DISPOFF 0x28
#define ILI9341_DISPON  0x29
#define ILI9341_CASET   0x2A
#define ILI9341_PASET   0x2B
#define ILI9341_RAMWR   0x2C
#define ILI9341_COLOR_SET 0x2D
#define ILI9341_RAMRD   0x2E
#define ILI9341_PTLAR   0x30
#define ILI9341_VSCRDEF 0x33
#define ILI9341_TEAR_EFF_OFF 0x34
#define ILI9341_TEAR_EFF_ON 0x35
#define ILI9341_MADCTL  0x36
#define ILI9341_VSCRSADD 0x37
#define ILI9341_IDLE_MODE_OFF 0x38
#define ILI9341_IDLE_MODE_ON 0x39
#define ILI9341_PIXFMT  0x3A
#define ILI9341_WRT_MEM_CTRL 0x3C
#define ILI9341_RD_MEM_CTRL 0x3E
#define ILI9341_SET_TEAR_SCANLINE 0x44
#define ILI9341_GET_SCANLINE 0x45
#define ILI9341_WRT_DISP_BRIGHT 0x51
#define ILI9341_RD_DISP_BRIGHT 0x52
#define ILI9341_WRT_CTRL_DISP 0x53
#define ILI9341_RD_CTRL_DISP 0x54
#define ILI9341_WRT_ADAPT_BRIGHT_CTRL 0x55
#define ILI9341_RD_ADAPT_BRIGHT_CTRL 0x56
#define ILI9341_WRT_CABC_MIN_BRIGHT 0x5E
#define ILI9341_RD_CABC_MIN_BRIGHT 0x5F
#define ILI9341_RGB_INTF_SIG_CTRL 0xB0
#define ILI9341_FRMCTR1 0xB1
#define ILI9341_FRMCTR2 0xB2
#define ILI9341_FRMCTR3 0xB3
#define ILI9341_INVCTR  0xB4
#define ILI9341_BLANKING_PORCH_CTRL 0xB5
#define ILI9341_DFUNCTR 0xB6
#define ILI9341_ENTRY_MODE_SET 0xB7
#define ILI9341_BKLGT_CTRL_1 0xB8
#define ILI9341_BKLGT_CTRL_2 0xB9
#define ILI9341_BKLGT_CTRL_3 0xBA
#define ILI9341_BKLGT_CTRL_4 0xBB
#define ILI9341_BKLGT_CTRL_5 0xBC
#define ILI9341_BKLGT_CTRL_7 0xBE
#define ILI9341_BKLGT_CTRL_8 0xBF
#define ILI9341_PWCTR1  0xC0
#define ILI9341_PWCTR2  0xC1
#define ILI9341_PWCTR3  0xC2
#define ILI9341_PWCTR4  0xC3
#define ILI9341_PWCTR5  0xC4
#define ILI9341_VMCTR1  0xC5
#define ILI9341_VMCTR2  0xC7
#define ILI9341_NV_MEM_WRT 0xD0
#define ILI9341_NV_MEM_PROT_KEY 0xD1
#define ILI9341_NV_MEM_STAT_RD 0xD2
#define ILI9341_RD_ID4 0xD3
#define ILI9341_RDID1   0xDA
#define ILI9341_RDID2   0xDB
#define ILI9341_RDID3   0xDC
#define ILI9341_RDID4   0xDD
#define ILI9341_GMCTRP1 0xE0
#define ILI9341_GMCTRN1 0xE1
#define ILI9341_GAMMA_CTRL_1 0xE2
#define ILI9341_GAMMA_CTRL_2 0xE3
#define ILI9341_INTF_CTRL 0xF6


const char SmallFont[] = {
  7, // ymax
  1, // descender
  ' ', //first char
  3, 0x00,0x00,0x00,                           // space
  1, 0xFA,                                     // !
  3, 0xC0,0x00,0xC0,                           // "
  5, 0x28,0xFE,0x28,0xFE,0x28,                 // #
  5, 0x24,0x52,0xFF,0x4A,0x24,                 // dollar
  6, 0x62,0x64,0x08,0x10,0x26,0x46,            // %
  5, 0x5C,0xA2,0x52,0x0C,0x12,                 // &
  1, 0xC0,                                     // '
  2, 0x7E,0x81,                                // (
  2, 0x81,0x7E,                                // )
  2, 0xC0,0xC0,                                // *
  5, 0x08,0x08,0x3E,0x08,0x08,                 // +
  1, 0x03,                                     // ,
  5, 0x08,0x08,0x08,0x08,0x08,                 // -
  1, 0x02,                                     // .
  3, 0x06,0x38,0xC0,                           // /
  4, 0x7C,0x82,0x82,0x7C,                      // 0
  2, 0x40,0xFE,                                // 1
  4, 0x46,0x8A,0x92,0x62,                      // 2
  4, 0x44,0x82,0x92,0x6C,                      // 3
  4, 0x18,0x68,0xFE,0x08,                      // 4
  4, 0xF4,0xA2,0xA2,0x9C,                      // 5
  4, 0x7C,0x92,0x92,0x4C,                      // 6
  4, 0x80,0x8E,0xB0,0xC0,                      // 7
  4, 0x6C,0x92,0x92,0x6C,                      // 8
  4, 0x64,0x92,0x92,0x7C,                      // 9
  1, 0x12,                                     // :
  1, 0x13,                                     // ;
  3, 0x08,0x14,0x22,                           // <
  3, 0x14,0x14,0x14,                           // =
  3, 0x22,0x14,0x08,                           // >
  4, 0x40,0x8A,0x90,0x60,                      // ?
  8, 0x3C,0x42,0x99,0xA5,0xB9,0x85,0x44,0x38,  // @
  6, 0x06,0x38,0xC8,0xC8,0x38,0x06,            // A
  6, 0xFE,0x92,0x92,0x92,0x92,0x6C,            // B
  6, 0x7C,0x82,0x82,0x82,0x82,0x44,            // C
  6, 0xFE,0x82,0x82,0x82,0x82,0x7C,            // D
  5, 0xFE,0x92,0x92,0x92,0x82,                 // E
  5, 0xFE,0x90,0x90,0x90,0x80,                 // F
  6, 0x7C,0x82,0x82,0x92,0x94,0x5E,            // G
  6, 0xFE,0x10,0x10,0x10,0x10,0xFE,            // H
  1, 0xFE,                                     // I
  4, 0x0C,0x02,0x02,0xFC,                      // J
  5, 0xFE,0x10,0x28,0x44,0x82,                 // K
  4, 0xFE,0x02,0x02,0x02,                      // L
  8, 0xFE,0xC0,0x30,0x0C,0x0C,0x30,0xC0,0xFE,  // M
  6, 0xFE,0xC0,0x30,0x0C,0x02,0xFE,            // N
  6, 0x7C,0x82,0x82,0x82,0x82,0x7C,            // O
  6, 0xFE,0x90,0x90,0x90,0x90,0x60,            // P
  6, 0x7C,0x82,0x82,0x8A,0x86,0x7D,            // Q
  6, 0xFE,0x90,0x90,0x90,0x90,0x6E,            // R
  5, 0x64,0x92,0x92,0x92,0x4C,                 // S
  5, 0x80,0x80,0xFE,0x80,0x80,                 // T
  5, 0xFC,0x02,0x02,0x02,0xFC,                 // U
  5, 0xC0,0x38,0x06,0x38,0xC0,                 // V
  7, 0xC0,0x38,0x06,0x18,0x06,0x38,0xC0,       // W
  5, 0xC6,0x28,0x10,0x28,0xC6,                 // X
  5, 0xC0,0x20,0x1E,0x20,0xC0,                 // Y
  5, 0x86,0x8A,0x92,0xA2,0xC2,                 // Z
  2, 0xFF,0x81,                                // [
  3, 0x60,0x1C,0x03,                           // backslash
  2, 0x81,0xFF,                                // ]
  3, 0x40,0x80,0x40,                           // ^
  5, 0x02,0x02,0x02,0x02,0x02,                 // _
  2, 0x80,0x40,                                // '
  4, 0x04,0x2A,0x2A,0x1E,                      // a
  4, 0xFE,0x22,0x22,0x1C,                      // b
  4, 0x1C,0x22,0x22,0x14,                      // c
  4, 0x1C,0x22,0x22,0xFE,                      // d
  4, 0x1C,0x2A,0x2A,0x18,                      // e
  2, 0x7E,0x90,                                // f
  4, 0x19,0x25,0x25,0x3E,                      // g
  4, 0xFE,0x10,0x20,0x1E,                      // h
  1, 0xBE,                                     // i
  2, 0x01,0xBF,                                // j
  4, 0xFE,0x18,0x24,0x02,                      // k
  1, 0xFE,                                     // l
  7, 0x3E,0x20,0x20,0x3E,0x20,0x20,0x1E,       // m
  4, 0x3E,0x20,0x20,0x1E,                      // n
  5, 0x1C,0x22,0x22,0x22,0x1C,                 // o
  4, 0x3F,0x24,0x24,0x18,                      // p
  4, 0x18,0x24,0x24,0x3F,                      // q
  2, 0x3E,0x20,                                // r
  4, 0x12,0x2A,0x2A,0x24,                      // s
  2, 0x7C,0x22,                                // t
  4, 0x3C,0x02,0x02,0x3E,                      // u
  4, 0x38,0x06,0x06,0x38,                      // v
  5, 0x38,0x06,0x18,0x06,0x38,                 // w
  3, 0x36,0x08,0x36,                           // x
  3, 0x31,0x0A,0x3C,                           // y
  3, 0x26,0x2A,0x32,                           // z
  3, 0x08,0x36,0x41,                           // {
  1, 0x7F,                                     // |
  3, 0x41,0x36,0x08,                           // )
  4, 0x20,0x40,0x20,0x40,                      // ~
  0}; // end

const char CompactFont[] = {
  12, // ymax
  3, // descender
  ' ', // first char
  5, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //  
  1, 0xE8,0x0F,  // !
  3, 0x00,0x0E,0x00,0x00,0x00,0x0E,  // "
  7, 0x40,0x02,0x58,0x02,0xE0,0x03,0x58,0x0E,0xE0,0x03,0x40,0x0E,0x40,0x02,  // #
  5, 0x30,0x07,0x88,0x08,0xFC,0x0F,0x88,0x08,0x70,0x04,  // $
  9, 0x00,0x07,0x80,0x08,0x88,0x08,0x30,0x07,0xC0,0x01,0x70,0x06,0x88,0x08,0x88,0x00,0x70,0x00,  // %
  7, 0x70,0x00,0x88,0x07,0x88,0x08,0x48,0x09,0x30,0x06,0x50,0x00,0x08,0x00,  // &
  1, 0x00,0x0E,  // '
  3, 0xF0,0x01,0x0C,0x06,0x02,0x08,  // (
  3, 0x02,0x08,0x0C,0x06,0xF0,0x01,  // )
  5, 0x00,0x04,0x00,0x05,0x00,0x0E,0x00,0x05,0x00,0x04,  // *
  5, 0x80,0x00,0x80,0x00,0xE0,0x03,0x80,0x00,0x80,0x00,  // +
  2, 0x12,0x00,0x1C,0x00,  // ,
  3, 0x40,0x00,0x40,0x00,0x40,0x00,  // -
  2, 0x18,0x00,0x18,0x00,  // .
  3, 0x18,0x00,0xE0,0x03,0x00,0x0C,  // /
  5, 0xF0,0x07,0x08,0x08,0x08,0x08,0x08,0x08,0xF0,0x07,  // 0
  3, 0x00,0x02,0x00,0x04,0xF8,0x0F,  // 1
  5, 0x08,0x04,0x18,0x08,0x28,0x08,0xC8,0x08,0x08,0x07,  // 2
  5, 0x10,0x04,0x08,0x08,0x88,0x08,0x88,0x08,0x70,0x07,  // 3
  5, 0x60,0x00,0xA0,0x01,0x20,0x06,0xF8,0x0F,0x20,0x00,  // 4
  5, 0x90,0x03,0x08,0x0D,0x08,0x09,0x08,0x09,0xF0,0x08,  // 5
  5, 0xF0,0x07,0x88,0x08,0x08,0x09,0x08,0x09,0xF0,0x04,  // 6
  5, 0x00,0x08,0x38,0x08,0xC0,0x09,0x00,0x0E,0x00,0x08,  // 7
  5, 0x70,0x07,0x88,0x08,0x88,0x08,0x88,0x08,0x70,0x07,  // 8
  5, 0x90,0x07,0x48,0x08,0x48,0x08,0x88,0x08,0xF0,0x07,  // 9
  1, 0x08,0x02,  // :
  1, 0x0E,0x02,  // ;
  5, 0x80,0x00,0x40,0x01,0x40,0x01,0x40,0x01,0x20,0x02,  // <
  6, 0x20,0x01,0x20,0x01,0x20,0x01,0x20,0x01,0x20,0x01,0x20,0x01,  // =
  5, 0x20,0x02,0x40,0x01,0x40,0x01,0x40,0x01,0x80,0x00,  // >
  5, 0x00,0x06,0x00,0x08,0x68,0x08,0x80,0x08,0x00,0x07,  // ?
  11, 0xF8,0x00,0x04,0x03,0x72,0x04,0x89,0x04,0x09,0x09,0x09,0x09,0xF9,0x08,0x89,0x09,0x0A,0x04,0x12,0x06,0xE4,0x01,  // @
  7, 0x18,0x00,0xE0,0x00,0x40,0x07,0x40,0x08,0x40,0x07,0xE0,0x00,0x18,0x00,  // A
  6, 0xF8,0x0F,0x88,0x08,0x88,0x08,0x88,0x08,0x88,0x08,0xF0,0x07,  // B
  7, 0xE0,0x03,0x10,0x04,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x04,0x20,0x02,  // C
  7, 0xF8,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x04,0xE0,0x03,  // D
  6, 0xF8,0x0F,0x88,0x08,0x88,0x08,0x88,0x08,0x88,0x08,0x88,0x08,  // E
  5, 0xF8,0x0F,0x80,0x08,0x80,0x08,0x80,0x08,0x00,0x08,  // F
  7, 0xE0,0x03,0x10,0x04,0x08,0x08,0x08,0x08,0x88,0x08,0x90,0x04,0xE0,0x02,  // G
  7, 0xF8,0x0F,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0xF8,0x0F,  // H
  1, 0xF8,0x0F,  // I
  5, 0x30,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0xF0,0x0F,  // J
  7, 0xF8,0x0F,0x40,0x00,0x80,0x00,0x40,0x01,0x20,0x02,0x10,0x04,0x08,0x08,  // K
  6, 0xF8,0x0F,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,  // L
  7, 0xF8,0x0F,0x00,0x06,0xE0,0x01,0x18,0x00,0xE0,0x01,0x00,0x06,0xF8,0x0F,  // M
  7, 0xF8,0x0F,0x00,0x04,0x00,0x03,0x80,0x00,0x60,0x00,0x10,0x00,0xF8,0x0F,  // N
  7, 0xE0,0x03,0x10,0x04,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x04,0xE0,0x03,  // O
  6, 0xF8,0x0F,0x80,0x08,0x80,0x08,0x80,0x08,0x80,0x08,0x00,0x07,  // P
  7, 0xE0,0x03,0x10,0x04,0x08,0x08,0x28,0x08,0x28,0x08,0x10,0x04,0xE8,0x03,  // Q
  7, 0xF8,0x0F,0x80,0x08,0x80,0x08,0x80,0x08,0xC0,0x08,0xB0,0x08,0x08,0x07,  // R
  6, 0x30,0x07,0x88,0x08,0x88,0x08,0x88,0x08,0x88,0x08,0x70,0x06,  // S
  7, 0x00,0x08,0x00,0x08,0x00,0x08,0xF8,0x0F,0x00,0x08,0x00,0x08,0x00,0x08,  // T
  7, 0xE0,0x0F,0x10,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0xE0,0x0F,  // U
  7, 0x00,0x0C,0x80,0x03,0x60,0x00,0x18,0x00,0x60,0x00,0x80,0x03,0x00,0x0C,  // V
  11, 0x00,0x0E,0xE0,0x01,0x18,0x00,0xE0,0x00,0x00,0x07,0x00,0x08,0x00,0x07,0xE0,0x00,0x18,0x00,0xE0,0x03,0x00,0x0C,  // W
  7, 0x08,0x08,0x30,0x06,0x40,0x01,0x80,0x00,0x40,0x01,0x30,0x06,0x08,0x08,  // X
  7, 0x00,0x08,0x00,0x06,0x00,0x01,0xF8,0x00,0x00,0x01,0x00,0x06,0x00,0x08,  // Y
  7, 0x08,0x00,0x18,0x08,0x68,0x08,0x88,0x08,0x08,0x0B,0x08,0x0C,0x08,0x08,  // Z
  2, 0xFE,0x0F,0x02,0x08,  // [
  3, 0x00,0x0C,0xE0,0x03,0x18,0x00,
  2, 0x02,0x08,0xFE,0x0F,  // ]
  5, 0x80,0x00,0x00,0x07,0x00,0x08,0x00,0x07,0x80,0x00,  // ^
  7, 0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,  // _
  2, 0x00,0x08,0x00,0x04,  // `
  5, 0x30,0x01,0x48,0x02,0x48,0x02,0x50,0x02,0xF8,0x01,  // a
  5, 0xF8,0x0F,0x10,0x01,0x08,0x02,0x08,0x02,0xF0,0x01,  // b
  4, 0xF0,0x01,0x08,0x02,0x08,0x02,0x10,0x01,  // c
  5, 0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x01,0xF8,0x0F,  // d
  5, 0xF0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xD0,0x01,  // e
  4, 0x00,0x02,0xF8,0x07,0x00,0x0A,0x00,0x08,  // f
  5, 0xF2,0x01,0x0A,0x02,0x0A,0x02,0x12,0x01,0xFC,0x03,  // g
  5, 0xF8,0x0F,0x00,0x01,0x00,0x02,0x00,0x02,0xF8,0x01,  // h
  1, 0xF8,0x0B,  // i
  2, 0x02,0x00,0xFC,0x0B,  // j
  5, 0xF8,0x0F,0x40,0x00,0xC0,0x00,0x30,0x01,0x08,0x02,  // k
  1, 0xF8,0x0F,  // l
  9, 0xF8,0x03,0x00,0x01,0x00,0x02,0x00,0x02,0xF8,0x01,0x00,0x01,0x00,0x02,0x00,0x02,0xF8,0x01,  // m
  5, 0xF8,0x03,0x00,0x01,0x00,0x02,0x00,0x02,0xF8,0x01,  // n
  5, 0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01,  // o
  5, 0xFE,0x03,0x10,0x01,0x08,0x02,0x08,0x02,0xF0,0x01,  // p
  5, 0xF0,0x01,0x08,0x02,0x08,0x02,0x10,0x01,0xFE,0x03,  // q
  3, 0xF8,0x03,0x00,0x01,0x00,0x02,  // r
  5, 0x90,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0x30,0x01,  // s
  3, 0x00,0x02,0xF8,0x0F,0x08,0x02,  // t
  5, 0xF0,0x03,0x08,0x00,0x08,0x00,0x08,0x00,0xF8,0x03,  // u
  5, 0x00,0x03,0xE0,0x00,0x18,0x00,0xE0,0x00,0x00,0x03,  // v
  9, 0x00,0x03,0xE0,0x00,0x18,0x00,0xE0,0x00,0x00,0x03,0xE0,0x00,0x18,0x00,0xE0,0x00,0x00,0x03,  // w
  5, 0x08,0x02,0xB0,0x01,0x40,0x00,0xB0,0x01,0x08,0x02,  // x
  5, 0x00,0x03,0xE2,0x00,0x1C,0x00,0xE0,0x00,0x00,0x03,  // y
  5, 0x08,0x02,0x38,0x02,0x48,0x02,0x88,0x03,0x08,0x02,  // z
  3, 0x40,0x00,0xBC,0x07,0x02,0x08,  // {
  1, 0xFE,0x0F,  // |
  3, 0x02,0x08,0xBC,0x07,0x40,0x00,  // }
  0};
  
const char MediumFont[] = {
  15, // ymax 
  4, // descender
  ' ', //first char
  5, 0,0,0,0,0,0,0,0,0,0, // space
  1, 160,255, // !
  3, 0,240,0,0,0,240, // "
  8, 0,9,224,9,0,31,0,233,224,9,0,31,0,233,0,9, // #
  6, 192,112,32,136,240,255,32,132,32,130,192,97, // dollar
  12, 0,120,0,132,96,132,128,120,0,1,0,6,0,8,0,16,192,99,32,132,32,4,192,3, // %
  11, 92,1,96,2,32,116,32,140,32,142,192,145,192,112,32,11,32,12,32,8,64,0, // &
  1, 0,240, // '
  4, 92,15,48,48,8,64,4,128, // (
  4, 4,128,8,64,48,48,192,15, // )
  7, 0,72,0,72,0,48,0,252,0,48,0,72,0,72, // *
  9, 0,4,0,4,0,4,0,4,192,127,0,4,0,4,0,4,0,4, // +
  2, 40,0,48,0, // ,
  4, 0,1,0,1,0,1,0,1, // -
  1, 32,0, // .
  4, 96,0,128,7,0,56,0,192, // /
  6, 128,63,64,64,32,128,32,128,64,64,128,63, // 0
  4, 0,64,32,64,224,255,32,0, // 1
  6, 96,96,160,128,32,129,32,130,32,68,96,56, // 2
  6, 32,64,32,128,32,132,32,140,64,148,128,99, // 3
  7, 0,3,0,5,0,9,0,49,0,65,224,255,0,1, // 4
  6, 32,16,32,112,32,144,32,144,64,136,128,135, // 5
  6, 128,15,64,52,32,72,32,136,64,132,128,131, // 6
  7, 0,64,0,128,0,128,96,128,128,131,0,156,0,224, // 7
  6, 192,113,32,138,32,132,32,132,32,138,192,113, // 8
  6, 32,56,32,196,64,130,64,130,128,69,0,62, // 9
  1, 32,8, // :
  2, 40,8,48,0, // ;
  7, 0,4,0,10,0,10,0,17,0,17,0,17,128,32, // <
  9, 0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9,0,9, // =
  7, 128,32,0,17,0,17,0,17,0,10,0,10,0,4, // >
  5, 0,96,0,128,160,131,0,140,0,112, // ?
  14, 224,7,16,24,8,32,228,65,36,70,36,136,68,144,228,145,36,143,36,152,68,64,200,32,16,31,32,0, // @
  11, 32,0,32,0,224,0,32,7,0,57,0,193,0,57,32,7,224,0,32,0,32,0, // A
  9, 32,128,32,128,224,255,32,132,32,132,32,132,32,76,64,50,128,1, // B
  9, 0,31,128,32,64,64,32,128,32,128,32,128,32,128,64,64,128,240, // C
  10, 32,128,32,128,224,255,32,128,32,128,32,128,32,128,64,64,128,32,0,31, // D
  9, 32,128,32,128,224,255,32,132,32,132,32,132,32,142,96,192,128,0, // E
  8, 32,128,32,128,224,255,32,132,32,132,0,132,0,142,0,192, // F
  10, 0,31,128,32,64,64,32,128,32,128,32,128,32,132,32,68,192,231,0,4, // G
  11, 32,128,32,128,224,255,32,132,32,132,0,4,32,132,32,132,224,255,32,128,32,128, // H
  5, 32,128,32,128,224,255,32,128,32,128, // I
  6, 96,0,32,128,32,128,192,255,0,128,0,128, // J
  11, 32,128,32,128,224,255,32,132,32,132,0,10,0,145,160,160,96,192,32,128,32,0, // K
  9, 32,128,32,128,224,255,32,128,32,128,32,0,32,0,96,0,128,0, // L
  14, 32,128,32,128,224,255,32,96,32,24,0,7,128,0,224,0,0,3,32,28,32,96,224,255,32,128,32,128, // M
  12, 32,128,32,128,224,255,32,32,32,16,0,8,0,6,0,129,128,128,224,255,0,128,0,128, // N}M
  10, 0,31,128,32,64,64,32,128,32,128,32,128,32,128,64,64,128,32,0,31, // O}N}MN}
  8, 32,128,32,128,224,255,32,130,32,130,0,130,0,68,0,56, // P}O}N}MN}O}N}MN
  10, 0,31,128,32,64,64,32,128,32,128,48,128,40,128,72,64,132,32,4,31, // Q
  10, 32,128,32,128,224,255,32,132,32,132,0,134,128,73,64,48,32,0,32,0, // R}Q
  7, 224,48,64,72,32,136,32,132,32,132,64,66,128,225, // S
  9, 0,224,0,128,32,128,32,128,224,255,32,128,32,128,0,128,0,224, // T}S
  11, 0,128,0,128,128,255,64,128,32,128,32,0,32,128,64,128,128,255,0,128,0,128, // U
  11, 0,128,0,128,0,240,0,142,192,1,32,0,192,1,0,142,0,240,0,128,0,128, // V
  15, 0,128,0,128,0,224,0,156,128,3,96,128,128,131,0,244,0,142,128,1,96,0,128,143,0,240,0,128,0,128, // W
  11, 32,128,32,128,96,192,160,160,0,155,0,4,0,27,160,160,96,192,32,128,32,128, // X
  10, 0,128,0,128,32,224,32,152,224,7,32,8,32,176,0,192,0,128,0,128, // Y
  9, 32,0,224,224,32,129,32,130,32,140,32,144,32,224,96,128,128,0, // Z
  3, 252,255,4,128,4,128, // [
  4, 0,192,0,60,128,3,96,0, // backslash
  3, 4,128,4,128,252,255, // ]
  8, 0,4,0,24,0,96,0,128,0,128,0,96,0,24,0,4, // ^
  8, 4,0,4,0,4,0,4,0,4,0,4,0,4,0,4,0, // _
  2, 0,128,0,96, // '
  6, 224,4,32,9,32,9,64,10,224,7,32,0, // a
  7, 0,64,192,255,32,4,32,8,32,8,64,4,128,3, // b
  5, 192,7,32,8,32,8,32,8,64,4, // c
  7, 128,3,64,4,32,8,32,8,64,68,224,255,64,0, // d
  5, 192,7,32,10,32,10,32,10,64,6, // e
  6, 32,8,224,127,32,136,32,136,0,128,0,64, // f
  7, 24,0,100,7,164,8,164,8,164,8,36,15,24,8, // g
  7, 32,64,224,255,32,4,0,8,32,8,224,7,32,0, // h
  3, 32,4,224,143,32,0, // i
  4, 4,0,4,0,4,4,248,143, // j
  7, 32,64,224,255,32,1,0,3,192,12,32,8,32,8, // k
  4, 32,0,32,64,224,255,32,0, // l
  11, 32,4,224,15,32,4,0,8,32,8,224,7,32,4,0,8,32,8,224,7,32,0, // m
  7, 32,4,224,15,32,4,0,8,32,8,224,7,32,0, // n
  6, 128,3,64,4,32,8,32,8,64,4,128,3, // o
  7, 4,4,252,15,68,4,32,8,32,8,64,4,128,3, // p
  7, 192,3,32,4,32,8,32,8,68,4,252,15,4,0, // q
  5, 32,4,224,15,32,4,0,8,0,8, // r
  4, 96,6,32,9,32,9,192,12, // s
  4, 0,8,224,63,32,8,64,8, // t
  7, 0,8,192,15,32,0,32,0,64,8,224,15,64,0, // u
  7, 0,8,0,14,192,9,32,0,192,9,0,14,0,8, // v
  11, 0,8,0,14,192,9,96,0,128,9,0,14,192,9,96,0,128,9,0,14,0,8, // w
  7, 32,8,96,12,160,10,0,1,160,10,96,12,32,8, // x
  7, 4,8,4,14,136,9,112,0,192,9,0,14,0,8, // y
  6, 32,0,96,12,160,9,32,10,32,12,96,8, // z
  4, 0,2,240,61,8,64,4,128, // {
  1, 252,255, // |
  4, 4,128,8,64,240,61,0,2, // )
  9, 0,1,0,2,0,2,0,2,0,1,0,1,0,1,0,1,0,2, // ~
  0}; // end

const char LargeFont[] = {
  15, // ymax
  4, // descender
  ' ', //first char
  8,  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // space
  3,  0x70,0xF8,0x70,0xFF,0x70,0xF8, // !
  7,  0x00,0xE0,0x00,0xFC,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0xFC,0x00,0xE0, // "
  10, 0x00,0x11,0x30,0x11,0xC0,0x13,0x00,0x3D,0x00,0xD1,0x30,0x11,0xC0,0x13,0x00,0x3D,0x00,0xD1,0x00,0x11, // #
  7,  0x60,0x78,0x30,0xFC,0x10,0x8E,0xF8,0xFF,0x10,0x87,0xF0,0xC3,0xE0,0x61, // dollar
  16, 0x00,0x7E,0x00,0xFF,0x00,0x81,0x10,0x81,0x20,0xFF,0x40,0x7E,0x80,0x01,0x00,0x02,0x00,0x04,0x00,0x18,0xE0,0x27,0xF0,0x4F,0x10,0x88,0x10,0x08,0xF0,0x0F,0xE0,0x07, // %
  14, 0xE0,0x01,0xE0,0x03,0xF0,0x03,0x30,0x7C,0x10,0xFE,0x90,0x8F,0xD0,0x8F,0xE0,0xF1,0xE0,0x70,0xF0,0x04,0x30,0x07,0x30,0x06,0x20,0x04,0x40,0x00, // &
  3,  0x00,0xE0,0x00,0xFC,0x00,0xE0, // '
  5,  0xE0,0x07,0xF8,0x1F,0x0C,0x30,0x02,0x40,0x01,0x80, // (
  5,  0x01,0x80,0x02,0x40,0x0C,0x30,0xF8,0x1F,0xE0,0x07, // )
  7,  0x00,0x60,0x00,0x6C,0x00,0xAC,0x00,0xF0,0x00,0xAC,0x00,0x6C,0x00,0x60, // *
  9,  0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0xE0,0x3F,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // +
  3,  0x72,0x00,0x74,0x00,0x38,0x00, // ,
  5,  0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00, // -
  3,  0x70,0x00,0x70,0x00,0x70,0x00, // .
  5,  0x30,0x00,0xC0,0x01,0x00,0x0E,0x00,0x70,0x00,0x80, // /
  8,  0x80,0x1F,0xE0,0x7F,0xF0,0xFF,0x10,0x80,0x10,0x80,0xF0,0xFF,0xE0,0x7F,0x80,0x1F, // 0
  7,  0x10,0x20,0x10,0x60,0xF0,0x7F,0xF0,0x7F,0xF0,0xFF,0x10,0x00,0x10,0x00, // 1
  9,  0x10,0x00,0x30,0x60,0x70,0xC0,0xB0,0xC0,0x30,0xE3,0x30,0xFE,0x30,0x7C,0x30,0x38,0x40,0x00, // 2
  8,  0x60,0x20,0x70,0x40,0x30,0xC4,0x10,0xCE,0x10,0xFF,0xE0,0xF7,0xE0,0x73,0xC0,0x01, // 3
  8,  0x80,0x03,0x80,0x05,0x80,0x09,0x80,0x11,0xF0,0x7F,0xF0,0xFF,0xF0,0xFF,0x80,0x01, // 4
  8,  0x60,0x0C,0x70,0x7C,0x30,0xDC,0x30,0xDC,0x10,0xDE,0x10,0xCE,0x20,0xCF,0xC0,0x83, // 5
  8,  0xC0,0x07,0xE0,0x1F,0xF0,0x3F,0x30,0x78,0x10,0x4C,0xF0,0xCF,0xE0,0x87,0xC0,0x83, // 6
  8,  0x00,0x60,0x00,0xC0,0x00,0xC0,0x30,0xC0,0xC0,0xC0,0x00,0xC7,0x00,0xF8,0x00,0xC0, // 7
  8,  0xE0,0x70,0xF0,0x79,0x10,0xFE,0x10,0x8E,0x10,0x8F,0xF0,0x8F,0xE0,0xFB,0xC0,0x71, // 8
  8,  0x10,0x3C,0x10,0x7E,0x30,0xFF,0x20,0x83,0xE0,0xC1,0xC0,0xFF,0x80,0x7F,0x00,0x3E, // 9
  3,  0x70,0x1C,0x70,0x1C,0x70,0x1C, // :
  3,  0x72,0x1C,0x74,0x1C,0x38,0x1C, // ;
  10, 0x00,0x04,0x00,0x0A,0x00,0x0A,0x00,0x0A,0x00,0x11,0x00,0x11,0x80,0x20,0x80,0x20,0x80,0x20,0x40,0x40, // <
  9,  0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09, // =
  10,  0x40,0x40,0x80,0x20,0x80,0x20,0x80,0x20,0x00,0x11,0x00,0x11,0x00,0x0A,0x00,0x0A,0x00,0x0A,0x00,0x04, // >
  8,   0x00,0x70,0x00,0x70,0x70,0xB0,0x70,0x83,0x70,0x84,0x00,0xFC,0x00,0xF8,0x00,0x70, // ?
  17, 0xE0,0x07,0x18,0x0C,0x04,0x30,0x02,0x20,0xE2,0x41,0xF1,0x47,0xF1,0x8F,0x11,0x9E,0x61,0x90,0xF1,0x8F,0xF1,0x9F,0x11,0x9F,0x12,0x50,0x22,0x60,0xC4,0x30,0x08,0x1F,0x10,0x00, // @
  13, 0x10,0x00,0x10,0x00,0x70,0x00,0x90,0x03,0x80,0x0C,0x80,0x30,0x80,0xFC,0x90,0x7F,0xF0,0x1F,0xF0,0x03,0xF0,0x00,0x30,0x00,0x10,0x00, // A
  12, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x84,0x10,0x84,0x10,0x84,0x30,0xCE,0xF0,0xFF,0xE0,0x7B,0xC0,0x31, // B
  12, 0x80,0x0F,0xC0,0x3F,0xE0,0x7F,0x70,0x60,0x30,0xC0,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x80,0x10,0x40,0x20,0x60,0x40,0xF0, // C
  13, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x80,0x10,0x80,0x10,0x80,0x30,0xC0,0x60,0x60,0xE0,0x7F,0xC0,0x3F,0x80,0x0F, // D
  12, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x84,0x10,0x84,0x10,0x9F,0x10,0x80,0x10,0xC0,0x30,0xE0,0xF0,0x00, // E
  11, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x84,0x10,0x84,0x00,0x9F,0x00,0x80,0x00,0xC0,0x00,0xE0, // F
  14, 0x80,0x0F,0xC0,0x3F,0xE0,0x7F,0x60,0x70,0x30,0xC0,0x10,0x80,0x10,0x80,0x10,0x81,0x10,0x81,0xF0,0x41,0xE0,0x61,0xE0,0xF1,0x00,0x01,0x00,0x01, // G
  14, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x84,0x00,0x04,0x00,0x04,0x10,0x84,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x80,0x10,0x80, // H
  7,  0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x80,0x10,0x80, // I
  10, 0xE0,0x00,0xE0,0x00,0xD0,0x00,0x10,0x80,0x10,0x80,0xF0,0xFF,0xE0,0xFF,0xC0,0xFF,0x00,0x80,0x00,0x80, // J
  14, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x84,0x10,0x86,0x80,0x0F,0xD0,0x13,0xF0,0xA1,0x70,0xA0,0x30,0xC0,0x10,0x80,0x10,0x80, // K
  11, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x80,0x10,0x80,0x10,0x00,0x10,0x00,0x30,0x00,0xF0,0x00, // L
  18, 0x10,0x80,0x10,0x80,0xF0,0xFF,0x10,0xC0,0x10,0xF0,0x00,0xFE,0x80,0x3F,0xE0,0x0F,0xF0,0x01,0xC0,0x01,0x00,0x06,0x10,0x38,0x10,0xC0,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x80,0x10,0x80, // M
  14, 0x10,0x00,0x10,0x80,0xF0,0xFF,0x10,0xF0,0x10,0x78,0x00,0x3C,0x00,0x1E,0x00,0x0F,0x80,0x07,0xC0,0x83,0xE0,0x81,0xF0,0xFF,0x00,0x80,0x00,0x80, // N
  13, 0x00,0x0F,0xC0,0x3F,0xE0,0x7F,0x60,0x60,0x30,0xC0,0x10,0x80,0x10,0x80,0x10,0x80,0x30,0xC0,0x60,0x60,0xE0,0x7F,0xC0,0x3F,0x00,0x0F, // O
  11, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x82,0x10,0x82,0x00,0xC6,0x00,0xFE,0x00,0x7C,0x00,0x38, // P
  13, 0x80,0x0F,0xC0,0x3F,0xE0,0x7F,0x70,0x60,0x30,0xC0,0x18,0x80,0x1C,0x80,0x1E,0x80,0x36,0xC0,0x72,0x60,0xE2,0x7F,0xC2,0x3F,0x80,0x0F, // Q
  14, 0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x82,0x10,0x83,0x80,0x83,0xC0,0xC7,0xE0,0xFC,0x70,0x7C,0x30,0x38,0x10,0x00,0x10,0x00, // R
  9,  0xF0,0x39,0x60,0x7C,0x20,0xFC,0x10,0x8E,0x10,0x86,0x10,0x87,0xF0,0x43,0xE0,0xF3,0xC0,0x01, // S
  11, 0x00,0xE0,0x00,0x80,0x10,0x80,0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x80,0x10,0x80,0x00,0x80,0x00,0xE0, // T
  13, 0x00,0x80,0x00,0x80,0xC0,0xFF,0xE0,0xFF,0xF0,0xFF,0x30,0x80,0x10,0x80,0x10,0x00,0x10,0x80,0x20,0x80,0xC0,0xFF,0x00,0x80,0x00,0x80, // U
  13, 0x00,0x80,0x00,0xC0,0x00,0xF0,0x00,0xFC,0x80,0xBF,0xE0,0x8F,0xF0,0x03,0x60,0x00,0x80,0x03,0x00,0x8C,0x00,0xB0,0x00,0xC0,0x00,0x80, // V
  19, 0x00,0x80,0x00,0xC0,0x00,0xF0,0x00,0xFC,0x00,0xBF,0xE0,0x8F,0xF0,0x03,0xE0,0x80,0x00,0x83,0x00,0xEC,0x00,0xF8,0x00,0xFF,0xE0,0x9F,0xF0,0x03,0xE0,0x00,0x00,0x07,0x00,0x98,0x00,0xE0,0x00,0x80, // W
  13, 0x10,0x80,0x10,0x80,0x30,0xC0,0x50,0xF0,0x90,0xF8,0x00,0xBF,0x00,0x0F,0xD0,0x0F,0xF0,0x91,0xF0,0xA0,0x30,0xC0,0x10,0x80,0x10,0x80, // X
  13, 0x00,0x80,0x00,0xC0,0x00,0xF0,0x10,0xF8,0x10,0xBE,0xF0,0x8F,0xF0,0x07,0xF0,0x03,0x10,0x04,0x10,0x98,0x00,0xA0,0x00,0xC0,0x00,0x80, // Y
  12, 0x10,0x00,0x30,0xF0,0xF0,0xC0,0xF0,0x81,0xD0,0x87,0x10,0x8F,0x10,0xBE,0x10,0xF8,0x10,0xF0,0x10,0xC0,0x30,0x80,0xF0,0x00, // Z
  4,  0xFF,0xFF,0xFF,0xFF,0x01,0x80,0x01,0x80, // [
  5,  0x00,0xC0,0x00,0x38,0x00,0x07,0xE0,0x00,0x10,0x00, // backslash
  4,  0x01,0x80,0x01,0x80,0xFF,0xFF,0xFF,0xFF, // ]
  9,  0x00,0x04,0x00,0x18,0x00,0x20,0x00,0x40,0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x18,0x00,0x04, // ^
  10, 0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00, // _
  3,  0xD0,0x0A,0x00,0xC0,0x00,0x60, // '
  9,  0x60,0x0E,0xF0,0x0E,0xF0,0x16,0x90,0x11,0x20,0x11,0xF0,0x1F,0xF0,0x1F,0xF0,0x0F,0x10,0x00, // a
  9,  0x00,0x80,0xF0,0xFF,0xE0,0xFF,0xE0,0xFF,0x10,0x08,0x10,0x10,0xF0,0x1F,0xE0,0x0F,0xC0,0x07, // b
  7,  0xC0,0x07,0xE0,0x0F,0xF0,0x1F,0x30,0x10,0x10,0x10,0x10,0x1C,0x20,0x0C, // c
  9,  0xC0,0x07,0xE0,0x0F,0xF0,0x1F,0x10,0x10,0x20,0x88,0xF0,0xFF,0xF0,0xFF,0xE0,0xFF,0x20,0x00, // d
  7,  0xC0,0x07,0xE0,0x0F,0xF0,0x1F,0x30,0x12,0x10,0x1E,0x10,0x0E,0x20,0x06, // e
  7,  0x10,0x10,0xF0,0x7F,0xF0,0xFF,0xF0,0xFF,0x10,0x90,0x00,0xC0,0x00,0xC0, // f
  9,  0x36,0x07,0x79,0x0F,0xB9,0x1F,0xB9,0x10,0xB9,0x10,0xB9,0x1F,0x39,0x1F,0x3A,0x16,0x1C,0x10, // g
  10, 0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x08,0x00,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x0F,0x10,0x00, // h
  5,  0x10,0x10,0xF0,0xDF,0xF0,0xDF,0xF0,0xDF,0x10,0x00, // i
  6,  0x06,0x00,0x07,0x00,0x01,0x10,0xFF,0xDF,0xFF,0xDF,0xFE,0xDF, // j
  11, 0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x01,0x80,0x03,0xD0,0x13,0xF0,0x1C,0x70,0x18,0x30,0x10,0x10,0x00, // k
  5,  0x10,0x80,0xF0,0xFF,0xF0,0xFF,0xF0,0xFF,0x10,0x00, // l
  15, 0x10,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x1F,0x10,0x08,0x00,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x0F,0x10,0x08,0x00,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x0F,0x10,0x00, // m
  10, 0x10,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x1F,0x10,0x08,0x00,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x0F,0x10,0x00, // n
  8,  0xC0,0x07,0xE0,0x0F,0xF0,0x1F,0x10,0x10,0x10,0x10,0xF0,0x1F,0xE0,0x0F,0xC0,0x07, // o
  9,  0x01,0x10,0xFF,0x1F,0xFF,0x1F,0xFF,0x1F,0x21,0x08,0x10,0x10,0xF0,0x1F,0xE0,0x0F,0xC0,0x07, // p
  9,  0xC0,0x07,0xE0,0x0F,0xF0,0x1F,0x10,0x10,0x21,0x10,0xFF,0x1F,0xFF,0x0F,0xFF,0x1F,0x01,0x00, // q
  8,  0x10,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x1F,0x10,0x08,0x00,0x10,0x00,0x1C,0x00,0x1C, // r
  6,  0x70,0x0E,0x20,0x1F,0x90,0x17,0xD0,0x13,0xF0,0x09,0xE0,0x1C, // s
  6,  0x00,0x10,0xE0,0x1F,0xF0,0x3F,0xF0,0xFF,0x10,0x10,0x20,0x10, // t
  10, 0x00,0x10,0xE0,0x1F,0xF0,0x1F,0xF0,0x1F,0x10,0x00,0x20,0x10,0xF0,0x1F,0xF0,0x1F,0xF0,0x1F,0x10,0x00, // u
  9,  0x00,0x10,0x00,0x1C,0x80,0x1F,0xE0,0x1F,0xF0,0x13,0x60,0x00,0x80,0x13,0x00,0x1C,0x00,0x10, // v
  14, 0x00,0x10,0x00,0x1C,0x00,0x1F,0xE0,0x1F,0xF0,0x13,0xE0,0x00,0x00,0x17,0x00,0x1E,0xE0,0x1F,0xF0,0x17,0xE0,0x00,0x00,0x17,0x00,0x18,0x00,0x10, // w
  9,  0x10,0x10,0x10,0x10,0x30,0x1C,0x50,0x1F,0xC0,0x1F,0xF0,0x03,0xF0,0x14,0x30,0x18,0x10,0x10, // x
  9,  0x06,0x10,0x07,0x18,0x07,0x1E,0x82,0x1F,0xEC,0x17,0xF0,0x01,0x80,0x13,0x00,0x1C,0x00,0x10, // y
  8,  0x10,0x00,0x70,0x1C,0xF0,0x10,0xF0,0x13,0x90,0x1F,0x10,0x1F,0x30,0x1C,0x70,0x10, // z
  5,  0x00,0x01,0x8C,0x32,0xFE,0x7E,0x73,0xCC,0x01,0x80, // {
  1,  0xFF,0xFF, // |
  5,  0x01,0x80,0x73,0xCC,0xFE,0x7E,0x8C,0x32,0x00,0x01, // )
  10, 0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x06, // ~
  0}; // end

const char DigitsFont[] = {
  7, // ymax
  1, // descender
  '-', //first char
  5, 0x08,0x08,0x08,0x08,0x08,                 // -
  1, 0x02,                                     // .
  3, 0x06,0x38,0xC0,                           // /
  4, 0x7C,0x82,0x82,0x7C,                      // 0
  2, 0x40,0xFE,                                // 1
  4, 0x46,0x8A,0x92,0x62,                      // 2
  4, 0x44,0x82,0x92,0x6C,                      // 3
  4, 0x18,0x68,0xFE,0x08,                      // 4
  4, 0xF4,0xA2,0xA2,0x9C,                      // 5
  4, 0x7C,0x92,0x92,0x4C,                      // 6
  4, 0x80,0x8E,0xB0,0xC0,                      // 7
  4, 0x6C,0x92,0x92,0x6C,                      // 8
  4, 0x64,0x92,0x92,0x7C,                      // 9
  0}; // end

/***************************************************************************************
** Function name:           tft_spiwrite
** Description:             Write 8 bits to SPI port
***************************************************************************************/
void tft_spiwrite(uint8_t c)
{
  SPI.transfer(c);
}

/***************************************************************************************
** Function name:           tft_writecommand
** Description:             Send an 8 bit command to the TFT
***************************************************************************************/
void tft_writecommand(uint8_t c)
{
  digitalWrite(tft_DC, LOW);
  digitalWrite(tft_CS, LOW);
  tft_spiwrite(c);
  digitalWrite(tft_CS, HIGH);
}

/***************************************************************************************
** Function name:           tft_writedata
** Description:             Send a 8 bit data value to the TFT
***************************************************************************************/
void tft_writedata(uint8_t c)
{
  digitalWrite(tft_DC, HIGH);
  digitalWrite(tft_CS, LOW);
  tft_spiwrite(c);
  digitalWrite(tft_CS, HIGH);
}

void write16(uint16_t data, int16_t count)
{
  for (; count > 0; count--)
    SPI.transfer16(data);
}

/***************************************************************************************
** Function name:           spi_begin
** Description:             Prepare for SPI communication to TFT
***************************************************************************************/
// If the SPI library has transaction support, these functions
// establish settings and protect from interference from other
// libraries.  Otherwise, they simply do nothing.

#ifdef SPI_HAS_TRANSACTION
#ifdef SUPPORT_TRANSACTIONS

static inline void spi_begin(void) __attribute__((always_inline));

static inline void spi_begin(void) {
  SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE0));
}

static inline void spi_end(void) __attribute__((always_inline));

static inline void spi_end(void) {
  while (!(SPSR & _BV(SPIF))); // wait for everything SPI to finish before freeing up the bus
  SPI.endTransaction();
}
#else // we do not want to SUPPORT_TRANSACTIONS

#define spi_begin()
#define spi_end()

#endif // SUPPORT_TRANSACTIONS

#else
#define spi_begin()
#define spi_end()
#endif

/***************************************************************************************
** Description:             Get initialisation commands from FLASH and send to TFT
***************************************************************************************/
void tft_commandList (const uint8_t *addr)
{
  uint8_t  numCommands, numArgs;
  uint8_t  ms;

  spi_begin();
  numCommands = *addr++;            // Number of commands to follow
  while (numCommands--)                           // For each command...
  {
    tft_writecommand(*addr++);    // Read, issue command
    numArgs = *addr++;        // Number of args to follow
    ms = numArgs & ILI9341_INIT_DELAY;      // If hibit set, delay follows args
    numArgs &= ~ILI9341_INIT_DELAY;         // Mask out delay bit
    while (numArgs--)                       // For each argument...
    {
      tft_writedata(*addr++); // Read, issue argument
    }

    if (ms)
    {
      ms = *addr++;     // Read post-command delay time (ms)
      delay( (ms == 255 ? 500 : ms) );
    }
  }
  spi_end();
}

/***************************************************************************************
** Function name:           DrawBitmap
** Description:             Draw an image stored in an array on the TFT
***************************************************************************************/
void DrawBitmap(int16_t x, int16_t y, const unsigned short *bitmap) {
  int16_t i, j, w, h;
  w = *bitmap++;
  h = *bitmap++;

  //  tft_fastSetup();
  for (j = 0; j < h; j++) {
    for (i = 0; i < w; i++ ) {
      tft_fastPixel(x + i, y + j, *bitmap++);
    }
  }
}

/***************************************************************************************
** Function name:           DrawBitmapMonoBits
** Description:             Draw a black and white image stored as an array of bits
***************************************************************************************/
void DrawBitmapMonoBits(int16_t x, int16_t y, const uint8_t *bitmap, uint16_t color) {
  int16_t i, j, bits, n, w, h;
  w = *bitmap++;
  w = w | (*bitmap++ << 8);
  h = *bitmap++;
  h = h | (*bitmap++ << 8);

  //  tft_fastSetup();
  n = 0;
  for (j = 0; j < h; j++) {
    for (i = 0; i < w; i++ ) {
      if (n % 8 == 0)
        bits = *bitmap++;

      if ((bits & 0x80) == 0)
        tft_fastPixel(x + i, y + j, color);

      bits = bits << 1;
      n++;
    }
  }
}

/***************************************************************************************
** Function name:           DrawBitmapMonoRLE
** Description:             Draw a black and white image stored as RLE
***************************************************************************************/
void DrawBitmapMonoRLE(int16_t x, int16_t y, const uint8_t *bitmap, uint16_t color) {
  int16_t i, j, nb, w, h;
  bool CurIsBlack;

  w = *bitmap++;
  w = w | (*bitmap++ << 8);
  w = w & 0x7FFF;
  h = *bitmap++;
  h = h | (*bitmap++ << 8);

  //  tft_fastSetup();

  nb = 0;
  CurIsBlack = true;
  j = 0;
  i = 0;

  for (j = 0; j < h; j++) {
    for (i = 0; i < w; i++ ) {
      while (nb == 0) {
        nb = *bitmap++;
        CurIsBlack = !CurIsBlack;
      }

      if (!CurIsBlack)
        tft_fastPixel(x + i, y + j, color);
      nb--;
    }
  }
}

/***************************************************************************************
** Function name:           DrawBitmapMono
** Description:             Draw a black and white image stored in an array on the TFT
***************************************************************************************/
void DrawBitmapMono(int16_t x, int16_t y, const uint8_t *bitmap, uint16_t color) {
  if ((bitmap[1] & 0x80) > 0)
    DrawBitmapMonoRLE(x, y, bitmap, color);
  else
    DrawBitmapMonoBits(x, y, bitmap, color);
}

/***************************************************************************************
** Function name:           setCursor
** Description:             Set the text cursor x,y position
***************************************************************************************/
void tft_setCursor(int16_t x, int16_t y)
{
  Cursorx = x;
  Cursory = y;
}

/***************************************************************************************
** Function name:           tft_setAddrWindow
** Description:             define an area to receive a stream of pixels
***************************************************************************************/
// Chip select stays low, use setWindow() from sketches

void tft_setAddrWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
  tft_writecommand(ILI9341_CASET);        // Column addr set
  tft_writedata(x0 >> 8);
  tft_writedata(x0);                    // XSTART
  tft_writedata(x1 >> 8);
  tft_writedata(x1);                    // XEND
  tft_writecommand(ILI9341_PASET);      // Row addr set
  tft_writedata(y0 >> 8);
  tft_writedata(y0);                    // YSTART
  tft_writedata(y1 >> 8);
  tft_writedata(y1);                    // YEND
  tft_writecommand(ILI9341_RAMWR);      // write to RAM
  digitalWrite(tft_CS, LOW);
  digitalWrite(tft_DC, HIGH);
}

/***************************************************************************************
** Function name:           DrawPixel
** Description:             push a single pixel at an arbitrary position
***************************************************************************************/
void DrawPixel(uint16_t x, uint16_t y, uint16_t color)
{
  // Faster range checking, possible because x and y are unsigned
  if ((x >= tft_width) || (y >= tft_height)) return;
  spi_begin();

  digitalWrite(tft_CS, LOW);

  tft_setAddrWindow(x, y, x, y);
  write16(color, 1);

  digitalWrite(tft_CS, HIGH);

  spi_end();
}

/***************************************************************************************
** Function name:           tft_fastPixel
***************************************************************************************/
void tft_fastPixel(uint16_t x, uint16_t y, uint16_t color)
{
  DrawPixel(x, y, color);
}

/***************************************************************************************
** Function name:           DrawVLine
** Description:             draw a vertical line
***************************************************************************************/
void DrawVLine(uint16_t x, uint16_t y, uint16_t h, uint16_t color)
{
#ifdef CLIP_CHECK
  // Rudimentary clipping
  if ((x >= tft_width) || (y >= tft_height)) return;
  if ((y + h - 1) >= tft_height) h = tft_height - y;
#endif

  if (pen_width > 1) {
    DrawLine(x, y, x, y + h - 1, color);
  } else {
    spi_begin();
    tft_setAddrWindow(x, y, x, y + h - 1);
    write16(color, h);
    digitalWrite(tft_CS, HIGH);
    spi_end();
  }
}

/***************************************************************************************
** Function name:           DrawHLineSingle
** Description:             draw a horizontal line with pen width 1
***************************************************************************************/
void DrawHLineSingle(uint16_t x, uint16_t y, uint16_t w, uint16_t color)
{
#ifdef CLIP_CHECK
  // Rudimentary clipping
  if ((x >= tft_width) || (y >= tft_height)) return;
  if ((x + w - 1) >= tft_width)  w = tft_width - x;
#endif

  spi_begin();
  tft_setAddrWindow(x, y, x + w - 1, y);

  write16(color, w);
  digitalWrite(tft_CS, HIGH);

  spi_end();
}

/***************************************************************************************
** Function name:           DrawHLine
** Description:             draw a horizontal line
***************************************************************************************/
void DrawHLine(uint16_t x, uint16_t y, uint16_t w, uint16_t color)
{
  if (pen_width > 1)
    DrawLine(x, y, x + w - 1, y, color);
  else
    DrawHLineSingle(x, y, w, color);
}

/***************************************************************************************
** Function name:           DrawBox
** Description:             draw a filled rectangle
***************************************************************************************/
void DrawBox(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)
{
#ifdef CLIP_CHECK
  if ((x > tft_width) || (y > tft_height) || (w == 0) || (h == 0)) return;
  if ((x + w - 1) > tft_width)  w = tft_width  - x;
  if ((y + h - 1) > tft_height) h = tft_height - y;
#endif

  spi_begin();
  tft_setAddrWindow(x, y, x + w - 1, y + h - 1);

  while (h--) write16(color, w);
  digitalWrite(tft_CS, HIGH);

  spi_end();
}

/***************************************************************************************
** Function name:           DrawRoundRect
** Description:             draw a filled rectangle with round corners
***************************************************************************************/
void DrawRoundRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t rad, uint16_t color) {
  DrawBox(x, y + rad + 1, w, h - rad * 2 - 2, color);
  DrawDisc(x + rad, y + rad, rad, color);
  DrawDisc(x + w - rad - 1, y + rad, rad, color);
  DrawDisc(x + rad, y + h - rad - 1, rad, color);
  DrawDisc(x + w - rad - 1, y + h - rad - 1, rad, color);
  DrawBox(x + rad + 1, y, w - rad * 2 - 2, rad * 2, color);
  DrawBox(x + rad + 1, y + h - rad * 2, w - rad * 2 - 2, rad * 2, color);
}

/***************************************************************************************
** Function name:           invertDisplay
** Description:             invert the display colours i = 1 invert, i = 0 normal
***************************************************************************************/
void InvertDisplay(bool i)
{
  spi_begin();
  // Send the command twice as otherwise it does not always work!
  tft_writecommand(i ? ILI9341_INVON : ILI9341_INVOFF);
  tft_writecommand(i ? ILI9341_INVON : ILI9341_INVOFF);
  spi_end();
}

/***************************************************************************************
** Function name:           drawFloat
** Descriptions:            drawFloat, prints 7 non zero digits maximum
***************************************************************************************/
void DrawFloat(float floatNumber, int dp, const char *Font, uint16_t color)
{
  char str[14];               // Array to contain decimal string
  uint8_t ptr = 0;            // Initialise pointer for array
  int8_t  digits = 1;         // Count the digits to avoid array overflow
  float rounding = 0.5;       // Round up down delta

  if (dp > 7) dp = 7; // Limit the size of decimal portion

  // Adjust the rounding value
  for (uint8_t i = 0; i < dp; ++i) rounding /= 10.0;

  if (floatNumber < -rounding)    // add sign, avoid adding - sign to 0.0!
  {
    str[ptr++] = '-'; // Negative number
    str[ptr] = 0; // Put a nullptr in the array as a precaution
    digits = 0;   // Set digits to 0 to compensate so pointer value can be used later
    floatNumber = -floatNumber; // Make positive
  }

  floatNumber += rounding; // Round up or down

  // For error put ... in string and return (all TFT_ILI9341 library fonts contain . character)
  if (floatNumber >= 2147483647) {
    strcpy(str, "...");
    DrawString(str, Font, color);
    return;
  }
  // No chance of overflow from here on

  // Get integer part
  unsigned long temp = (unsigned long)floatNumber;

  // Put integer part into array
  ltoa(temp, str + ptr, 10);

  // Find out where the nullptr is to get the digit count loaded
  while ((uint8_t)str[ptr] != 0) ptr++; // Move the pointer along
  digits += ptr;                  // Count the digits

  str[ptr++] = '.'; // Add decimal point
  str[ptr] = '0';   // Add a dummy zero
  str[ptr + 1] = 0; // Add a nullptr but don't increment pointer so it can be overwritten

  // Get the decimal portion
  floatNumber = floatNumber - temp;

  // Get decimal digits one by one and put in array
  // Limit digit count so we don't get a false sense of resolution
  uint8_t i = 0;
  while ((i < dp) && (digits < 9)) // while (i < dp) for no limit but array size must be increased
  {
    i++;
    floatNumber *= 10;       // for the next decimal
    temp = floatNumber;      // get the decimal
    ltoa(temp, str + ptr, 10);
    ptr++; digits++;         // Increment pointer and digits count
    floatNumber -= temp;     // Remove that digit
  }

  // Finally we can plot the string and return pixel length
  DrawString(str, Font, color);
}

///***************************************************************************************
//** Function name:           spiWrite16
//** Descriptions:            Delay based assembler loop for fast SPI write
//***************************************************************************************/
//static inline void spiWrite16(uint16_t data, int16_t count)
//{
//// We can enter this loop with 0 pixels to draw, so we need to check this
//  if (ILI9341fast) {
//    uint8_t temp;
//    asm volatile
//    (
//      "  sbiw  %[count],0\n"   // test count
//      //" brmi  2f\n"     // if < 0 then done
//      " breq  2f\n"     // if == 0 then done
//
//      "1: out %[spi],%[hi]\n"   // write SPI data
//      " rcall 3f      \n" // 7
//      " rcall 3f      \n" // 14
//      " rjmp  4f      \n" // 16
//      "3: ret     \n" //
//      "4: nop     \n" // 17
//
//      " out %[spi],%[lo]\n"   // write SPI data
//
//      " adiw  r24,0   \n" // 2
//      " adiw  r24,0  \n"  // 4
//      " rcall 5f     \n"  // 11
//      " rjmp  6f     \n"  // 13
//      "5: ret    \n"  //
//      "6:        \n"
//
//      " sbiw  %[count],1 \n" // 15 decrement count
//      " brne  1b         \n" // 17 if != 0 then loop
//
//      "2:\n"
//
//      : [temp] "=d" (temp), [count] "+w" (count)
//      : [spi] "i" (_SFR_IO_ADDR(SPDR)), [lo] "r" ((uint8_t)data), [hi] "r" ((uint8_t)(data>>8))
//      :
//    );
//  } else {
//    for (;count > 0; count--)
//      SPI.transfer16(data);
//  }
//}
//
///***************************************************************************************
//** Function name:           spiWait
//** Descriptions:            17 cycle delay
//***************************************************************************************/
//static inline void spiWait17(void)
//{
//  asm volatile
//  (
//    " rcall 1f    \n" // 7
//    " rcall 1f    \n" // 14
//    " rjmp  2f    \n" // 16
//    "1: ret   \n" //
//    "2: nop  \n" // 17
//  );
//}
//
///***************************************************************************************
//** Function name:           spiWait
//** Descriptions:            15 cycle delay
//***************************************************************************************/
//static inline void spiWait15(void)
//{
//  asm volatile
//  (
//    " adiw  r24,0  \n"  // 2
//    " adiw  r24,0  \n"  // 4
//    " adiw  r24,0  \n"  // 6
//    " rcall 1f     \n"  // 13
//    " rjmp  2f     \n"  // 15
//    "1: ret    \n"  //
//    "2:        \n"  //
//  );
//}
//
///***************************************************************************************
//** Function name:           spiWait
//** Descriptions:            14 cycle delay
//***************************************************************************************/
//static inline void spiWait14(void)
//{
//  asm volatile
//  (
//    " nop         \n" // 1
//    " adiw  r24,0  \n"  // 3
//    " adiw  r24,0  \n"  // 5
//    " rcall 1f     \n"  // 12
//    " rjmp  2f     \n"  // 14
//    "1: ret    \n"  //
//    "2:        \n"  //
//  );
//}
//
///***************************************************************************************
//** Function name:           spiWait
//** Descriptions:            12 cycle delay
//***************************************************************************************/
//static inline void spiWait12(void)
//{
//  asm volatile
//  (
//    " nop         \n" // 1
//    " adiw  r24,0  \n"  // 3
//    " rcall 1f     \n"  // 10
//    " rjmp  2f     \n"  // 12
//    "1: ret    \n"  //
//    "2:        \n"  //
//  );
//}

/***************************************************************************************
** Function name:           ILI9341Begin
** Descriptions:            initialise ILI9341
***************************************************************************************/
void ILI9341Begin(uint8_t CS, uint8_t CD, uint8_t RST, uint16_t w, uint16_t h, uint8_t Rotation) {

  tft_CS   = CS;
  tft_DC   = CD;
  tft_RST  = RST;

  SPI.begin();

  if (tft_RST > 0) {
    digitalWrite(tft_RST, LOW);
    pinMode(tft_RST, OUTPUT);
  }

  digitalWrite(tft_DC, HIGH);
  pinMode(tft_DC, OUTPUT);

  digitalWrite(tft_CS, HIGH);
  pinMode(tft_CS, OUTPUT);

  tft_width    = w;
  tft_height   = h;
  Cursory  = Cursorx    = 0;

  addr_row = 0xFFFF;
  addr_col = 0xFFFF;
  win_xe = 0xFFFF;
  win_ye = 0xFFFF;

  SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz (full! speed!)
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  //  mySPCR = SPCR;

  // toggle RST low to reset
  if (tft_RST > 0) {
    digitalWrite(tft_RST, HIGH);
    delay(5);
    digitalWrite(tft_RST, LOW);
    delay(20);
    digitalWrite(tft_RST, HIGH);
    delay(150);
  }

  if (tft_RST <= 0) {
    spi_begin();

    tft_writecommand(ILI9341_SWRESET);
    delay(10);
    spi_end();
  };

  // Initialization commands for ILI9341 screens
  static const uint8_t ILI9341_cmds[] =
  {
    21,               /* num commands             */
    0xEF,             /*                          */ 3, 0x03, 0x80, 0x02,
    0xCF,             /*                          */ 3, 0x00, 0xC1, 0x30,
    0xED,             /*                          */ 4, 0x64, 0x03, 0x12, 0x81,
    0xE8,             /*                          */ 3, 0x85, 0x00, 0x78,
    0xCB,             /*                          */ 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
    0xF7,             /*                          */ 1, 0x20,
    0xEA,             /*                          */ 2, 0x00, 0x00,
    ILI9341_PWCTR1,   /* power control            */ 1, 0x23,                           // VRH[5:0]
    ILI9341_PWCTR2,   /* power control           */ 1, 0x10,                           // SAP[2:0];BT[3:0]
    ILI9341_VMCTR1,   /* VCM control             */ 2, 0x3e, 0x28,
    ILI9341_VMCTR2,   /* VCM control2            */ 1, 0x86,                           // --
    ILI9341_MADCTL,   /*                         */ 1, (ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR),
    ILI9341_PIXFMT,   /*                         */ 1, 0x55,
    ILI9341_FRMCTR1,  /*                         */ 2, 0x00, 0x18,
    ILI9341_DFUNCTR,  /*                         */ 3, 0x08, 0x82, 0x27,
    0xF2,             /* 3Gamma Function Disable */ 1, 0x00,
    ILI9341_GAMMASET, /* Gamma curve selected    */ 1, 0x01,
    ILI9341_GMCTRP1,  /* Set Gamma               */ 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
    ILI9341_GMCTRN1,  /*                         */ 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
    ILI9341_SLPOUT,   /*                         */ ILI9341_INIT_DELAY, 120,
    ILI9341_DISPON,   /*                         */ 0,
  };

  tft_commandList(ILI9341_cmds);


  spi_begin();
  tft_writecommand(ILI9341_MADCTL);
  tft_writedata(Rotation);
  spi_end();

}

void ILI9341SetCursor(uint16_t x, uint16_t y) {
  tft_setCursor(x, y);
}

void ClearDisplay(uint16_t color) {
  DrawBox(0, 0, tft_width, tft_height, color);
}

void DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
  int tmp, x, y, dx, dy, err, ystep, swapxy;
  swapxy = 0;

  if (x1 > x2) dx = x1 - x2;
  else dx = x2 - x1;
  if (y1 > y2) dy = y1 - y2;
  else dy = y2 - y1;

  if (dy > dx) {
    swapxy = 1;
    tmp = dx;
    dx = dy;
    dy = tmp;
    tmp = x1;
    x1 = y1;
    y1 = tmp;
    tmp = x2;
    x2 = y2;
    y2 = tmp;
  }
  if ((x1 > x2)) {
    tmp = x1;
    x1 = x2;
    x2 = tmp;
    tmp = y1;
    y1 = y2;
    y2 = tmp;
  }
  err = dx >> 1;
  if ((y2 > y1)) ystep = 1;
  else ystep = -1;
  y = y1;
  for (x = x1; x <= x2; x++) {
    if (pen_width > 1) {
      if ((swapxy == 0))
        DrawDisc(x, y, pen_width - 1, color);
      else
        DrawDisc(y, x, pen_width - 1, color);
    } else {
      if ((swapxy == 0)) {
        DrawPixel(x, y, color);
      } else {
        DrawPixel(y, x, color);
      }
    }

    err -= dy;
    if (err < 0) {
      y += ystep;
      err += dx;
    }
  }
}

void DrawFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
  DrawHLine(x, y, w, color);
  DrawHLine(x, y + h - 1, w, color);
  DrawVLine(x, y, h, color);
  DrawVLine(x + w - 1, y, h, color);
}

void DrawCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color) {
  DrawEllipse(x0, y0, rad, rad, color);
}

void DrawDisc(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color) {
  DrawFilledEllipse(x0, y0, rad, rad, color);
}

void DrawFilledEllipse(int x0, int y0, int rx, int ry, uint16_t color) {
  // the overall width is rad*2+1
  if (rx < 1) return;
  if (ry < 1) return;
  int16_t x, y;
  int32_t rx2 = rx * rx;
  int32_t ry2 = ry * ry;
  int32_t fx2 = 4 * rx2;
  int32_t fy2 = 4 * ry2;
  int32_t s;

  for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++)
  {
    DrawHLineSingle(x0 - x, y0 - y, x + x + 1, color);
    DrawHLineSingle(x0 - x, y0 + y, x + x + 1, color);

    if (s >= 0)
    {
      s += fx2 * (1 - y);
      y--;
    }
    s += ry2 * ((4 * x) + 6);
  }

  for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++)
  {
    DrawHLineSingle(x0 - x, y0 - y, x + x + 1, color);
    DrawHLineSingle(x0 - x, y0 + y, x + x + 1, color);

    if (s >= 0)
    {
      s += fy2 * (1 - x);
      x--;
    }
    s += rx2 * ((4 * y) + 6);
  }
}

void DrawEllipse(int x0, int y0, int rx, int ry, uint16_t color) {
  if (rx < 2) return;
  if (ry < 2) return;
  int16_t x, y;
  int32_t rx2 = rx * rx;
  int32_t ry2 = ry * ry;
  int32_t fx2 = 4 * rx2;
  int32_t fy2 = 4 * ry2;
  int32_t s;

  //  tft_fastSetup();

  for (x = 0, y = ry, s = 2 * ry2 + rx2 * (1 - 2 * ry); ry2 * x <= rx2 * y; x++)
  {
    tft_fastPixel(x0 + x, y0 + y, color);
    tft_fastPixel(x0 - x, y0 + y, color);
    tft_fastPixel(x0 - x, y0 - y, color);
    tft_fastPixel(x0 + x, y0 - y, color);
    if (s >= 0)
    {
      s += fx2 * (1 - y);
      y--;
    }
    s += ry2 * ((4 * x) + 6);
  }

  for (x = rx, y = 0, s = 2 * rx2 + ry2 * (1 - 2 * rx); rx2 * y <= ry2 * x; y++)
  {
    tft_fastPixel(x0 + x, y0 + y, color);
    tft_fastPixel(x0 - x, y0 + y, color);
    tft_fastPixel(x0 - x, y0 - y, color);
    tft_fastPixel(x0 + x, y0 - y, color);
    if (s >= 0)
    {
      s += fy2 * (1 - x);
      x--;
    }
    s += rx2 * ((4 * y) + 6);
  }
}

void swapu(uint16_t *a, uint16_t *b) {
  uint16_t c;
  c = *a;
  *a = *b;
  *b = c;
}

void swapi(int16_t *a, int16_t *b) {
  int16_t c;
  c = *a;
  *a = *b;
  *b = c;
}

void DrawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
  int16_t a, b, y, last;

  // Sort coordinates by Y order (y2 >= y1 >= y0)
  if (y0 > y1) {
    swapu(&y0, &y1); swapu(&x0, &x1);
  }
  if (y1 > y2) {
    swapu(&y2, &y1); swapu(&x2, &x1);
  }
  if (y0 > y1) {
    swapu(&y0, &y1); swapu(&x0, &x1);
  }

  if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing
    a = b = x0;
    if (x1 < a)      a = x1;
    else if (x1 > b) b = x1;
    if (x2 < a)      a = x2;
    else if (x2 > b) b = x2;
    DrawHLineSingle(a, y0, b - a + 1, color);
    return;
  }

  int16_t
  dx01 = x1 - x0,
  dy01 = y1 - y0,
  dx02 = x2 - x0,
  dy02 = y2 - y0,
  dx12 = x2 - x1,
  dy12 = y2 - y1,
  sa   = 0,
  sb   = 0;

  // For upper part of triangle, find scanline crossings for segments
  // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
  // is included here (and second loop will be skipped, avoiding a /0
  // error there), otherwise scanline y1 is skipped here and handled
  // in the second loop...which also avoids a /0 error here if y0=y1
  // (flat-topped triangle).
  if (y1 == y2) last = y1;  // Include y1 scanline
  else         last = y1 - 1; // Skip it

  for (y = y0; y <= last; y++) {
    a   = x0 + sa / dy01;
    b   = x0 + sb / dy02;
    sa += dx01;
    sb += dx02;

    if (a > b) swapi(&a, &b);
    DrawHLineSingle(a, y, b - a + 1, color);
  }

  // For lower part of triangle, find scanline crossings for segments
  // 0-2 and 1-2.  This loop is skipped if y1=y2.
  sa = dx12 * (y - y1);
  sb = dx02 * (y - y0);
  for (; y <= y2; y++) {
    a   = x1 + sa / dy12;
    b   = x0 + sb / dy02;
    sa += dx12;
    sb += dx02;

    if (a > b) swapi(&a, &b);
    DrawHLineSingle(a, y, b - a + 1, color);
  }
}

void DrawChar(uint8_t c, const char *Font, uint16_t color) {
  uint16_t n, j, px;
  byte ymax, desc;
  unsigned long b, d;

  ymax = *Font;
  Font++;
  desc = *Font;
  Font++;

  j = *Font; // first char
  Font++;
  if (c < j) return;

  while (c > j) {
    b = *Font;
    if (b == 0)
      return;
    if (ymax > 15)
      Font += b * 3 + 1;
    else if (ymax > 7)
      Font += b * 2 + 1;
    else
      Font += b + 1;
    c--;
  }
  px = Cursorx;
  n = *Font;
  Font++;
  while (n > 0) {
    b = *Font;
    b &= 0xFF;
    Font++;
    if (ymax > 7) {
      d = *Font;
      d &= 0xFF;
      b |= d << 8;
      Font++;
    }
    if (ymax > 15) {
      d = *Font;
      d &= 0xFF;
      b |= d << 16;
      Font++;
    }
    if (execDrawChar) {
      for (j = 0; j <= ymax; j++) {
        if (b & 1)
          DrawPixel(Cursorx, Cursory + desc - j, color);
        b = b >> 1;
      }
    }
    Cursorx++;
    n--;
  }
  Cursorx += letter_gap;
}

void DrawString(char *s, const char *Font, uint16_t color) {
  for (char *t = s; * t; t++) {
    DrawChar( * t, Font, color);
  }
}

void DrawStringAt(int16_t x, int16_t y, char *s, const char *Font, uint16_t color) {
  ILI9341SetCursor(x, y);
  DrawString(s, Font, color);
}

void DrawInt(int i, const char *Font, uint16_t color) {
  if (i < 0) {
    i = -i;
    DrawChar('-', Font, color);
  }

  bool HasDigit = false;
  int n =  10000;
  if (i == 0) {
    DrawChar('0', Font, color);
  } else {
    while (n > 0) {
      if ((i >= n) or HasDigit) {
        DrawChar('0' + (i / n), Font, color);
        HasDigit = true;
      }
      i %= n;
      n /= 10;
    }
  }
}

/***************************************************************************************
** Function name:           TextWidth
** Description:             width of text
***************************************************************************************/
int TextWidth(char *s, const char *Font)
{
  int result, x;
  execDrawChar = false;
  x = Cursorx;
  Cursorx = 0;
  DrawString(s, Font, TFT_WHITE);
  result = Cursorx;
  Cursorx = x;
  execDrawChar = true;
  return result;
}

/***************************************************************************************
** Function name:           TextHeight
** Description:             height of font
***************************************************************************************/
int TextHeight(const char *Font)
{
  return *Font;
}

/***************************************************************************************
** Function name:           rgb
** Description:             convert three 8 bit RGB levels to a 16 bit colour value
***************************************************************************************/
uint16_t rgb(uint8_t r, uint8_t g, uint8_t b)
{
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}


/***************************************************************************************
** Function name:           cmp_swap
** Description:             if a > b then swap a and b
***************************************************************************************/
static inline void cmp_swap(int16_t *a, int16_t *b)
{
  if (*a > *b) {
    int16_t c = *a;
    *a = *b;
    *b = c;
  }
}

/***************************************************************************************
** Function name:           median_filter
** Description:             return median value of five
***************************************************************************************/
static int median_filter(int16_t t[]) {
  cmp_swap(&t[0], &t[4]);
  cmp_swap(&t[2], &t[4]);
  cmp_swap(&t[1], &t[3]);
  cmp_swap(&t[0], &t[2]);
  cmp_swap(&t[3], &t[4]);
  cmp_swap(&t[1], &t[2]);
  cmp_swap(&t[0], &t[3]);
  cmp_swap(&t[2], &t[3]);

  return t[2];
}

/***************************************************************************************
** Function name:           GetTouch
** Description:             get touch coordinates
**                          return true if touched
***************************************************************************************/
bool GetTouch(int *x, int *y) {
  int16_t z, z1, z2, ax[5], ay[5];

  //SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));

  digitalWrite(touch_CS, LOW);
  delayMicroseconds(100);
  //  z = SPI.transfer(0xB1);
  //  z1 = SPI.transfer16(0xC1) >> 3;
  //  z2 = SPI.transfer16(0x91) >> 3;
  z = SPI.transfer16(0xB4) >> 3;
  z1 = SPI.transfer16(0xB4) >> 3;
  z2 = SPI.transfer16(0xC4) >> 3;

  SPI.transfer16(0x91);  // switch on drivers
  ax[0] = SPI.transfer16(0x91) >> 3;
  ax[1] = SPI.transfer16(0x91) >> 3;
  ax[2] = SPI.transfer16(0x91) >> 3;
  ax[3] = SPI.transfer16(0x91) >> 3;
  ax[4] = SPI.transfer16(0x91) >> 3;

  SPI.transfer16(0xD1);  // switch on drivers
  ay[0] = SPI.transfer16(0xD1) >> 3;
  ay[1] = SPI.transfer16(0xD1) >> 3;
  ay[2] = SPI.transfer16(0xD1) >> 3;
  ay[3] = SPI.transfer16(0xD1) >> 3;
  ay[4] = SPI.transfer16(0xD1) >> 3;

  //  data[4] = SPI.transfer16(0xD0) >> 3;  // Last Y touch power down
  //  data[5] = SPI.transfer16(0) >> 3;
  SPI.transfer16(0);  // power down
  digitalWrite(touch_CS, HIGH);
  delayMicroseconds(100);
  SPI.endTransaction();
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  SPI.endTransaction();

  if (z1 + z2 > 0x80) {
    switch (touch_Rotation) {
      case 1:
        *x = round(long(touch_xmax - median_filter(ax)) * tft_width / long(touch_xmax - touch_xmin));
        *y = round(long(touch_ymax - median_filter(ay)) * tft_height / long(touch_ymax - touch_ymin));
        break;
      case 2:
        *x = round(long(touch_xmax - median_filter(ay)) * tft_width / long(touch_xmax - touch_xmin));
        *y = tft_height - round(long(touch_ymax - median_filter(ax)) * tft_height / long(touch_ymax - touch_ymin));
        break;
      case 3:
        *x = round(long(median_filter(ax) - touch_xmin) * tft_width / long(touch_xmax - touch_xmin));
        *y = round(long(median_filter(ay) - touch_ymin) * tft_height / long(touch_ymax - touch_ymin));
        break;
      default:
        *x = round(long(median_filter(ay) - touch_xmin) * tft_width / long(touch_xmax - touch_xmin));
        *y = tft_height - round(long(median_filter(ax) - touch_ymin) * tft_height / long(touch_ymax - touch_ymin));
        break;
    }
    return true;
  } else {
    *x = 0;
    *y = 0;
    return false;
  }
}

/***************************************************************************************
** Function name:           BeginTouch
** Descriptions:            initialise ILI9341
***************************************************************************************/
void BeginTouch(uint8_t CS, uint8_t Rotation, int xmin, int ymin, int xmax, int ymax) {
  int x, y;

  touch_xmin = xmin;
  touch_xmax = xmax;
  touch_ymin = ymin;
  touch_ymax = ymax;
  touch_Rotation = Rotation % 4;
  touch_CS = CS;

  if (touch_CS > 0) {
    pinMode(touch_CS, OUTPUT);
    digitalWrite(touch_CS, LOW);
  }

  GetTouch(&x, &y);
}
