Source file: /~heha/Mikrocontroller/Displays/utft/uno-itdb28.zip/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::LCD_Writ_Bus(word w) {
#if DISP_TRANSFER==8
 PORTD=w>>8;
 pulse_low(P_WR,B_WR);
 PORTD=w;
 pulse_low(P_WR,B_WR);
#elif DISP_TRANSFER==16
 PORTD=w>>8;
 PORTC=PORTC&0xFC|(byte)w>>6&3;
 PORTB=(byte)w&0x3F;
 pulse_low(P_WR,B_WR);
#else
# error "unimplemented display transfer mode"
#endif
}

void UTFT::_set_direction_registers() {
#ifdef P_RST	// has RESET connection?
 PORTC|=B_RS|B_WR|B_CS|B_RST;
 DDRC|=B_RS|B_WR|B_CS|B_RST;
#else
 PORTC|=B_RS|B_WR|B_CS;
 DDRC|=B_RS|B_WR|B_CS;
#endif
 DDRD =0xFF;
#if DISP_TRANSFER==16
 DDRB|=0x3F;
 DDRC|=0x03;
#endif
}

void UTFT::fillW(word w, int r1, int r2) {
#ifdef __MPS430__	// 16-bit optimized loops
 if (!r1 || !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
 PORTD=w>>8;
 PORTC=PORTC&0xFC|(byte)w>>6&3;
 PORTB=(byte)w&0x3F;
 DO pulse_low(P_WR,B_WR); WHILE;
#elif DISP_TRANSFER==8
 DO{		// inline LCD_Writ_Bus() code for performance
  PORTD=w>>8;
  pulse_low(P_WR,B_WR);
  PORTD=w;
  pulse_low(P_WR,B_WR);
 }WHILE;
#elif
 DO LCD_Writ_Bus(w); WHILE;
#endif
#undef DO
#undef WHILE
}

void UTFT::LCD_Write_CMD_DATA(byte b,word w) {
 LCD_CmdB(b);
 LCD_DataW(w);
}

/*************************************
 * Hardware-dependent initialization *
 *************************************/

void UTFT::InitLCD(byte orientation) {
 orient=orientation;
 flags=0;
 clrClip();
 clrOrg();
 //clrScale();
 _set_direction_registers();
#ifdef P_RST
 _delay_ms(5); 
 RST(0);
 _delay_ms(15);
 RST(1);
 _delay_ms(15);
#endif
 CS(0);
#ifdef disp_ILI9325D
 LCD_Write_CMD_DATA(0xE5,0x78F0); // set SRAM internal timing
 LCD_Write_CMD_DATA(0x01,orient<<7&0x0100); // set Driver Output Control, bit 8 reflects X counting
 LCD_Write_CMD_DATA(0x02,0x0200); // set 1 line inversion  
 LCD_Write_CMD_DATA(0x03,0x1030);
 LCD_Write_CMD_DATA(0x04,0);	// Resize register  
 LCD_Write_CMD_DATA(0x08,0x0207); // set the back porch and front porch  
 LCD_Write_CMD_DATA(0x09,0);	// set non-display area refresh cycle ISC[3:0]  
 LCD_Write_CMD_DATA(0x0A,0);	// FMARK function  
 LCD_Write_CMD_DATA(0x0C,0);	// RGB interface setting  
 LCD_Write_CMD_DATA(0x0D,0);	// Frame marker Position  
 LCD_Write_CMD_DATA(0x0F,0);	// RGB interface polarity  
	//*************Power On sequence ****************//  
 LCD_Write_CMD_DATA(0x10,0);	// SAP, BT[3:0], AP, DSTB, SLP, STB  
 LCD_Write_CMD_DATA(0x11,0x0007); // DC1[2:0], DC0[2:0], VC[2:0]  
 LCD_Write_CMD_DATA(0x12,0);	// VREG1OUT voltage  
 LCD_Write_CMD_DATA(0x13,0);	// VDV[4:0] for VCOM amplitude  
 LCD_Write_CMD_DATA(0x07,0x0001);  
 _delay_ms(200);		// Dis-charge capacitor power voltage  
 LCD_Write_CMD_DATA(0x10,0x1690); // SAP, BT[3:0], AP, DSTB, SLP, STB  
 LCD_Write_CMD_DATA(0x11,0x0227); // Set DC1[2:0], DC0[2:0], VC[2:0]  
 _delay_ms(50);
 LCD_Write_CMD_DATA(0x12,0x000D);
 _delay_ms(50);
 LCD_Write_CMD_DATA(0x13,0x1200); // VDV[4:0] for VCOM amplitude  
 LCD_Write_CMD_DATA(0x29,0x000A); // 04  VCM[5:0] for VCOMH  
 LCD_Write_CMD_DATA(0x2B,0x000D); // Set Frame Rate  
 _delay_ms(50);
	// ----------- Adjust the Gamma Curve ----------//  
 LCD_Write_CMD_DATA(0x30,0x0000);
 LCD_Write_CMD_DATA(0x31,0x0404);
 LCD_Write_CMD_DATA(0x32,0x0003);
 LCD_Write_CMD_DATA(0x35,0x0405);
 LCD_Write_CMD_DATA(0x36,0x0808);
 LCD_Write_CMD_DATA(0x37,0x0407);
 LCD_Write_CMD_DATA(0x38,0x0303);
 LCD_Write_CMD_DATA(0x39,0x0707);
 LCD_Write_CMD_DATA(0x3C,0x0504);
 LCD_Write_CMD_DATA(0x3D,0x0808);
	//------------------ Set GRAM area ---------------//
 unsetWindow();
 LCD_Write_CMD_DATA(0x60,0x2700|orient<<13&0x8000); // Gate Scan Line, bit 15 reflects Y counting
 LCD_Write_CMD_DATA(0x61,0x0001); // NDL,VLE, REV   
 LCD_Write_CMD_DATA(0x6A,0);	// set scrolling line  
	//-------------- Partial Display Control ---------//  
 LCD_Write_CMD_DATA(0x80,0);
 LCD_Write_CMD_DATA(0x81,0);
 LCD_Write_CMD_DATA(0x82,0);
 LCD_Write_CMD_DATA(0x83,0);
 LCD_Write_CMD_DATA(0x84,0);
 LCD_Write_CMD_DATA(0x85,0);
	//-------------- Panel Control -------------------//  
 LCD_Write_CMD_DATA(0x90,0x0010);  
 LCD_Write_CMD_DATA(0x92,0);
 LCD_Write_CMD_DATA(0x07,0x0133); // 262K color and display ON        
#else
# error "Add code here!"
#endif
 CS(1); 
 setColor(VGA_BLACK);
 setBackColor(VGA_WHITE);
}

void UTFT::lcdOff() {
 CS(0);
#ifdef disp_PCF8833
 LCD_CmdB(0x28);
#elifdef disp_CPLD
 LCD_Write_CMD_DATA(0x01,0x0000);
 LCD_CmdB(0x0F);   
#endif
 CS(1);
}

void UTFT::lcdOn() {
 CS(0);
#ifdef disp_PCF8833
 LCD_CmdB(0x29);
#elifdef disp_CPLD
 LCD_Write_CMD_DATA(0x01,0x0010);
 LCD_CmdB(0x0F);
#endif
 CS(1);
}

void UTFT::setContrast(char c) {
 CS(0);
#ifdef disp_PCF8833
 if (c>64) c=64;
 LCD_Write_COM(0x25);
 LCD_Write_DATA(c);
#endif
 CS(1);
}

void UTFT::setBrightness(byte br) {
 CS(0);
#ifdef disp_CPLD
 if (br>16) br=16;
 LCD_Write_CMD_DATA(0x01,br);
 LCD_Write_COM(0x0F);
#endif
 CS(1);
}

void UTFT::setDisplayPage(byte page) {
 CS(0);
#ifdef disp_CPLD
 if (page>7) page=7;
 LCD_Write_CMD_DATA(0x04,page);
 LCD_Write_COM(0x0F);
#endif
 CS(1);
}

void UTFT::setWritePage(byte page) {
 CS(0);
#ifdef disp_CPLD
 if (page>7) page=7;
 LCD_Write_CMD_DATA(0x05,page);
 LCD_Write_COM(0x0F);
#endif
 CS(1);
}

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

void UTFT::setXY(int X, int Y, bool swap) {
 LCD_Write_CMD_DATA(0x20^swap,X);
 LCD_Write_CMD_DATA(0x21^swap,Y);
 LCD_CmdB(0x22);	// prepare pixel value output
}

void UTFT::setWindow(byte o) {
 byte ori=orient&1;
#ifdef disp_ILI9325D
 if (o&0x80) {
  if (ori) o=o&0xF9 | o<<1&4 | o>>1&2;	// irgendwie müssen die Bits getauscht werden, hm...
  LCD_Write_CMD_DATA(0x03,0x1030^(o&7^ori)<<3);
 }
 if (o&0x40) {
  ori<<=1;
  LCD_Write_CMD_DATA(0x50^ori,L);
  LCD_Write_CMD_DATA(0x52^ori,T);
  LCD_Write_CMD_DATA(0x51^ori,R);
  LCD_Write_CMD_DATA(0x53^ori,B);
  ori>>=1;
 }
 if (o&0x10) setXY(X,Y,ori);		// set pixel address
#else
# error "Add code here!"
#endif
}

void UTFT::setPixel() {
 if (clipOk()) {setWindow(0x30); LCD_DataW(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;
 }
 setXY(x,y,(o^orient)&1);	// don't touch current position here!!
}

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;
  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;
      LCD_DataW(fc);		// set pixel to foreground color at X/Y
     }else skip=true;
    }else LCD_DataW(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;
  }while(--y);
  unsetWindow();
  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;
  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: LCD_DataW(w);	// set pixel (always)
   }while(--xx);
   if (wordsperline) myseek(f,wordsperline);	// next line
  }while(--y);
  unsetWindow();
  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;
 setWindow(0xC0);
}

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

