Source file: /~heha/Mikrocontroller/Displays/utft/Kalender.zip/ILI9341/UTFT.cpp

#include "UTFT.h"

#include <util/delay.h>
#include <stdlib.h>

static void myseek(FILE*f,int dist) {
 if (f->flags&__SSTR) {	// mine! Do fast access
  f->len+=(int)dist;
 }//else fseek(f,dist,SEEK_CUR);
}
static word mygetw(FILE*f) {
 word w;
 if (f->flags&__SSTR) {	// mine! Do fast access
  word*p=(word*)f->len;
  if (f->flags&__SPGM) w=pgm_read_word(p);
  else w=*p;
  ++p;
  f->len=(int)p;
 }else{			// not mine! Use function pointer
  w=fgetc(f)&0xFF;
  w|=fgetc(f)<<8;
 }
 return w;
}

/********************
 * Basic LCD access *
 ********************/

void UTFT::writeB(byte b) {
#if DISP_TRANSFER==16
 writeW(b);		// always 16-bit
#elif DISP_TRANSFER==1
 for (byte m=0x80; m; m>>=1) {
  if (VL & 0x80) DISP_SDA(1); else DISP_SDA(0);
  DISP_SCL(0);
  DISP_SCL(1);
 }
#else
 DISP_OUT(b);
 DISP_WR(0);
 DISP_WR(1);
#endif
}

void UTFT::writeW(word w) {
#if DISP_TRANSFER<16
 writeB(w>>8);
 writeB(w);
#elif defined(DISP_ALE)
 DISP_OUT(w>>8);
 DISP_ALE(1);
 DISP_ALE(0);
 DISP_OUT(w);
 DISP_WR(0);
 DISP_WR(1);
#else
 DISP_OUT(w);
 DISP_WR(0);
 DISP_WR(1);
#endif
}

void UTFT::fillW(word w, int r1, int r2) {
#ifdef __MPS430__	// 16-bit optimized loops
 if (r1<r2) SWAP(r1,r2);	// ensure more inner loop executions
 if (!r2) return;
#define DO do {int r=r1; do	// two nested loops
#define WHILE while (--r);} while(--r2)
#else			// 8-bit optimized loops
 unsigned long rep=(unsigned long)(word)r1*(word)r2;
 if (!rep) return;
 byte rep0=byte(rep);
 byte rep1=byte(rep>>8);
 byte rep2=byte(rep>>16);	// 24 bits are sufficient here
 if (rep0) rep1++;
 if (rep1) rep2++;
#define DO do do do	// three nested loops
#define WHILE while (--rep0); while(--rep1); while(--rep2)
#endif
#if DISP_TRANSFER==16
 DISP_OUT(w);
 DO{
  DISP_WR(0);
  DISP_WR(1);
 }WHILE;
#elif DISP_TRANSFER==8
 if (w>>8==(w&0xFF)) {
  DISP_OUT(w);
  DO{
   DISP_WR(0);
   DISP_WR(1);
   DISP_WR(0);
   DISP_WR(1);
  }WHILE;
 }else{
  DO{
   DISP_OUT(w>>8);
   DISP_WR(0);
   DISP_WR(1);
   DISP_OUT(w);
   DISP_WR(0);
   DISP_WR(1);
  }WHILE;
 }
#else
 DO setPixel(w); WHILE;
#endif
#undef DO
#undef WHILE
}

void UTFT::writeCW(byte b,word w) {
 writeC(b);
#ifdef DISP_CMD16
 writeW(w);
#else
 writeB(w>>8);
 writeB(w);
#endif
}

void UTFT::writeCWW(byte b,word x,word y) {
 writeC(b);
#ifdef DISP_CMD16
 writeW(x);
 writeW(y);
#else
 writeB(x>>8);
 writeB(x);
 writeB(y>>8);
 writeB(y);
#endif
}

void UTFT::writeCBList(const byte*z) {
 for(byte len;len=pgm_read_byte(z++);) {
  UTFT::writeC(pgm_read_byte(z++));
  while (--len) UTFT::writeB(pgm_read_byte(z++));
 };
}

/*************************************
 * Hardware-dependent initialization *
 *************************************/