void UTFT::drawPixel() {
 CS(0);
 setPixel();
 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;
  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();
  CS(1);
 }
}

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

void UTFT::drawRect(int x1, int y1, int x2, int y2) {
 if (x1>x2) {int t=x1;x1=x2;x2=t;}
 if (y1>y2) {int t=y1;y1=x2;y2=t;}
 flags&=~MOVE;
 drawHLine(x1,y1,x2-x1);
 drawHLine(x1,y2,x2-x1);
 drawVLine(x1,y1,y2-y1);
 drawVLine(x2,y1,y2-y1);
}

void UTFT::drawRoundRect(int x1, int y1, int x2, int y2, int) {
 if (x1>x2) {int t=x1;x1=x2;x2=t;}
 if (y1>y2) {int t=y1;y1=y2;y2=t;}
 if ((x2-x1)>4 && (y2-y1)>4) {
  drawPixel(x1+1,y1+1);
  drawPixel(x2-1,y1+1);
  drawPixel(x1+1,y2-1);
  drawPixel(x2-1,y2-1);
  drawHLine(x1+2, y1, x2-x1-4);
  drawHLine(x1+2, y2, x2-x1-4);
  drawVLine(x1, y1+2, y2-y1-4);
  drawVLine(x2, y1+2, y2-y1-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;
 
 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);
 }
 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()) {
  CS(0);
  setWindow(0xF0);	// no specific fill direction
  fillW(fc,W,H);
  unsetWindow();
  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);

 if ((x2-x1)>4 && (y2-y1)>4) {
  for (int i=0; i<((y2-y1)/2)+1; i++) {
   switch(i) {
    case 0:
	drawHLine(x1+2, y1+i, x2-x1-4);
	drawHLine(x1+2, y2-i, x2-x1-4);
	break;
    case 1:
	drawHLine(x1+1, y1+i, x2-x1-2);
	drawHLine(x1+1, y2-i, x2-x1-2);
	break;
    default:
	drawHLine(x1, y1+i, x2-x1);
	drawHLine(x1, y2-i, x2-x1);
   }
  }
 }
}

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) {
 CS(0);
 unsetWindow();
 setXY(L,T,orient);
 fillW(color,DISP_X_SIZE,DISP_Y_SIZE);
 CS(1);
}

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