void UTFT::InitLCD(byte orientation) {
 orient=orientation;
 flags=0;
 align=0;
 clrClip();
 clrOrg();
 //clrScale();
 DISP_INIT();
#ifdef DISP_DDR
 DISP_DDR(1);	// data port is output port
#endif
#ifdef DISP_RST
 DISP_RST(1);
 _delay_ms(5); 
 DISP_RST(0);
 _delay_ms(15);
 DISP_RST(1);
 _delay_ms(15);
#endif
 DISP_CS(0);
#ifdef DISP_ILI9325D
 static const struct {byte a;word b;} pairs[] PROGMEM={
  {0xE5,0x78F0},	// set SRAM internal timing
  {0x01,0},	// set Driver Output Control, bit 8 reflects X counting
  {0x02,0x0200},	// set 1 line inversion  
  {0x03,0x1030},
  {0x04,0},	// Resize register  
//  {0x08,0x0207},	// set the back porch and front porch  
//  {0x09,0},	// set non-display area refresh cycle ISC[3:0]  
//  {0x0A,0},	// FMARK function  
//  {0x0C,0},	// RGB interface setting  
//  {0x0D,0},	// Frame marker Position  
//  {0x0F,0},	// RGB interface polarity  
	//*************Power On sequence ****************//  
  {0x10,0},	// SAP, BT[3:0], AP, DSTB, SLP, STB  
  {0x11,0x0007}, // DC1[2:0], DC0[2:0], VC[2:0]  
  {0x12,0},	// VREG1OUT voltage  
  {0x13,0},	// VDV[4:0] for VCOM amplitude  
  {0x07,0x0001},
  {0xFF,200},	// Dis-charge capacitor power voltage  
  {0x10,0x1690},	// SAP, BT[3:0], AP, DSTB, SLP, STB  
  {0x11,0x0227},	// Set DC1[2:0], DC0[2:0], VC[2:0]  
  {0xFF,50},
  {0x12,0x000D},
  {0xFF,50},
  {0x13,0x1200}, // VDV[4:0] for VCOM amplitude  
  {0x29,0x000A}, // 04  VCM[5:0] for VCOMH  
//  {0x2B,0x000D}, // Set Frame Rate  
  {0xFF,50},
	// ----------- Adjust the Gamma Curve ----------//  
  {0x30,0},
  {0x31,0x0700},
  {0x32,0x0707},
  {0x35,0},
  {0x36,0x000F},
  {0x37,0x0707},
  {0x38,0x0007},
  {0x39,0},
  {0x3C,0},
  {0x3D,0x0F00},
	//------------------ Set GRAM area ---------------//
  {0xFE,0},
  {0x60,0x2700},	// Gate Scan Line, bit 15 reflects Y counting
  {0x61,0x0003}, // NDL,VLE, REV   
//  {0x6A,0},	// set scrolling line  
	//-------------- Partial Display Control ---------//  
//  {0x80,0},
//  {0x81,0},
//  {0x82,0},
//  {0x83,0},
//  {0x84,0},
//  {0x85,0},
	//-------------- Panel Control -------------------//  
  {0x90,0x0010},  
  {0x92,0},
  {0x07,0x0133}}; // 262K color and display ON

 for (char*z=(char*)pairs; z<(char*)pairs+sizeof pairs;) {
  byte a=pgm_read_byte(z++);
  word b=pgm_read_word(z); z+=2;
  switch (a) {
   case 1: b|=orient<<7&0x0100; break;	//bit 8 reflects X counting
   case 0xFF: _delay_ms(200); continue;
   case 0xFE: unsetWindow(); continue;
   case 0x60: b|=orient<<13&0x8000; break;	// bit 15 reflects Y counting
  }
  writeCW(a,b);
 }
#elif defined(DISP_ILI9341)
 PROGMEM static const byte data_init[]={	// Kette von Pascal-Strings
   6,0xCB,0x39,0x2C,0x00,0x34,0x02,
   4,0xCF,0x00,0XC1,0X30,
   4,0xE8,0x85,0x00,0x78,
   3,0xEA,0x00,0x00,
   5,0xED,0x64,0x03,0X12,0X81,
   2,0xF7,0x20,
   2,0xC0,0x23,		//VRH[5:0] 	//Power control 
   2,0xC1,0x10,		//SAP[2:0];BT[3:0]
   3,0xC5,0x3e,0x28,	//Contrast	//VCM control
   2,0xC7,0x86,		//--
   2,0x3A,0x55,		// RGB16
   3,0xB1,0x00,0x18,	// DIVA[1:0]=0	// RTNA[4:0]=24 = 79 Hz	// Frame Control
   5,0xB6,0x0A,0x82,0x27,0x00,	// PTG = Interval Scan, PT = Source Output: GND	// Display Function Control 
   2,0xF2,0x00,	// 3Gamma Function Disable 
   2,0x26,0x01,	//Gamma curve selected 
  16,0xE0,0x0F,0x31,0x2B,0x0C,0x0E,0x08,0x4E,0xF1,0x37,0x07,0x10,0x03,
     0x0E,0x09,0x00,	//Set Gamma 
  16,0xE1,0x00,0x0E,0x14,0x03,0x11,0x07,0x31,0xC1,0x48,0x08,0x0F,0x0C,
     0x31,0x36,0x0F,
   1,0x11,		//Exit Sleep
   0};
 writeCBList(data_init);
 _delay_ms(120); 
 PROGMEM static const byte data_dispon[]={
    1,0x29,		//Display on
    2,0x36,0x48,	// Memory Access Control	// MY=0, MX=0, MV=0, ML=0, BGR=1, MH=0, -=0, -=0
    0};
 writeCBList(data_dispon);
//    writeC(0x2c); 
#elif defined(DISP_SPFD5408)	// September 2016

#define ILI9341_SOFTRESET          0x01
#define ILI9341_SLEEPIN            0x10
#define ILI9341_SLEEPOUT           0x11
#define ILI9341_NORMALDISP         0x13
#define ILI9341_INVERTOFF          0x20
#define ILI9341_INVERTON           0x21
#define ILI9341_GAMMASET           0x26
#define ILI9341_DISPLAYOFF         0x28
#define ILI9341_DISPLAYON          0x29
#define ILI9341_COLADDRSET         0x2A
#define ILI9341_PAGEADDRSET        0x2B
#define ILI9341_MEMORYWRITE        0x2C
#define ILI9341_PIXELFORMAT        0x3A
#define ILI9341_FRAMECONTROL       0xB1
#define ILI9341_DISPLAYFUNC        0xB6
#define ILI9341_ENTRYMODE          0xB7
#define ILI9341_POWERCONTROL1      0xC0
#define ILI9341_POWERCONTROL2      0xC1
#define ILI9341_VCOMCONTROL1      0xC5
#define ILI9341_VCOMCONTROL2      0xC7
#define ILI9341_MEMCONTROL      0x36
#define ILI9341_MADCTL  0x36

#define ILI9341_MADCTL_MY  0x80
#define ILI9341_MADCTL_MX  0x40
#define ILI9341_MADCTL_MV  0x20
#define ILI9341_MADCTL_ML  0x10
#define ILI9341_MADCTL_RGB 0x00
#define ILI9341_MADCTL_BGR 0x08
#define ILI9341_MADCTL_MH  0x04 

#define writeRegister8(a,b) writeC(a);writeB(b)
#define writeRegister16(a,b) writeC(a);writeB((b)>>8);writeB((b)&0xFF)

    writeRegister8(0x01, 0);
    _delay_ms(50);
    writeRegister8(0x28, 0);

    writeRegister8(0xC0, 0x23);
    writeRegister8(0xC1, 0x10);
    writeRegister16(0xC5, 0x2B2B);
    writeRegister8(0xC7, 0xC0);
    writeRegister8(0x36, 0x88);
    writeRegister8(0x3A, 0x55);
    writeRegister16(0xB1, 0x001B);
    
    writeRegister8(0xB7, 0x07);
    /* writeRegister32(0xB6, 0x0A822700);*/

    writeRegister8(0x11, 0);
    _delay_ms(150);
    writeRegister8(0x29, 0);
    _delay_ms(500);
	// *** SPFD5408 change -- Begin
	// Not tested yet
	//writeRegister8(0x20, 0);
	//delay(500);
    // *** SPFD5408 change -- End
//    setAddrWindow(0, 0, 240-1, 320-1); 
#elif defined(DISP_S6D04H0)
// PowerOn
 _delay_ms(50);
// Init
 PROGMEM static const byte data_init[]={	// Kette von Pascal-Strings
    3,0xF0,0x5A,0x5A,
    3,0xFC,0x5A,0x5A,
   12,0xFD,0x00,0x00,0x10,0x14,0x12,0x00,0x04,0x48,0x40,0x16,0x16,
    1,0x35,
    2,0x36,0x48,
    2,0x3A,0x55,
   18,0xF2,0x28,0x5B,0x7F,0x08,0x08,0x00,0x00,0x15,0x48,0x04,0x07,0x01,
      0x00,0x00,0x63,0x08,0x08,
    5,0xF7,0x01,0x00,0x10,0x00,
    4,0xF8,0x33,0x00,0x00,
    0};
 writeCBList(data_init);
 PROGMEM static const byte data_power[]={
   10,0xF6,0x01,0x01,0x07,0x00,0x01,0x0C,0x03,0x0C,0x03,
   13,0xF5,0x00,0x2E,0x40,0x00,0x00,0x01,0x00,0x00,0x0D,0x0D,0x00,0x00,
   21,0xF4,0x07,0x00,0x00,0x00,0x22,0x64,0x01,0x02,0x2A,0x4D,0x06,0x2A,
      0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
    2,0xF3,0x01,
    0};
 writeCBList(data_power);
 PROGMEM static const byte data_gamma[]={
   13,0xFA,0x0A,0x04,0x0C,0x19,0x25,0x33,0x2D,0x27,0x22,0x1E,0x1A,0x00,
   13,0xFB,0x0C,0x04,0x19,0x1E,0x20,0x23,0x18,0x3D,0x25,0x19,0x0B,0x00,
   0};
 for (byte i=4; i; i>>=1) {
  writeC(0xF9); writeB(i);	// Gamma Red-green-blue
  writeCBList(data_gamma);
 }
 writeC(0x11);
 _delay_ms(120);
 PROGMEM static const byte data_dispon[]={
    3,0xF0,0xA5,0xA5,
    3,0xFC,0xA5,0xA5,
    1,0x29,
    0};
 writeCBList(data_dispon);
#elif defined(DISP_ILI9481)
 writeC(0x11);
 _delay_ms(20);
 PROGMEM static const byte data_init[]={ 
   4,0xD0,0x07,0x42,0x18,
   4,0xD1,0x00,0x07,0x10,
   3,0xD2,0x01,0x02,
   6,0xC0,0x10,0x3B,0x00,0x02,0x11,
   2,0xC5,0x03,
  13,0xC8,0x00,0x32,0x36,0x45,0x06,0x16,0x37,0x75,0x77,0x54,0x0C,0x00,
   2,0x36,0x0A,
   2,0x3A,0x55,
   5,0x2A,0x00,0x00,0x01,0x3F,
   5,0x2B,0x00,0x00,0x01,0xE0,
   0};
 writeCBList(data_init);
 _delay_ms(120);
 writeC(0x29);
#else
# error Add code here!
#endif
 DISP_CS(1);
 setColor(COLOR_BLACK);
 setBackColor(COLOR_WHITE);
}

void UTFT::lcdOff() {
#ifdef DISP_PCF8833
 DISP_CS(0);
 writeC(0x28);
 DISP_CS(1);
#elif defined(DISP_CPLD)
 DISP_CS(0);
 writeCW(0x01,0x0000);
 writeC(0x0F);   
 DISP_CS(1);
#endif
}

void UTFT::lcdOn() {
#ifdef DISP_PCF8833
 DISP_CS(0);
 writeC(0x29);
 DISP_CS(1);
#elif defined(DISP_CPLD)
 DISP_CS(0);
 writeCW(0x01,0x0010);
 writeC(0x0F);
 DISP_CS(1);
#endif
}

void UTFT::setContrast(char c) {
#ifdef DISP_PCF8833
 DISP_CS(0);
 if (c>64) c=64;
 writeC(0x25);
 writeB(c);
 DISP_CS(1);
#endif
}

void UTFT::setBrightness(byte br) {
#ifdef DISP_CPLD
 DISP_CS(0);
 if (br>16) br=16;
 writeCW(0x01,br);
 writeC(0x0F);
 DISP_CS(1);
#endif
}

void UTFT::setDisplayPage(byte page) {
#ifdef DISP_CPLD
 DISP_CS(0);
 if (page>7) page=7;
 writeCW(0x04,page);
 writeC(0x0F);
 DISP_CS(1);
#endif
}