void UTFT::print(char 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;
  }
 }
 if ((byte)c<cfont.first || (byte)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) {			// dense propotional font
  char w=0;
  for (byte 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==(byte)c) {
     startbit=bitsperline;	// take that snapshot
     w=n;
    }
    bitsperline+=n;		// sum the widths
    b>>=4;
    if (++i==cfont.count) goto exi;
   }
  }
exi: cx=w;
  deb1=w;
  deb2=bitsperline;
 }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::print(const char *s) {
 for(;;s++) {
  char c=*s;
  if (!c) break;
  print(c);
 }
}
void UTFT::print(const __FlashStringHelper*h) {
 const char*s=(const char*)h;
 for(;;s++) {
  char c=pgm_read_byte(s);
  if (!c) break;
  print(c);
 }
}

int UTFT::getTextExtent(char c) {
 int ext=cfont.cx;
 if (ext>0) return ext;	// return constant for monospaced font
 if ((byte)c<cfont.first || (byte)c+cfont.first>=cfont.count) c='?';
 c-=cfont.first;
 byte b=pgm_read_byte(cfont.bits+(c>>1));
 if (c&1) b>>=1;	// use high nibble
 return (b&15)-ext;	// "add" the minimum character width
}
int UTFT::getTextExtent(const char *s) {
 int ext=0;
 for(;;s++) {
  char c=*s;
  if (!c) break;
  ext+=getTextExtent(c);
 }
 return ext;
}
int UTFT::getTextExtent(const __FlashStringHelper*h) {
 int ext=0;
 const char*s=(const char*)h;
 for(;;s++) {
  char c=pgm_read_byte(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 __FlashStringHelper*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 __FlashStringHelper*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