void UTFT::setWritePage(byte page) {
#ifdef DISP_CPLD
 DISP_CS(0);
 if (page>7) page=7;
 writeCW(0x05,page);
 writeC(0x0F);
 DISP_CS(1);
#endif
}

// UTF-8 string processing helpers
// No error checking!
// 2-byte UTF-8 (110x xxxx 10xx xxxx)
// 3-byte UTF-8 (1110 xxxx 10xx xxxx 10xx xxxx)
wchar_t UTFT::take_wchar(const char*&s) {
 wchar_t ret=*s++;
 if (ret&0x80) {
  if (ret&0x20) {
   ret=ret<<12|(*s++&0x3F)<<6;
  }else ret=ret<<6&0x07C0;
  ret|=*s++&0x3F;
 }
 return ret; 
}

wchar_t UTFT::take_wchar(const __FlashString*&h) {
 const char*&s=reinterpret_cast<const char*&>(h);
 wchar_t ret=pgm_read_byte(s++);
 if (ret&0x80) {
  if (ret&0x20) {
   ret=ret<<12|(pgm_read_byte(s++)&0x3F)<<6;
  }else ret=ret<<6&0x07C0;
  ret|=pgm_read_byte(s++)&0x3F;
 }
 return ret;
}

/**********************************************
 * Speed-optimized setpixel + bitblt routines *
 **********************************************/

void UTFT::setXY(int X, int Y, bool swap) {
#if defined(DISP_ILI9325D)
 writeCW(0x20^swap,X);
 writeCW(0x21^swap,Y);
 writeC(0x22);		// prepare pixel value output
#elif defined(DISP_ILI9341) || defined(DISP_SPFD5408) || defined(DISP_S6D04H0) || defined(DISP_ILI9481)
 writeCWW(0x2A,X,X);	// Column Address Set
 writeCWW(0x2B,Y,Y);	// Page Address Set
 writeC(0x2C); 		// Memory write
#else
# error Add code here!
#endif
}

void UTFT::setWindow(byte o) {
 byte ori=orient&1;
#if defined(DISP_ILI9325D)
 if (o&0x80) {
  if (ori) o=o&0xF9 | o<<1&4 | o>>1&2;	// irgendwie müssen die Bits getauscht werden, hm...
  writeCW(0x03,0x1030^(o&7^ori)<<3);
 }
 if (o&0x40) {
  ori<<=1;
  writeCW(0x50^ori,L);
  writeCW(0x52^ori,T);
  writeCW(0x51^ori,R);
  writeCW(0x53^ori,B);
  ori>>=1;
 }
 if (o&0x10) setXY(X,Y,ori);		// set pixel address
#elif defined(DISP_ILI9341) || defined(DISP_SPFD5408) || defined(DISP_S6D04H0) || defined(DISP_ILI9481)
 if (o&0x80) {
  writeC(0x36);		// Memory Access Control
  writeB((o^ori)<<5|0x08|orient&0x04|orient<<3&0x10|1
# ifndef DISP_ILI9481
							^0x40
# endif
								);
 }
 if (o&0x40) {
  writeCWW(0x2A,L,R);	// Column Address Set
  writeCWW(0x2B,T,B);	// Page Address Set
  writeC(0x2C); 	// Memory write
 }else if (o&0x10) setXY(X,Y,ori);		// set pixel address
#else
# error Add code here!
#endif
}

void UTFT::scroll(int v) {
 DISP_CS(0);
#if defined(DISP_ILI9325D)
 writeCW(0x6A,v);
#elif defined(DISP_ILI9341) || defined(DISP_SPFD5408) || defined(DISP_S6D04H0) || defined(DISP_ILI9481)
 writeCW(0x37,v);	// Vertical Scrolling Start Address
#else
# error Add code here!
#endif
 DISP_CS(1);
}

void UTFT::setPixel() {
 if (clipOk()) {setWindow(0x30); writeW(fc);}
}

// Skips unset pixels for transparent bitblt:
// Calculates the new horizontal and vertical GRAM address
// x and y are _reversed_ _source_ coordinates
void UTFT::advance(int x,int y) {
 byte o=orient>>3;
 if (o&1) {
  x-=H;
  y-=W;
  if (o&2) x+=B; else x=T-x;
  if (o&4) y+=R; else y=L-y;
 }else{
  x-=W;		// W-x = current positive x, therefore, x is now 0 or negative
  y-=H;
  if (o&2) x+=R; else x=L-x;
  if (o&4) y+=B; else y=T-y;
 }
#ifdef DISP_SETXY
 setXY(x,y,(o^orient)&1);	// don't touch current position here!!
#else
#endif
}

void UTFT::advance() {
 byte o=orient;
 if (o&0x40) Y=o&0x80?(Y<<1)-YE:YE;	// escapement: advance current position
 else X=o&0x80?(X<<1)-XE:XE;
}

// Schwarzweiß-Version, vom Flash-Speicher, MSBfirst
// startbit = 0..7 (0 = MSB, 7 = LSB)
// Can work with very dense bitmaps (for bitmap fonts!)
// startbit is not limited to 0..7, can be any bit number
void UTFT::bitblt(const byte*data, int bitsperline, int startbit) {
 if (clipX() && clipY()) {
  int x=XC;
  int y=YC;
  if (orient&8) SWAP(x,y);
  startbit+=y*bitsperline+x;
  data+=startbit>>3;		// correct source address
  startbit&=7;
  x=W;
  y=H;
  if (orient&8) SWAP(x,y);
  bitsperline-=x;		// now: bits at end of line to skip
  bool skip=false;
  DISP_CS(0);
  setWindow(0xF0|orient>>3&7);	// make clipped window current
  do{
   byte b=pgm_read_byte(data);	// load first byte (possible multiple times!)
   byte m=1<<(~startbit&7);	// make first bit mask
   for (int xx=x;;) {
    if (flags&TRANS) {
     if (b&m) {
      if (skip) advance(xx,y);	// set new address only at non-contiguous pixels (for more speed)
      skip=false;
      writeW(fc);		// set pixel to foreground color at X/Y
     }else skip=true;
    }else writeW(b&m?fc:bc);	// set pixel (always)
    startbit++;			// track bit where we are
    if (!--xx) break;		// end of line
    m>>=1;			// next bit in mask
    if (!m) {			// if emptied...
     b=pgm_read_byte(++data);	// load next byte
     m=0x80;			// set next mask bit (MSB)
     startbit-=8;		// should be 0
    }
   }
   startbit+=bitsperline;	// startbit can be 0..8 inclusive(!) at loop exit
   data+=startbit>>3;		// prepate for next line
   startbit&=7;
#ifndef DISP_SETXY
   skip=true;
#endif
  }while(--y);
  unsetWindow();
  DISP_CS(1);
 }//else drawCircle(100,100,80);
 advance();
}

// HiColor-Version, vom Massenspeicher (Live-JPG-Dekompressor?)
void UTFT::bitblt(FILE*f, int wordsperline) {
 if (clipX() && clipY()) {
  int x=XC;
  int y=YC;
  if (x||y) {
   if (orient&8) SWAP(x,y);
   myseek(f,y*wordsperline+x<<1);	// correct source pointer
  }
  x=W;
  y=H;
  if (orient&8) SWAP(x,y);
  wordsperline-=x;		// now: skip words at end (should be >=0!)
  wordsperline<<=1;		// now: skip bytes at end
  bool skip=false;
  DISP_CS(0);
  setWindow(0xF0|orient>>3&7);	// make clipped window current
  do{		// iterate over source's y (lines)
   int xx=x;
   do{		// iterate over source's x (pixels)
    word w=mygetw(f);		// cannot handle EOF here
    if (flags&TRANS) {
     if (w==bc) skip=true;
     else{
      if (skip) advance(xx,y);	// set new address _only_ at non-contiguous pixels (for more speed)
      skip=false;
      goto w;
     }
    }else w: writeW(w);		// set pixel (always)
   }while(--xx);
   if (wordsperline) myseek(f,wordsperline);	// next line
  }while(--y);
  unsetWindow();
  DISP_CS(1);
 }//else drawCircle(120,120,80);
 advance();
}

void UTFT::bitblt(const word*w, int wordsperline) {
 FILE f;
 fdev_setup_stream(&f,0,0,_FDEV_SETUP_READ|__SSTR|__SPGM);
 f.len=(int)w;
 bitblt(&f,wordsperline);
}

/*********************************
 * Hardware-independent graphics *
 *********************************/
 
/*==== clipping ====*/

void UTFT::setClip(int l, int t, int r, int b) {
 if (flags&MOVE) {
  l+=XO; t+=YO; r+=XO; b+=YO;
  flags&=~MOVE;
 }
 if (l<0) l=0;
 if (t<0) t=0;
 int e=getDisplayXSize()-1;
 if (r>e) r=e;
 e=getDisplayYSize()-1;
 if (b>e) b=e;
 clip.L=l; clip.T=t; clip.R=r; clip.B=b;
}

bool UTFT::clipX() {
 int o=clip.L-L;
 XC=0;
 if (o>0) {L=clip.L; XC=o;}
 if (R>clip.R) R=clip.R;
 return (W=R-L+1)>0;	// new width, return true when not empty
}

bool UTFT::clipOk() const{	// checks point inside clipping area
 return clip.L<=X && X<=clip.R && clip.T<=Y && Y<=clip.B;
}

/*==== position calculation ====*/

void UTFT::setOrg(int x, int y) {
 if (flags&MOVE) {
  XO+=x; YO+=y;
  flags&=~MOVE;		// one-time flag!
 }else XO=x, YO=y;
}

void UTFT::gotoXY(int x,int y) {
 if (flags&MOVE) {
  X+=x;
  Y+=y;
  flags&=~MOVE;		// one-time flag!
 }else X=x+XO, Y=y+YO;
}

void UTFT::targetXY(int x,int y) {
 if (flags&MOVE) {
  XE=X+x;
  YE=Y+y;
  flags&=~MOVE;		// one-time flag!
 }else XE=x+XO, YE=y+YO;
}

void UTFT::calcRectX() {
 int w=XE-X;
 R=(w<0?(L=XE)+(W=-w):(L=X)+(W=w))-1; // W never negative
}

// remove a limiting bitblt window; CS must be 0
void UTFT::unsetWindow() {
 L=clip.L; T=clip.T; R=clip.R; B=clip.B;
#ifdef DISP_SETXY
 setWindow(0xC0);
#endif
}

/*==== points and lines ====*/

void UTFT::drawPixel() {
 DISP_CS(0);
 setPixel();
 DISP_CS(1);
}

void UTFT::lineTo(int x, int y) {
 targetXY(x,y);
 calcRect();	// calculate W and H
 if (!W) {
  R=L; B+=flags&ENDPIX; goto fr;
 }else if (!H) {
  B=T; R+=flags&ENDPIX; fr:
  fillRect();	// clip and draw
  X=XE;
  Y=YE;
 }else{
  int xs=XE<X?-1:1;
  int ys=YE<Y?-1:1;
  int err=W-H;
  DISP_CS(0);
  for(;X!=XE || Y!=YE;) {
   setPixel();		// clip and draw using DISPLAY X/Y coordinate
   int e2=err<<1;
   if (e2+H>0) err-=H, X+=xs;
   if (e2<W)   err+=W, Y+=ys;
  }
  if (flags&ENDPIX) setPixel();
  DISP_CS(1);
 }
}

/*==== outline figures ====*/

void UTFT::drawRect(int x1, int y1, int x2, int y2, int w) {
 if (x1>x2) SWAP(x1,x2);
 if (y1>y2) SWAP(y1,y2);
 flags&=~MOVE;
 if (x1+w+w>=x2 || y1+w+w>=y2) {
  fillRect(x1,y1,x2,y2);	// nicht offen
 }else{
  fillRect(x1,y1,x1+w,y2-w);	// links
  fillRect(x1+w,y1,x2,y1+w);	// oben
  fillRect(x2-w,y1+w,x2,y2);	// rechts
  fillRect(x1,y2-w,x2-w,y2);	// unten
 }
}

void UTFT::drawRoundRect(int x1, int y1, int x2, int y2, int) {
 if (x1>x2) SWAP(x1,x2);
 if (y1>y2) SWAP(y1,y2);
 int w=x2-x1;	// stets >=0
 int h=y2-y1;
 if ((unsigned)w>=4 && (unsigned)h>=4) {
  drawPixel(x1+1,y1+1);
  drawPixel(x2-2,y1+1);
  drawPixel(x1+1,y2-2);
  drawPixel(x2-2,y2-2);
  drawHLine(x1+2,y1,w-4);
  drawHLine(x1+2,y2-1,w-4);
  drawVLine(x1,y1+2,h-4);
  drawVLine(x2-1,y1+2,h-4);
 }
}

void UTFT::drawCircle(int x, int y, int radius) {
 int f = 1 - radius;
 int ddF_x = 1;
 int ddF_y = -2 * radius;
 int x1 = 0;
 int y1 = radius;
 
 DISP_CS(0);
 flags&=~MOVE;
 setPixel(x,y+radius);
 setPixel(x,y-radius);
 setPixel(x+radius,y);
 setPixel(x-radius,y);
 
 while(x1 < y1) {
  if (f>=0) {
   y1--;
   ddF_y += 2;
   f += ddF_y;
  }
  x1++;
  ddF_x += 2;
  f += ddF_x;    
  setPixel(x+x1,y+y1);
  setPixel(x-x1,y+y1);
  setPixel(x+x1,y-y1);
  setPixel(x-x1,y-y1);
  setPixel(x+y1,y+x1);
  setPixel(x-y1,y+x1);
  setPixel(x+y1,y-x1);
  setPixel(x-y1,y-x1);
 }
 DISP_CS(1);
}

void UTFT::drawPoly(const POINT*p,int n) {
 gotoXY(p[n-1].x,p[n-1].y);
 for (int i=0; i<n; i++) lineTo(p[i].x,p[i].y);
}

/*==== filled figures ====*/

void UTFT::fillRect() {
 if (clipX() && clipY()) {
  DISP_CS(0);
  setWindow(0xF0);	// no specific fill direction
  fillW(fc,W,H);
  unsetWindow();
  DISP_CS(1);
 }
}

void UTFT::fillRoundRect(int x1, int y1, int x2, int y2, int) {
 if (x1>x2) SWAP(x1,x2);
 if (y1>y2) SWAP(y1,y2);
 int w=x2-x1;	// stets >=0
 int h=y2-y1;
 if ((unsigned)w>=4 && (unsigned)h>=4) {
  drawHLine(x1+2,y1,w-4);
  drawHLine(x1+1,y1+1,w-2);
  fillRectW(x1,y1+2,w,h-4);
  drawHLine(x1+1,y2-2,w-2);
  drawHLine(x1+2,y2-1,w-4);
 }
}

void UTFT::fillCircle(int x, int y, int radius) {
 flags&=~MOVE;
 for (int y1=-radius; y1<=0; y1++) 
   for (int x1=-radius; x1<=0; x1++)
   if (x1*x1+y1*y1 <= radius*radius) {
  drawHLine(x+x1, y+y1, 2*(-x1));
  drawHLine(x+x1, y-y1, 2*(-x1));
  break;
 }
}

void UTFT::fillScr(word color) {
 DISP_CS(0);
 unsetWindow();
 setWindow(0xF0);
// setXY(L,T,orient);
 fillW(color,DISP_X_SIZE,DISP_Y_SIZE);
 DISP_CS(1);
}

/*==== character output ====*/

const wchar_t*wcschr_P(const wchar_t*p,wchar_t c) {
 for(;;p++) {
  wchar_t v=pgm_read_word(p);
  if (v==c) return p;
  if (!v) return 0;
 }
}

static wchar_t uni2cp1252(wchar_t c) {
// Generiert die Kodepositionen 0x80..0x9F
 static const PROGMEM wchar_t codes[]=
   L"€ ‚ƒ„…†‡ˆ‰Š‹Œ Ž "
   L" ‘’“”•–—˜™š›œ žŸ";
 if (c>128) {
  const wchar_t*p=wcschr_P(codes,c);
  if (p) c=0x80+p-codes;
 }
 return c;
}

void UTFT::print(wchar_t c) {
 if (flags&COOKED) {
  if (c=='\n') {
   byte o=orient;
   o^=o>>3;	// include font rotation and character direction
   o^=o>>5&6;	// include string escapement
   if (o&1) {	// rotated screen ^ rotated font
    Y=YO;
    if (o&2) X-=cfont.cy; else X+=cfont.cy;
   }else{
    X=XO;
    if (o&4) Y-=cfont.cy; else Y+=cfont.cy;
   }
   return;
  }
 }
 c=uni2cp1252(c);
 if (c<cfont.first || c>=cfont.first+cfont.count) c='?';
 c-=cfont.first;
 int bitsperline=0;
 int startbit=0;
 char cx=cfont.cx;
 char cy=cfont.cy;
 const byte*addr=cfont.bits;
 if (cx<=0) {			// propotional font
  char w=0;
  if (cx==-16) {
   for (wchar_t i=0;i<cfont.count;i++) {
    byte n=pgm_read_byte(addr++); // width of this character
    if (i==c) {
     startbit=bitsperline;	// take that snapshot
     w=n;
    }
    bitsperline+=n;		// sum all widths
   }
  }else{
   for (wchar_t i=0;;) {
    byte b=pgm_read_byte(addr++);
    for (byte m=0; m<2; m++) {
     byte n=(b&15)-cx;		// width of this character
     if (i==c) {
      startbit=bitsperline;	// take that snapshot
      w=n;
     }
     bitsperline+=n;		// sum the widths
     b>>=4;
     if (++i==cfont.count) goto exi;
    }
   }
  }
exi: cx=w;
#ifdef DEBUG
  deb1=w;
  deb2=bitsperline;
#endif
 }else{
  bitsperline=cfont.cx+7&~7;
  addr+=(byte)c*((byte)bitsperline>>3)*cy;
 }
 if (orient&8) SWAP(cx,cy);	// rotate target character cell (more precisely, flip on main diagonal)
 if (orient&16) cx=-cx;	// reflect character cell towards left
 if (orient&32) cy=-cy;	// reflect character cell towards top
 targetWH(cx,cy);
 calcRect();			// generate character cell
 bitblt(addr,bitsperline,startbit);
}

void UTFT::realign(int textlen) {
 if (align&0x0F) {		// horizontal
  if (align&2) textlen>>=1;	// zentrieren
  if (orient&8) {		// TODO: Operationen prüfen/korrigieren
   if (orient&0x20) Y+=textlen; else Y-=textlen;
  }else{
   if (orient&0x10) X+=textlen; else X-=textlen;
  }
 }
}

void UTFT::print(const char *s) {
 realign(align&0x0F?getTextExtent(s):0);
 for(;;) {
  wchar_t c=take_wchar(s);
  if (!c) break;
  print(c);
 }
}
void UTFT::print(const __FlashString*s) {
 realign(align&0x0F?getTextExtent(s):0);
 for(;;) {
  wchar_t c=take_wchar(s);
  if (!c) break;
  print(c);
 }
}

int UTFT::getTextExtent(wchar_t c) {
 int ext=cfont.cx;
 if (ext>0) return ext;	// return constant for monospaced font
 if (c<cfont.first || c+cfont.first>=cfont.count) c='?';
 c-=cfont.first;
 if (ext==-16) {	// Byte-Angaben
  return pgm_read_byte(cfont.bits+c);
 }else{			// Nibble-Angaben
  byte b=pgm_read_byte(cfont.bits+(c>>1));
  if (c&1) b>>=4;	// use high nibble
  return (b&15)-ext;	// "add" the minimum character width
 }
}
int UTFT::getTextExtent(const char *s) {
 int ext=0;
 for(;;) {
  wchar_t c=take_wchar(s);
  if (!c) break;
  ext+=getTextExtent(c);
 }
 return ext;
}
int UTFT::getTextExtent(const __FlashString*s) {
 int ext=0;
 for(;;) {
  wchar_t c=take_wchar(s);
  if (!c) break;
  ext+=getTextExtent(c);
 }
 return ext;
}
#ifdef DISP_PRINTF
int UTFT::sendByte(char b, FILE*f) {
 return reinterpret_cast<UTFT*>(f->get)->print(b),0;
}
void UTFT::vprintf(const char*fmt,va_list args) {
 FILE f;
 fdev_setup_stream(&f,sendByte,reinterpret_cast<int(*)(FILE*)>(this),_FDEV_SETUP_WRITE);
 vfprintf(&f,fmt,args);
}
void UTFT::printf(const char*fmt,...) {
 va_list va;
 va_start(va,fmt);
 vprintf(fmt,va);
 va_end(va);
}
void UTFT::vprintf(const __FlashString*fmt,va_list args) {
 FILE f;
 fdev_setup_stream(&f,sendByte,reinterpret_cast<int(*)(FILE*)>(this),_FDEV_SETUP_WRITE);
 vfprintf_P(&f,reinterpret_cast<const char*>(fmt),args);
}
void UTFT::printf(const __FlashString*fmt,...) {
 va_list va;
 va_start(va,fmt);
 vprintf(fmt,va);
 va_end(va);
}
#endif

void UTFT::setFont(const byte*font) {
 cfont.cx=pgm_read_byte(font++);
 cfont.cy=pgm_read_byte(font++);
 cfont.first=pgm_read_byte(font++);
 cfont.count=pgm_read_byte(font++);
 cfont.bits=font;
}
Detected encoding: UTF-80