2363 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2363 lines
		
	
	
		
			65 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* TFT module
 | 
						|
 *
 | 
						|
 *  Author: LoBo (loboris@gmail.com, loboris.github)
 | 
						|
 *
 | 
						|
 *  Module supporting SPI TFT displays based on ILI9341 controllers
 | 
						|
*/
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <string.h>
 | 
						|
#include "freertos/FreeRTOS.h"
 | 
						|
#include "freertos/task.h"
 | 
						|
#include "esp_system.h"
 | 
						|
#include "tft.h"
 | 
						|
#include "time.h"
 | 
						|
#include <math.h>
 | 
						|
// #include "rom/tjpgd.h"
 | 
						|
#include "esp_heap_caps.h"
 | 
						|
#include "tftspi.h"
 | 
						|
#include "mgos.h"
 | 
						|
 | 
						|
// Define which spi bus to use VSPI_HOST or HSPI_HOST
 | 
						|
#define SPI_BUS VSPI_HOST
 | 
						|
 | 
						|
#define DEG_TO_RAD 0.01745329252
 | 
						|
#define RAD_TO_DEG 57.295779513
 | 
						|
#define deg_to_rad 0.01745329252 + 3.14159265359
 | 
						|
#define swap(a, b) { int16_t t = a; a = b; b = t; }
 | 
						|
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
 | 
						|
#if !defined(max)
 | 
						|
#define max(A,B) ( (A) > (B) ? (A):(B))
 | 
						|
#endif
 | 
						|
#if !defined(min)
 | 
						|
#define min(A,B) ( (A) < (B) ? (A):(B))
 | 
						|
#endif
 | 
						|
 | 
						|
// Embedded fonts
 | 
						|
extern uint8_t tft_SmallFont[];
 | 
						|
extern uint8_t tft_DefaultFont[];
 | 
						|
extern uint8_t tft_Dejavu18[];
 | 
						|
extern uint8_t tft_Dejavu24[];
 | 
						|
extern uint8_t tft_Ubuntu16[];
 | 
						|
extern uint8_t tft_Comic24[];
 | 
						|
extern uint8_t tft_minya24[];
 | 
						|
extern uint8_t tft_tooney32[];
 | 
						|
extern uint8_t tft_def_small[];
 | 
						|
 | 
						|
// ==== Color definitions constants ==============
 | 
						|
const color_t ILI9341_BLACK       = {   0,   0,   0 };
 | 
						|
const color_t ILI9341_NAVY        = {   0,   0, 128 };
 | 
						|
const color_t ILI9341_DARKGREEN   = {   0, 128,   0 };
 | 
						|
const color_t ILI9341_DARKCYAN    = {   0, 128, 128 };
 | 
						|
const color_t ILI9341_MAROON      = { 128,   0,   0 };
 | 
						|
const color_t ILI9341_PURPLE      = { 128,   0, 128 };
 | 
						|
const color_t ILI9341_OLIVE       = { 128, 128,   0 };
 | 
						|
const color_t ILI9341_LIGHTGREY   = { 192, 192, 192 };
 | 
						|
const color_t ILI9341_DARKGREY    = { 128, 128, 128 };
 | 
						|
const color_t ILI9341_BLUE        = {   0,   0, 255 };
 | 
						|
const color_t ILI9341_GREEN       = {   0, 255,   0 };
 | 
						|
const color_t ILI9341_CYAN        = {   0, 255, 255 };
 | 
						|
const color_t ILI9341_RED         = { 252,   0,   0 };
 | 
						|
const color_t ILI9341_MAGENTA     = { 252,   0, 255 };
 | 
						|
const color_t ILI9341_YELLOW      = { 252, 252,   0 };
 | 
						|
const color_t ILI9341_WHITE       = { 252, 252, 252 };
 | 
						|
const color_t ILI9341_ORANGE      = { 252, 164,   0 };
 | 
						|
const color_t ILI9341_GREENYELLOW = { 172, 252,  44 };
 | 
						|
const color_t ILI9341_PINK        = { 252, 192, 202 };
 | 
						|
// ===============================================
 | 
						|
 | 
						|
// ==============================================================
 | 
						|
// ==== Set default values of global variables ==================
 | 
						|
uint8_t orientation = LANDSCAPE;// screen orientation
 | 
						|
uint16_t font_rotate = 0;		// font rotation
 | 
						|
uint8_t	font_transparent = 0;
 | 
						|
uint8_t	font_forceFixed = 0;
 | 
						|
uint8_t	text_wrap = 0;			// character wrapping to new line
 | 
						|
color_t	_fg = {  0, 255,   0};
 | 
						|
color_t _bg = {  0,   0,   0};
 | 
						|
uint8_t image_debug = 0;
 | 
						|
 | 
						|
float _angleOffset = DEFAULT_ANGLE_OFFSET;
 | 
						|
 | 
						|
int	TFT_X = 0;
 | 
						|
int	TFT_Y = 0;
 | 
						|
 | 
						|
dispWin_t dispWin = {
 | 
						|
  .x1 = 0,
 | 
						|
  .y1 = 0,
 | 
						|
  .x2 = DEFAULT_TFT_DISPLAY_WIDTH,
 | 
						|
  .y2 = DEFAULT_TFT_DISPLAY_HEIGHT,
 | 
						|
};
 | 
						|
 | 
						|
Font cfont = {
 | 
						|
	.font = tft_DefaultFont,
 | 
						|
	.x_size = 0,
 | 
						|
	.y_size = 0x0B,
 | 
						|
	.offset = 0,
 | 
						|
	.numchars = 95,
 | 
						|
	.bitmap = 1,
 | 
						|
};
 | 
						|
 | 
						|
uint8_t font_buffered_char = 1;
 | 
						|
uint8_t font_line_space = 0;
 | 
						|
// ==============================================================
 | 
						|
 | 
						|
 | 
						|
typedef struct {
 | 
						|
      uint8_t charCode;
 | 
						|
      int adjYOffset;
 | 
						|
      int width;
 | 
						|
      int height;
 | 
						|
      int xOffset;
 | 
						|
      int xDelta;
 | 
						|
      uint16_t dataPtr;
 | 
						|
} propFont;
 | 
						|
 | 
						|
static dispWin_t dispWinTemp;
 | 
						|
 | 
						|
static uint8_t *userfont = NULL;
 | 
						|
static int TFT_OFFSET = 0;
 | 
						|
static propFont	fontChar;
 | 
						|
static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX;
 | 
						|
 | 
						|
 | 
						|
// =========================================================================
 | 
						|
// ** All drawings are clipped to 'dispWin' **
 | 
						|
// ** All x,y coordinates in public functions are relative to clip window **
 | 
						|
// =========== : Public functions
 | 
						|
// ----------- : Local functions
 | 
						|
// =========================================================================
 | 
						|
 | 
						|
 | 
						|
// Compare two colors; return 0 if equal
 | 
						|
//============================================
 | 
						|
int mgos_ili9341_compare_colors(color_t c1, color_t c2)
 | 
						|
{
 | 
						|
	if ((c1.r & 0xFC) != (c2.r & 0xFC)) return 1;
 | 
						|
	if ((c1.g & 0xFC) != (c2.g & 0xFC)) return 1;
 | 
						|
	if ((c1.b & 0xFC) != (c2.b & 0xFC)) return 1;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
// draw color pixel on screen
 | 
						|
//------------------------------------------------------------------------
 | 
						|
static void _drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
 | 
						|
 | 
						|
	if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
 | 
						|
	drawPixel(x, y, color, sel);
 | 
						|
}
 | 
						|
 | 
						|
//====================================================================
 | 
						|
void mgos_ili9341_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
 | 
						|
 | 
						|
	_drawPixel(x+dispWin.x1, y+dispWin.y1, color, sel);
 | 
						|
}
 | 
						|
 | 
						|
//===========================================
 | 
						|
color_t mgos_ili9341_readPixel(int16_t x, int16_t y) {
 | 
						|
 | 
						|
  if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return ILI9341_BLACK;
 | 
						|
 | 
						|
  return readPixel(x, y);
 | 
						|
}
 | 
						|
 | 
						|
//--------------------------------------------------------------------------
 | 
						|
static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
 | 
						|
	// clipping
 | 
						|
	if ((x < dispWin.x1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
 | 
						|
	if (y < dispWin.y1) {
 | 
						|
		h -= (dispWin.y1 - y);
 | 
						|
		y = dispWin.y1;
 | 
						|
	}
 | 
						|
	if (h < 0) h = 0;
 | 
						|
	if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1;
 | 
						|
	if (h == 0) h = 1;
 | 
						|
	mgos_ili9341_pushColorRep(x, y, x, y+h-1, color, (uint32_t)h);
 | 
						|
}
 | 
						|
 | 
						|
//--------------------------------------------------------------------------
 | 
						|
static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
 | 
						|
	// clipping
 | 
						|
	if ((y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
 | 
						|
	if (x < dispWin.x1) {
 | 
						|
		w -= (dispWin.x1 - x);
 | 
						|
		x = dispWin.x1;
 | 
						|
	}
 | 
						|
	if (w < 0) w = 0;
 | 
						|
	if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1;
 | 
						|
	if (w == 0) w = 1;
 | 
						|
 | 
						|
	mgos_ili9341_pushColorRep(x, y, x+w-1, y, color, (uint32_t)w);
 | 
						|
}
 | 
						|
 | 
						|
//======================================================================
 | 
						|
void mgos_ili9341_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
 | 
						|
	_drawFastVLine(x+dispWin.x1, y+dispWin.y1, h, color);
 | 
						|
}
 | 
						|
 | 
						|
//======================================================================
 | 
						|
void mgos_ili9341_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
 | 
						|
	_drawFastHLine(x+dispWin.x1, y+dispWin.y1, w, color);
 | 
						|
}
 | 
						|
 | 
						|
// Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses
 | 
						|
// the eficient FastH/V Line draw routine for segments of 2 pixels or more
 | 
						|
//----------------------------------------------------------------------------------
 | 
						|
static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
 | 
						|
{
 | 
						|
  if (x0 == x1) {
 | 
						|
	  if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color);
 | 
						|
	  else _drawFastVLine(x0, y1, y0-y1, color);
 | 
						|
	  return;
 | 
						|
  }
 | 
						|
  if (y0 == y1) {
 | 
						|
	  if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color);
 | 
						|
	  else _drawFastHLine(x1, y0, x0-x1, color);
 | 
						|
	  return;
 | 
						|
  }
 | 
						|
 | 
						|
  int steep = 0;
 | 
						|
  if (abs(y1 - y0) > abs(x1 - x0)) steep = 1;
 | 
						|
  if (steep) {
 | 
						|
    swap(x0, y0);
 | 
						|
    swap(x1, y1);
 | 
						|
  }
 | 
						|
  if (x0 > x1) {
 | 
						|
    swap(x0, x1);
 | 
						|
    swap(y0, y1);
 | 
						|
  }
 | 
						|
 | 
						|
  int16_t dx = x1 - x0, dy = abs(y1 - y0);
 | 
						|
  int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
 | 
						|
 | 
						|
  if (y0 < y1) ystep = 1;
 | 
						|
 | 
						|
  // Split into steep and not steep for FastH/V separation
 | 
						|
  if (steep) {
 | 
						|
    for (; x0 <= x1; x0++) {
 | 
						|
      dlen++;
 | 
						|
      err -= dy;
 | 
						|
      if (err < 0) {
 | 
						|
        err += dx;
 | 
						|
        if (dlen == 1) _drawPixel(y0, xs, color, 1);
 | 
						|
        else _drawFastVLine(y0, xs, dlen, color);
 | 
						|
        dlen = 0; y0 += ystep; xs = x0 + 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (dlen) _drawFastVLine(y0, xs, dlen, color);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    for (; x0 <= x1; x0++) {
 | 
						|
      dlen++;
 | 
						|
      err -= dy;
 | 
						|
      if (err < 0) {
 | 
						|
        err += dx;
 | 
						|
        if (dlen == 1) _drawPixel(xs, y0, color, 1);
 | 
						|
        else _drawFastHLine(xs, y0, dlen, color);
 | 
						|
        dlen = 0; y0 += ystep; xs = x0 + 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (dlen) _drawFastHLine(xs, y0, dlen, color);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
void mgos_ili9341_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
 | 
						|
{
 | 
						|
	_drawLine(x0+dispWin.x1, y0+dispWin.y1, x1+dispWin.x1, y1+dispWin.y1, color);
 | 
						|
}
 | 
						|
 | 
						|
// fill a rectangle
 | 
						|
//--------------------------------------------------------------------------------
 | 
						|
static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
 | 
						|
	// clipping
 | 
						|
	if ((x >= dispWin.x2) || (y > dispWin.y2)) return;
 | 
						|
 | 
						|
	if (x < dispWin.x1) {
 | 
						|
		w -= (dispWin.x1 - x);
 | 
						|
		x = dispWin.x1;
 | 
						|
	}
 | 
						|
	if (y < dispWin.y1) {
 | 
						|
		h -= (dispWin.y1 - y);
 | 
						|
		y = dispWin.y1;
 | 
						|
	}
 | 
						|
	if (w < 0) w = 0;
 | 
						|
	if (h < 0) h = 0;
 | 
						|
 | 
						|
	if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1;
 | 
						|
	if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1;
 | 
						|
	if (w == 0) w = 1;
 | 
						|
	if (h == 0) h = 1;
 | 
						|
	mgos_ili9341_pushColorRep(x, y, x+w-1, y+h-1, color, (uint32_t)(h*w));
 | 
						|
}
 | 
						|
 | 
						|
//============================================================================
 | 
						|
void mgos_ili9341_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
 | 
						|
	_fillRect(x+dispWin.x1, y+dispWin.y1, w, h, color);
 | 
						|
}
 | 
						|
 | 
						|
//==================================
 | 
						|
void mgos_ili9341_fillScreen(color_t color) {
 | 
						|
	mgos_ili9341_pushColorRep(0, 0, _width-1, _height-1, color, (uint32_t)(_height*_width));
 | 
						|
}
 | 
						|
 | 
						|
//==================================
 | 
						|
void mgos_ili9341_fillWindow(color_t color) {
 | 
						|
	mgos_ili9341_pushColorRep(dispWin.x1, dispWin.y1, dispWin.x2, dispWin.y2,
 | 
						|
			color, (uint32_t)((dispWin.x2-dispWin.x1+1) * (dispWin.y2-dispWin.y1+1)));
 | 
						|
}
 | 
						|
 | 
						|
// ^^^============= Basics drawing functions ================================^^^
 | 
						|
 | 
						|
 | 
						|
// ================ Graphics drawing functions ==================================
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------------
 | 
						|
static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
 | 
						|
  _drawFastHLine(x1,y1,w, color);
 | 
						|
  _drawFastVLine(x1+w-1,y1,h, color);
 | 
						|
  _drawFastHLine(x1,y1+h-1,w, color);
 | 
						|
  _drawFastVLine(x1,y1,h, color);
 | 
						|
}
 | 
						|
 | 
						|
//===============================================================================
 | 
						|
void mgos_ili9341_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
 | 
						|
	_drawRect(x1+dispWin.x1, y1+dispWin.y1, w, h, color);
 | 
						|
}
 | 
						|
 | 
						|
//-------------------------------------------------------------------------------------------------
 | 
						|
static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color)
 | 
						|
{
 | 
						|
	int16_t f = 1 - r;
 | 
						|
	int16_t ddF_x = 1;
 | 
						|
	int16_t ddF_y = -2 * r;
 | 
						|
	int16_t x = 0;
 | 
						|
	int16_t y = r;
 | 
						|
 | 
						|
	disp_select();
 | 
						|
	while (x < y) {
 | 
						|
		if (f >= 0) {
 | 
						|
			y--;
 | 
						|
			ddF_y += 2;
 | 
						|
			f += ddF_y;
 | 
						|
		}
 | 
						|
		x++;
 | 
						|
		ddF_x += 2;
 | 
						|
		f += ddF_x;
 | 
						|
		if (cornername & 0x4) {
 | 
						|
			_drawPixel(x0 + x, y0 + y, color, 0);
 | 
						|
			_drawPixel(x0 + y, y0 + x, color, 0);
 | 
						|
		}
 | 
						|
		if (cornername & 0x2) {
 | 
						|
			_drawPixel(x0 + x, y0 - y, color, 0);
 | 
						|
			_drawPixel(x0 + y, y0 - x, color, 0);
 | 
						|
		}
 | 
						|
		if (cornername & 0x8) {
 | 
						|
			_drawPixel(x0 - y, y0 + x, color, 0);
 | 
						|
			_drawPixel(x0 - x, y0 + y, color, 0);
 | 
						|
		}
 | 
						|
		if (cornername & 0x1) {
 | 
						|
			_drawPixel(x0 - y, y0 - x, color, 0);
 | 
						|
			_drawPixel(x0 - x, y0 - y, color, 0);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	disp_deselect();
 | 
						|
}
 | 
						|
 | 
						|
// Used to do circles and roundrects
 | 
						|
//----------------------------------------------------------------------------------------------------------------
 | 
						|
static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r,	uint8_t cornername, int16_t delta, color_t color)
 | 
						|
{
 | 
						|
	int16_t f = 1 - r;
 | 
						|
	int16_t ddF_x = 1;
 | 
						|
	int16_t ddF_y = -2 * r;
 | 
						|
	int16_t x = 0;
 | 
						|
	int16_t y = r;
 | 
						|
	int16_t ylm = x0 - r;
 | 
						|
 | 
						|
	while (x < y) {
 | 
						|
		if (f >= 0) {
 | 
						|
			if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
 | 
						|
			if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
 | 
						|
			ylm = x0 - y;
 | 
						|
			y--;
 | 
						|
			ddF_y += 2;
 | 
						|
			f += ddF_y;
 | 
						|
		}
 | 
						|
		x++;
 | 
						|
		ddF_x += 2;
 | 
						|
		f += ddF_x;
 | 
						|
 | 
						|
		if ((x0 - x) > ylm) {
 | 
						|
			if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
 | 
						|
			if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Draw a rounded rectangle
 | 
						|
//=============================================================================================
 | 
						|
void mgos_ili9341_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
 | 
						|
{
 | 
						|
	x += dispWin.x1;
 | 
						|
	y += dispWin.y1;
 | 
						|
 | 
						|
	// smarter version
 | 
						|
	_drawFastHLine(x + r, y, w - 2 * r, color);			// Top
 | 
						|
	_drawFastHLine(x + r, y + h - 1, w - 2 * r, color);	// Bottom
 | 
						|
	_drawFastVLine(x, y + r, h - 2 * r, color);			// Left
 | 
						|
	_drawFastVLine(x + w - 1, y + r, h - 2 * r, color);	// Right
 | 
						|
 | 
						|
	// draw four corners
 | 
						|
	drawCircleHelper(x + r, y + r, r, 1, color);
 | 
						|
	drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
 | 
						|
	drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
 | 
						|
	drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
 | 
						|
}
 | 
						|
 | 
						|
// Fill a rounded rectangle
 | 
						|
//=============================================================================================
 | 
						|
void mgos_ili9341_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
 | 
						|
{
 | 
						|
	x += dispWin.x1;
 | 
						|
	y += dispWin.y1;
 | 
						|
 | 
						|
	// smarter version
 | 
						|
	_fillRect(x + r, y, w - 2 * r, h, color);
 | 
						|
 | 
						|
	// draw four corners
 | 
						|
	fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
 | 
						|
	fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------------------------
 | 
						|
static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color)
 | 
						|
{
 | 
						|
	_drawLine(
 | 
						|
		x,
 | 
						|
		y,
 | 
						|
		x + length * cos((angle + _angleOffset) * DEG_TO_RAD),
 | 
						|
		y + length * sin((angle + _angleOffset) * DEG_TO_RAD), color);
 | 
						|
}
 | 
						|
 | 
						|
//---------------------------------------------------------------------------------------------------------------
 | 
						|
static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color)
 | 
						|
{
 | 
						|
	_drawLine(
 | 
						|
		x + start * cos((angle + _angleOffset) * DEG_TO_RAD),
 | 
						|
		y + start * sin((angle + _angleOffset) * DEG_TO_RAD),
 | 
						|
		x + (start + length) * cos((angle + _angleOffset) * DEG_TO_RAD),
 | 
						|
		y + (start + length) * sin((angle + _angleOffset) * DEG_TO_RAD), color);
 | 
						|
}
 | 
						|
 | 
						|
//===========================================================================================================
 | 
						|
void mgos_ili9341_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color)
 | 
						|
{
 | 
						|
	x += dispWin.x1;
 | 
						|
	y += dispWin.y1;
 | 
						|
 | 
						|
	if (start == 0) _drawLineByAngle(x, y, angle, len, color);
 | 
						|
	else _DrawLineByAngle(x, y, angle, start, len, color);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// Draw a triangle
 | 
						|
//--------------------------------------------------------------------------------------------------------------------
 | 
						|
static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
 | 
						|
{
 | 
						|
	_drawLine(x0, y0, x1, y1, color);
 | 
						|
	_drawLine(x1, y1, x2, y2, color);
 | 
						|
	_drawLine(x2, y2, x0, y0, color);
 | 
						|
}
 | 
						|
 | 
						|
//================================================================================================================
 | 
						|
void mgos_ili9341_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
 | 
						|
{
 | 
						|
	x0 += dispWin.x1;
 | 
						|
	y0 += dispWin.y1;
 | 
						|
	x1 += dispWin.x1;
 | 
						|
	y1 += dispWin.y1;
 | 
						|
	x2 += dispWin.x1;
 | 
						|
	y2 += dispWin.y1;
 | 
						|
 | 
						|
	_drawLine(x0, y0, x1, y1, color);
 | 
						|
	_drawLine(x1, y1, x2, y2, color);
 | 
						|
	_drawLine(x2, y2, x0, y0, color);
 | 
						|
}
 | 
						|
 | 
						|
// Fill a triangle
 | 
						|
//--------------------------------------------------------------------------------------------------------------------
 | 
						|
static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
 | 
						|
{
 | 
						|
  int16_t a, b, y, last;
 | 
						|
 | 
						|
  // Sort coordinates by Y order (y2 >= y1 >= y0)
 | 
						|
  if (y0 > y1) {
 | 
						|
    swap(y0, y1); swap(x0, x1);
 | 
						|
  }
 | 
						|
  if (y1 > y2) {
 | 
						|
    swap(y2, y1); swap(x2, x1);
 | 
						|
  }
 | 
						|
  if (y0 > y1) {
 | 
						|
    swap(y0, y1); swap(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;
 | 
						|
    _drawFastHLine(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;
 | 
						|
  int32_t
 | 
						|
    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;
 | 
						|
    /* longhand:
 | 
						|
    a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
 | 
						|
    b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
 | 
						|
    */
 | 
						|
    if(a > b) swap(a,b);
 | 
						|
    _drawFastHLine(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;
 | 
						|
    /* longhand:
 | 
						|
    a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
 | 
						|
    b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
 | 
						|
    */
 | 
						|
    if(a > b) swap(a,b);
 | 
						|
    _drawFastHLine(a, y, b-a+1, color);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//================================================================================================================
 | 
						|
void mgos_ili9341_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
 | 
						|
{
 | 
						|
	_fillTriangle(
 | 
						|
			x0 + dispWin.x1, y0 + dispWin.y1,
 | 
						|
			x1 + dispWin.x1, y1 + dispWin.y1,
 | 
						|
			x2 + dispWin.x1, y2 + dispWin.y1,
 | 
						|
			color);
 | 
						|
}
 | 
						|
 | 
						|
//====================================================================
 | 
						|
void mgos_ili9341_drawCircle(int16_t x, int16_t y, int radius, color_t color) {
 | 
						|
	x += dispWin.x1;
 | 
						|
	y += dispWin.y1;
 | 
						|
	int f = 1 - radius;
 | 
						|
	int ddF_x = 1;
 | 
						|
	int ddF_y = -2 * radius;
 | 
						|
	int x1 = 0;
 | 
						|
	int y1 = radius;
 | 
						|
 | 
						|
	disp_select();
 | 
						|
	_drawPixel(x, y + radius, color, 0);
 | 
						|
	_drawPixel(x, y - radius, color, 0);
 | 
						|
	_drawPixel(x + radius, y, color, 0);
 | 
						|
	_drawPixel(x - radius, y, color, 0);
 | 
						|
	while(x1 < y1) {
 | 
						|
		if (f >= 0) {
 | 
						|
			y1--;
 | 
						|
			ddF_y += 2;
 | 
						|
			f += ddF_y;
 | 
						|
		}
 | 
						|
		x1++;
 | 
						|
		ddF_x += 2;
 | 
						|
		f += ddF_x;
 | 
						|
		_drawPixel(x + x1, y + y1, color, 0);
 | 
						|
		_drawPixel(x - x1, y + y1, color, 0);
 | 
						|
		_drawPixel(x + x1, y - y1, color, 0);
 | 
						|
		_drawPixel(x - x1, y - y1, color, 0);
 | 
						|
		_drawPixel(x + y1, y + x1, color, 0);
 | 
						|
		_drawPixel(x - y1, y + x1, color, 0);
 | 
						|
		_drawPixel(x + y1, y - x1, color, 0);
 | 
						|
		_drawPixel(x - y1, y - x1, color, 0);
 | 
						|
	}
 | 
						|
  disp_deselect();
 | 
						|
}
 | 
						|
 | 
						|
//====================================================================
 | 
						|
void mgos_ili9341_fillCircle(int16_t x, int16_t y, int radius, color_t color) {
 | 
						|
	x += dispWin.x1;
 | 
						|
	y += dispWin.y1;
 | 
						|
 | 
						|
	_drawFastVLine(x, y-radius, 2*radius+1, color);
 | 
						|
	fillCircleHelper(x, y, radius, 3, 0, color);
 | 
						|
}
 | 
						|
 | 
						|
//----------------------------------------------------------------------------------------------------------------
 | 
						|
static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
 | 
						|
{
 | 
						|
	disp_select();
 | 
						|
    // upper right
 | 
						|
    if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color, 0);
 | 
						|
    // upper left
 | 
						|
    if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color, 0);
 | 
						|
    // lower right
 | 
						|
    if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color, 0);
 | 
						|
    // lower left
 | 
						|
    if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color, 0);
 | 
						|
	disp_deselect();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//=====================================================================================================
 | 
						|
void mgos_ili9341_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
 | 
						|
{
 | 
						|
	x0 += dispWin.x1;
 | 
						|
	y0 += dispWin.y1;
 | 
						|
 | 
						|
	uint16_t x, y;
 | 
						|
	int32_t xchg, ychg;
 | 
						|
	int32_t err;
 | 
						|
	int32_t rxrx2;
 | 
						|
	int32_t ryry2;
 | 
						|
	int32_t stopx, stopy;
 | 
						|
 | 
						|
	rxrx2 = rx;
 | 
						|
	rxrx2 *= rx;
 | 
						|
	rxrx2 *= 2;
 | 
						|
 | 
						|
	ryry2 = ry;
 | 
						|
	ryry2 *= ry;
 | 
						|
	ryry2 *= 2;
 | 
						|
 | 
						|
	x = rx;
 | 
						|
	y = 0;
 | 
						|
 | 
						|
	xchg = 1;
 | 
						|
	xchg -= rx;
 | 
						|
	xchg -= rx;
 | 
						|
	xchg *= ry;
 | 
						|
	xchg *= ry;
 | 
						|
 | 
						|
	ychg = rx;
 | 
						|
	ychg *= rx;
 | 
						|
 | 
						|
	err = 0;
 | 
						|
 | 
						|
	stopx = ryry2;
 | 
						|
	stopx *= rx;
 | 
						|
	stopy = 0;
 | 
						|
 | 
						|
	while( stopx >= stopy ) {
 | 
						|
		_draw_ellipse_section(x, y, x0, y0, color, option);
 | 
						|
		y++;
 | 
						|
		stopy += rxrx2;
 | 
						|
		err += ychg;
 | 
						|
		ychg += rxrx2;
 | 
						|
		if ( 2*err+xchg > 0 ) {
 | 
						|
			x--;
 | 
						|
			stopx -= ryry2;
 | 
						|
			err += xchg;
 | 
						|
			xchg += ryry2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	x = 0;
 | 
						|
	y = ry;
 | 
						|
 | 
						|
	xchg = ry;
 | 
						|
	xchg *= ry;
 | 
						|
 | 
						|
	ychg = 1;
 | 
						|
	ychg -= ry;
 | 
						|
	ychg -= ry;
 | 
						|
	ychg *= rx;
 | 
						|
	ychg *= rx;
 | 
						|
 | 
						|
	err = 0;
 | 
						|
 | 
						|
	stopx = 0;
 | 
						|
 | 
						|
	stopy = rxrx2;
 | 
						|
	stopy *= ry;
 | 
						|
 | 
						|
	while( stopx <= stopy ) {
 | 
						|
		_draw_ellipse_section(x, y, x0, y0, color, option);
 | 
						|
		x++;
 | 
						|
		stopx += ryry2;
 | 
						|
		err += xchg;
 | 
						|
		xchg += ryry2;
 | 
						|
		if ( 2*err+ychg > 0 ) {
 | 
						|
			y--;
 | 
						|
			stopy -= rxrx2;
 | 
						|
			err += ychg;
 | 
						|
			ychg += rxrx2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------------------------------------------------
 | 
						|
static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
 | 
						|
{
 | 
						|
    // upper right
 | 
						|
    if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color);
 | 
						|
    // upper left
 | 
						|
    if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color);
 | 
						|
    // lower right
 | 
						|
    if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color);
 | 
						|
    // lower left
 | 
						|
    if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color);
 | 
						|
}
 | 
						|
 | 
						|
//=====================================================================================================
 | 
						|
void mgos_ili9341_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
 | 
						|
{
 | 
						|
	x0 += dispWin.x1;
 | 
						|
	y0 += dispWin.y1;
 | 
						|
 | 
						|
	uint16_t x, y;
 | 
						|
	int32_t xchg, ychg;
 | 
						|
	int32_t err;
 | 
						|
	int32_t rxrx2;
 | 
						|
	int32_t ryry2;
 | 
						|
	int32_t stopx, stopy;
 | 
						|
 | 
						|
	rxrx2 = rx;
 | 
						|
	rxrx2 *= rx;
 | 
						|
	rxrx2 *= 2;
 | 
						|
 | 
						|
	ryry2 = ry;
 | 
						|
	ryry2 *= ry;
 | 
						|
	ryry2 *= 2;
 | 
						|
 | 
						|
	x = rx;
 | 
						|
	y = 0;
 | 
						|
 | 
						|
	xchg = 1;
 | 
						|
	xchg -= rx;
 | 
						|
	xchg -= rx;
 | 
						|
	xchg *= ry;
 | 
						|
	xchg *= ry;
 | 
						|
 | 
						|
	ychg = rx;
 | 
						|
	ychg *= rx;
 | 
						|
 | 
						|
	err = 0;
 | 
						|
 | 
						|
	stopx = ryry2;
 | 
						|
	stopx *= rx;
 | 
						|
	stopy = 0;
 | 
						|
 | 
						|
	while( stopx >= stopy ) {
 | 
						|
		_draw_filled_ellipse_section(x, y, x0, y0, color, option);
 | 
						|
		y++;
 | 
						|
		stopy += rxrx2;
 | 
						|
		err += ychg;
 | 
						|
		ychg += rxrx2;
 | 
						|
		if ( 2*err+xchg > 0 ) {
 | 
						|
			x--;
 | 
						|
			stopx -= ryry2;
 | 
						|
			err += xchg;
 | 
						|
			xchg += ryry2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	x = 0;
 | 
						|
	y = ry;
 | 
						|
 | 
						|
	xchg = ry;
 | 
						|
	xchg *= ry;
 | 
						|
 | 
						|
	ychg = 1;
 | 
						|
	ychg -= ry;
 | 
						|
	ychg -= ry;
 | 
						|
	ychg *= rx;
 | 
						|
	ychg *= rx;
 | 
						|
 | 
						|
	err = 0;
 | 
						|
 | 
						|
	stopx = 0;
 | 
						|
 | 
						|
	stopy = rxrx2;
 | 
						|
	stopy *= ry;
 | 
						|
 | 
						|
	while( stopx <= stopy ) {
 | 
						|
		_draw_filled_ellipse_section(x, y, x0, y0, color, option);
 | 
						|
		x++;
 | 
						|
		stopx += ryry2;
 | 
						|
		err += xchg;
 | 
						|
		xchg += ryry2;
 | 
						|
		if ( 2*err+ychg > 0 ) {
 | 
						|
			y--;
 | 
						|
			stopy -= rxrx2;
 | 
						|
			err += ychg;
 | 
						|
			ychg += rxrx2;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// ==== ARC DRAWING ===================================================================
 | 
						|
 | 
						|
//---------------------------------------------------------------------------------------------------------------------------------
 | 
						|
static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color)
 | 
						|
{
 | 
						|
	//float sslope = (float)cos_lookup(start) / (float)sin_lookup(start);
 | 
						|
	//float eslope = (float)cos_lookup(end) / (float)sin_lookup(end);
 | 
						|
	float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ;
 | 
						|
	float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax);
 | 
						|
 | 
						|
	if (end == 360) eslope = -1000000;
 | 
						|
 | 
						|
	int ir2 = (radius - thickness) * (radius - thickness);
 | 
						|
	int or2 = radius * radius;
 | 
						|
 | 
						|
	disp_select();
 | 
						|
	for (int x = -radius; x <= radius; x++) {
 | 
						|
		for (int y = -radius; y <= radius; y++) {
 | 
						|
			int x2 = x * x;
 | 
						|
			int y2 = y * y;
 | 
						|
 | 
						|
			if (
 | 
						|
				(x2 + y2 < or2 && x2 + y2 >= ir2) &&
 | 
						|
				(
 | 
						|
				(y > 0 && start < 180 && x <= y * sslope) ||
 | 
						|
				(y < 0 && start > 180 && x >= y * sslope) ||
 | 
						|
				(y < 0 && start <= 180) ||
 | 
						|
				(y == 0 && start <= 180 && x < 0) ||
 | 
						|
				(y == 0 && start == 0 && x > 0)
 | 
						|
				) &&
 | 
						|
				(
 | 
						|
				(y > 0 && end < 180 && x >= y * eslope) ||
 | 
						|
				(y < 0 && end > 180 && x <= y * eslope) ||
 | 
						|
				(y > 0 && end >= 180) ||
 | 
						|
				(y == 0 && end >= 180 && x < 0) ||
 | 
						|
				(y == 0 && start == 0 && x > 0)
 | 
						|
				)
 | 
						|
				)
 | 
						|
				_drawPixel(cx+x, cy+y, color, 0);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	disp_deselect();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//===========================================================================================================================
 | 
						|
void mgos_ili9341_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor)
 | 
						|
{
 | 
						|
	cx += dispWin.x1;
 | 
						|
	cy += dispWin.y1;
 | 
						|
 | 
						|
	if (th < 1) th = 1;
 | 
						|
	if (th > r) th = r;
 | 
						|
 | 
						|
	int f = mgos_ili9341_compare_colors(fillcolor, color);
 | 
						|
 | 
						|
	float astart = fmodf(start, _arcAngleMax);
 | 
						|
	float aend = fmodf(end, _arcAngleMax);
 | 
						|
 | 
						|
	astart += _angleOffset;
 | 
						|
	aend += _angleOffset;
 | 
						|
 | 
						|
	if (astart < 0) astart += (float)360;
 | 
						|
	if (aend < 0) aend += (float)360;
 | 
						|
 | 
						|
	if (aend == 0) aend = (float)360;
 | 
						|
 | 
						|
	if (astart > aend) {
 | 
						|
		_fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor);
 | 
						|
		_fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor);
 | 
						|
		if (f) {
 | 
						|
			_fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color);
 | 
						|
			_fillArcOffsetted(cx, cy, r, 1, 0, aend, color);
 | 
						|
			_fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color);
 | 
						|
			_fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		_fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor);
 | 
						|
		if (f) {
 | 
						|
			_fillArcOffsetted(cx, cy, r, 1, astart, aend, color);
 | 
						|
			_fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (f) {
 | 
						|
		_drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD),
 | 
						|
			cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color);
 | 
						|
		_drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD),
 | 
						|
			cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
//=============================================================================================================
 | 
						|
void mgos_ili9341_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th)
 | 
						|
{
 | 
						|
	cx += dispWin.x1;
 | 
						|
	cy += dispWin.y1;
 | 
						|
 | 
						|
	int deg = rot - _angleOffset;
 | 
						|
	int f = mgos_ili9341_compare_colors(fill, color);
 | 
						|
 | 
						|
	if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES;	// This ensures the minimum side number
 | 
						|
	if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES;	// This ensures the maximum side number
 | 
						|
 | 
						|
	int Xpoints[sides], Ypoints[sides];							// Set the arrays based on the number of sides entered
 | 
						|
	int rads = 360 / sides;										// This equally spaces the points.
 | 
						|
 | 
						|
	for (int idx = 0; idx < sides; idx++) {
 | 
						|
		Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter;
 | 
						|
		Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter;
 | 
						|
	}
 | 
						|
 | 
						|
	// Draw the polygon on the screen.
 | 
						|
	if (f) {
 | 
						|
		for(int idx = 0; idx < sides; idx++) {
 | 
						|
			if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill);
 | 
						|
			else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (th) {
 | 
						|
		for (int n=0; n<th; n++) {
 | 
						|
			if (n > 0) {
 | 
						|
				for (int idx = 0; idx < sides; idx++) {
 | 
						|
					Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
 | 
						|
					Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			for(int idx = 0; idx < sides; idx++) {
 | 
						|
				if( (idx+1) < sides)
 | 
						|
					_drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines
 | 
						|
				else
 | 
						|
					_drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon.
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
// Similar to the Polygon function.
 | 
						|
//=====================================================================================
 | 
						|
void mgos_ili9341_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor)
 | 
						|
{
 | 
						|
	cx += dispWin.x1;
 | 
						|
	cy += dispWin.y1;
 | 
						|
 | 
						|
	factor = constrain(factor, 1.0, 4.0);
 | 
						|
	uint8_t sides = 5;
 | 
						|
	uint8_t rads = 360 / sides;
 | 
						|
 | 
						|
	int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5];
 | 
						|
 | 
						|
	for(int idx = 0; idx < sides; idx++) {
 | 
						|
		// makes the outer points
 | 
						|
		Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter;
 | 
						|
		Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter;
 | 
						|
		// makes the inner points
 | 
						|
		Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
 | 
						|
		// 36 is half of 72, and this will allow the inner and outer points to line up like a triangle.
 | 
						|
		Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
 | 
						|
	}
 | 
						|
 | 
						|
	for(int idx = 0; idx < sides; idx++) {
 | 
						|
		if((idx+1) < sides) {
 | 
						|
			if(fill) {// this part below should be self explanatory. It fills in the star.
 | 
						|
				_fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
 | 
						|
				_fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				_drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
 | 
						|
				_drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			if(fill) {
 | 
						|
				_fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
 | 
						|
				_fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				_drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
 | 
						|
				_drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
// ================ Font and string functions ==================================
 | 
						|
 | 
						|
//--------------------------------------------------------
 | 
						|
static int load_file_font(const char * fontfile, int info)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
 | 
						|
	if (userfont != NULL) {
 | 
						|
		free(userfont);
 | 
						|
		userfont = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
    struct stat sb;
 | 
						|
 | 
						|
    // Open the file
 | 
						|
    FILE *fhndl = fopen(fontfile, "r");
 | 
						|
    if (!fhndl) {
 | 
						|
    	LOG(LL_ERROR, ("Error opening font file '%s'", fontfile));
 | 
						|
		err = 1;
 | 
						|
		goto exit;
 | 
						|
    }
 | 
						|
 | 
						|
	// Get file size
 | 
						|
    if (stat(fontfile, &sb) != 0) {
 | 
						|
    	LOG(LL_ERROR, ("Error getting font file size"));
 | 
						|
		err = 2;
 | 
						|
		goto exit;
 | 
						|
    }
 | 
						|
	int fsize = sb.st_size;
 | 
						|
	if (fsize < 30) {
 | 
						|
		LOG(LL_ERROR, ("Error getting font file size"));
 | 
						|
		err = 3;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	userfont = malloc(fsize+4);
 | 
						|
	if (userfont == NULL) {
 | 
						|
		LOG(LL_ERROR, ("Font memory allocation error"));
 | 
						|
		fclose(fhndl);
 | 
						|
		err = 4;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	int read = fread(userfont, 1, fsize, fhndl);
 | 
						|
 | 
						|
	fclose(fhndl);
 | 
						|
 | 
						|
	if (read != fsize) {
 | 
						|
		LOG(LL_ERROR, ("Font read error"));
 | 
						|
		err = 5;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	userfont[read] = 0;
 | 
						|
	if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) {
 | 
						|
		LOG(LL_ERROR, ("Font ID not found"));
 | 
						|
		err = 6;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	// Check size
 | 
						|
	int size = 0;
 | 
						|
	int numchar = 0;
 | 
						|
	int width = userfont[0];
 | 
						|
	int height = userfont[1];
 | 
						|
	uint8_t first = 255;
 | 
						|
	uint8_t last = 0;
 | 
						|
	//int offst = 0;
 | 
						|
	int pminwidth = 255;
 | 
						|
	int pmaxwidth = 0;
 | 
						|
 | 
						|
	if (width != 0) {
 | 
						|
		// Fixed font
 | 
						|
		numchar = userfont[3];
 | 
						|
		first = userfont[2];
 | 
						|
		last = first + numchar - 1;
 | 
						|
		size = ((width * height * numchar) / 8) + 4;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		// Proportional font
 | 
						|
		size = 4; // point at first char data
 | 
						|
		uint8_t charCode;
 | 
						|
		int charwidth;
 | 
						|
 | 
						|
		do {
 | 
						|
		    charCode = userfont[size];
 | 
						|
		    charwidth = userfont[size+2];
 | 
						|
 | 
						|
		    if (charCode != 0xFF) {
 | 
						|
		    	numchar++;
 | 
						|
		    	if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7);
 | 
						|
		    	else size += 6;
 | 
						|
 | 
						|
		    	if (info) {
 | 
						|
	    			if (charwidth > pmaxwidth) pmaxwidth = charwidth;
 | 
						|
	    			if (charwidth < pminwidth) pminwidth = charwidth;
 | 
						|
	    			if (charCode < first) first = charCode;
 | 
						|
	    			if (charCode > last) last = charCode;
 | 
						|
	    		}
 | 
						|
		    }
 | 
						|
		    else size++;
 | 
						|
		  } while ((size < (read-8)) && (charCode != 0xFF));
 | 
						|
	}
 | 
						|
 | 
						|
	if (size != (read-8)) {
 | 
						|
		LOG(LL_ERROR, ("Font size error: found %d expected %d)", size, (read-8)));
 | 
						|
		err = 7;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	if (info) {
 | 
						|
		if (width != 0) {
 | 
						|
			LOG(LL_INFO, ("Fixed width font size: %d  width: %d  height: %d  characters: %d (%d~%d)",
 | 
						|
					size, width, height, numchar, first, last));
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			LOG(LL_INFO, ("Proportional font size: %d  width: %d~%d  height: %d  characters: %d (%d~%d)",
 | 
						|
					size, pminwidth, pmaxwidth, height, numchar, first, last));
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
exit:
 | 
						|
	if (err) {
 | 
						|
		if (userfont) {
 | 
						|
			free(userfont);
 | 
						|
			userfont = NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
//------------------------------------------------
 | 
						|
int compile_font_file(char *fontfile, uint8_t dbg)
 | 
						|
{
 | 
						|
	int err = 0;
 | 
						|
	char outfile[128] = {'\0'};
 | 
						|
	size_t len;
 | 
						|
    struct stat sb;
 | 
						|
    FILE *ffd = NULL;
 | 
						|
    FILE *ffd_out = NULL;
 | 
						|
    char *sourcebuf = NULL;
 | 
						|
 | 
						|
    len = strlen(fontfile);
 | 
						|
 | 
						|
	// check here that filename end with ".c".
 | 
						|
	if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) {
 | 
						|
		LOG(LL_ERROR, ("not a .c file"));
 | 
						|
		err = 1;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	sprintf(outfile, "%s", fontfile);
 | 
						|
	sprintf(outfile+strlen(outfile)-1, "fon");
 | 
						|
 | 
						|
	// Open the source file
 | 
						|
    if (stat(fontfile, &sb) != 0) {
 | 
						|
    	LOG(LL_ERROR, ("Error opening source file '%s'", fontfile));
 | 
						|
    	err = 2;
 | 
						|
		goto exit;
 | 
						|
    }
 | 
						|
    // Open the file
 | 
						|
    ffd = fopen(fontfile, "rb");
 | 
						|
    if (!ffd) {
 | 
						|
    	LOG(LL_ERROR, ("Error opening source file '%s'", fontfile));
 | 
						|
    	err = 3;
 | 
						|
		goto exit;
 | 
						|
    }
 | 
						|
 | 
						|
	// Open the font file
 | 
						|
    ffd_out= fopen(outfile, "wb");
 | 
						|
	if (!ffd_out) {
 | 
						|
		LOG(LL_ERROR, ("error opening destination file"));
 | 
						|
		err = 4;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	// Get file size
 | 
						|
	int fsize = sb.st_size;
 | 
						|
	if (fsize <= 0) {
 | 
						|
		LOG(LL_ERROR, ("source file size error"));
 | 
						|
		err = 5;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	sourcebuf = malloc(fsize+4);
 | 
						|
	if (sourcebuf == NULL) {
 | 
						|
		LOG(LL_ERROR, ("memory allocation error"));
 | 
						|
		err = 6;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
	char *fbuf = sourcebuf;
 | 
						|
 | 
						|
	int rdsize = fread(fbuf, 1, fsize, ffd);
 | 
						|
	fclose(ffd);
 | 
						|
	ffd = NULL;
 | 
						|
 | 
						|
	if (rdsize != fsize) {
 | 
						|
		LOG(LL_ERROR, ("error reading from source file"));
 | 
						|
		err = 7;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	*(fbuf+rdsize) = '\0';
 | 
						|
 | 
						|
	fbuf = strchr(fbuf, '{');			// beginning of font data
 | 
						|
	char *fend = strstr(fbuf, "};");	// end of font data
 | 
						|
 | 
						|
	if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) {
 | 
						|
		LOG(LL_ERROR, ("wrong source file format"));
 | 
						|
		err = 8;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	fbuf++;
 | 
						|
	*fend = '\0';
 | 
						|
	char hexstr[5] = {'\0'};
 | 
						|
	int lastline = 0;
 | 
						|
 | 
						|
	fbuf = strstr(fbuf, "0x");
 | 
						|
	int size = 0;
 | 
						|
	char *nextline;
 | 
						|
	char *numptr;
 | 
						|
 | 
						|
	int bptr = 0;
 | 
						|
 | 
						|
	while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) {
 | 
						|
		nextline = strchr(fbuf, '\n'); // beginning of the next line
 | 
						|
		if (nextline == NULL) {
 | 
						|
			nextline = fend-1;
 | 
						|
			lastline++;
 | 
						|
		}
 | 
						|
		else nextline++;
 | 
						|
 | 
						|
		while (fbuf < nextline) {
 | 
						|
			numptr = strstr(fbuf, "0x");
 | 
						|
			if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X");
 | 
						|
			if ((numptr != NULL) && ((numptr+4) <= nextline)) {
 | 
						|
				fbuf = numptr;
 | 
						|
				if (bptr >= 128) {
 | 
						|
					// buffer full, write to file
 | 
						|
                    if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error;
 | 
						|
					bptr = 0;
 | 
						|
					size += 128;
 | 
						|
				}
 | 
						|
				memcpy(hexstr, fbuf, 4);
 | 
						|
				hexstr[4] = 0;
 | 
						|
				outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0);
 | 
						|
				fbuf += 4;
 | 
						|
			}
 | 
						|
			else fbuf = nextline;
 | 
						|
		}
 | 
						|
		fbuf = nextline;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bptr > 0) {
 | 
						|
		size += bptr;
 | 
						|
        if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	// write font ID
 | 
						|
	sprintf(outfile, "RPH_font");
 | 
						|
    if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error;
 | 
						|
 | 
						|
    fclose(ffd_out);
 | 
						|
    ffd_out = NULL;
 | 
						|
 | 
						|
	// === Test compiled font ===
 | 
						|
	sprintf(outfile, "%s", fontfile);
 | 
						|
	sprintf(outfile+strlen(outfile)-1, "fon");
 | 
						|
 | 
						|
	uint8_t *uf = userfont; // save userfont pointer
 | 
						|
	userfont = NULL;
 | 
						|
	if (load_file_font(outfile, 1) != 0) {
 | 
						|
		LOG(LL_ERROR, ("Error compiling file!"));
 | 
						|
		err = 10;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		free(userfont);
 | 
						|
		LOG(LL_ERROR, ("File compiled successfully."));
 | 
						|
	}
 | 
						|
	userfont = uf; // restore userfont
 | 
						|
 | 
						|
	goto exit;
 | 
						|
 | 
						|
error:
 | 
						|
	LOG(LL_ERROR, ("error writing to destination file"));
 | 
						|
	err = 9;
 | 
						|
 | 
						|
exit:
 | 
						|
	if (sourcebuf) free(sourcebuf);
 | 
						|
	if (ffd) fclose(ffd);
 | 
						|
	if (ffd_out) fclose(ffd_out);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -----------------------------------------------------------------------------------------
 | 
						|
// Individual Proportional Font Character Format:
 | 
						|
// -----------------------------------------------------------------------------------------
 | 
						|
// Character Code
 | 
						|
// yOffset				(start Y of visible pixels)
 | 
						|
// Width				(width of the visible pixels)
 | 
						|
// Height				(height of the visible pixels)
 | 
						|
// xOffset				(start X of visible pixels)
 | 
						|
// xDelta				(the distance to move the cursor. Effective width of the character.)
 | 
						|
// Data[n]
 | 
						|
// -----------------------------------------------------------------------------------------
 | 
						|
 | 
						|
//---------------------------------------------------------------------------------------------
 | 
						|
// Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1)
 | 
						|
// Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
 | 
						|
//---------------------------------------------------------------------------------------------
 | 
						|
 | 
						|
//----------------------------------
 | 
						|
void getFontCharacters(uint8_t *buf)
 | 
						|
{
 | 
						|
    if (cfont.bitmap == 2) {
 | 
						|
    	//For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
 | 
						|
		for (uint8_t n=0; n < 11; n++) {
 | 
						|
			buf[n] = n + 0x30;
 | 
						|
		}
 | 
						|
		buf[11] = '.';
 | 
						|
		buf[12] = '-';
 | 
						|
		buf[13] = '/';
 | 
						|
		buf[14] = '\0';
 | 
						|
    	return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (cfont.x_size > 0) {
 | 
						|
		for (uint8_t n=0; n < cfont.numchars; n++) {
 | 
						|
			buf[n] = cfont.offset + n;
 | 
						|
		}
 | 
						|
		buf[cfont.numchars] = '\0';
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	uint16_t tempPtr = 4; // point at first char data
 | 
						|
	uint8_t cc, cw, ch, n;
 | 
						|
 | 
						|
	n = 0;
 | 
						|
    cc = cfont.font[tempPtr++];
 | 
						|
    while (cc != 0xFF)  {
 | 
						|
    	cfont.numchars++;
 | 
						|
        tempPtr++;
 | 
						|
        cw = cfont.font[tempPtr++];
 | 
						|
        ch = cfont.font[tempPtr++];
 | 
						|
        tempPtr++;
 | 
						|
        tempPtr++;
 | 
						|
		if (cw != 0) {
 | 
						|
			// packed bits
 | 
						|
			tempPtr += (((cw * ch)-1) / 8) + 1;
 | 
						|
		}
 | 
						|
		buf[n++] = cc;
 | 
						|
	    cc = cfont.font[tempPtr++];
 | 
						|
	}
 | 
						|
	buf[n] = '\0';
 | 
						|
}
 | 
						|
 | 
						|
// Set max width & height of the proportional font
 | 
						|
//-----------------------------
 | 
						|
static void getMaxWidthHeight()
 | 
						|
{
 | 
						|
	uint16_t tempPtr = 4; // point at first char data
 | 
						|
	uint8_t cc, cw, ch, cd, cy;
 | 
						|
 | 
						|
	cfont.numchars = 0;
 | 
						|
	cfont.max_x_size = 0;
 | 
						|
 | 
						|
    cc = cfont.font[tempPtr++];
 | 
						|
    while (cc != 0xFF)  {
 | 
						|
    	cfont.numchars++;
 | 
						|
        cy = cfont.font[tempPtr++];
 | 
						|
        cw = cfont.font[tempPtr++];
 | 
						|
        ch = cfont.font[tempPtr++];
 | 
						|
        tempPtr++;
 | 
						|
        cd = cfont.font[tempPtr++];
 | 
						|
        cy += ch;
 | 
						|
		if (cw > cfont.max_x_size) cfont.max_x_size = cw;
 | 
						|
		if (cd > cfont.max_x_size) cfont.max_x_size = cd;
 | 
						|
		if (ch > cfont.y_size) cfont.y_size = ch;
 | 
						|
		if (cy > cfont.y_size) cfont.y_size = cy;
 | 
						|
		if (cw != 0) {
 | 
						|
			// packed bits
 | 
						|
			tempPtr += (((cw * ch)-1) / 8) + 1;
 | 
						|
		}
 | 
						|
	    cc = cfont.font[tempPtr++];
 | 
						|
	}
 | 
						|
    cfont.size = tempPtr;
 | 
						|
}
 | 
						|
 | 
						|
// Return the Glyph data for an individual character in the proportional font
 | 
						|
//------------------------------------
 | 
						|
static uint8_t getCharPtr(uint8_t c) {
 | 
						|
  uint16_t tempPtr = 4; // point at first char data
 | 
						|
 | 
						|
  do {
 | 
						|
	fontChar.charCode = cfont.font[tempPtr++];
 | 
						|
    if (fontChar.charCode == 0xFF) return 0;
 | 
						|
 | 
						|
    fontChar.adjYOffset = cfont.font[tempPtr++];
 | 
						|
    fontChar.width = cfont.font[tempPtr++];
 | 
						|
    fontChar.height = cfont.font[tempPtr++];
 | 
						|
    fontChar.xOffset = cfont.font[tempPtr++];
 | 
						|
    fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
 | 
						|
    fontChar.xDelta = cfont.font[tempPtr++];
 | 
						|
 | 
						|
    if (c != fontChar.charCode && fontChar.charCode != 0xFF) {
 | 
						|
      if (fontChar.width != 0) {
 | 
						|
        // packed bits
 | 
						|
        tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF));
 | 
						|
 | 
						|
  fontChar.dataPtr = tempPtr;
 | 
						|
  if (c == fontChar.charCode) {
 | 
						|
    if (font_forceFixed > 0) {
 | 
						|
      // fix width & offset for forced fixed width
 | 
						|
      fontChar.xDelta = cfont.max_x_size;
 | 
						|
      fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else return 0;
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
//-----------------------
 | 
						|
static void _testFont() {
 | 
						|
  if (cfont.x_size) {
 | 
						|
	  printf("FONT TEST: fixed font\r\n");
 | 
						|
	  return;
 | 
						|
  }
 | 
						|
  uint16_t tempPtr = 4; // point at first char data
 | 
						|
  uint8_t c = 0x20;
 | 
						|
  for (c=0x20; c <0xFF; c++) {
 | 
						|
	fontChar.charCode = cfont.font[tempPtr++];
 | 
						|
    if (fontChar.charCode == 0xFF) break;
 | 
						|
    if (fontChar.charCode != c) {
 | 
						|
    	printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c);
 | 
						|
    	break;
 | 
						|
    }
 | 
						|
    c = fontChar.charCode;
 | 
						|
    fontChar.adjYOffset = cfont.font[tempPtr++];
 | 
						|
    fontChar.width = cfont.font[tempPtr++];
 | 
						|
    fontChar.height = cfont.font[tempPtr++];
 | 
						|
    fontChar.xOffset = cfont.font[tempPtr++];
 | 
						|
    fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
 | 
						|
    fontChar.xDelta = cfont.font[tempPtr++];
 | 
						|
 | 
						|
    if (fontChar.charCode != 0xFF) {
 | 
						|
      if (fontChar.width != 0) {
 | 
						|
        // packed bits
 | 
						|
        tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  printf("FONT TEST: W=%d  H=%d last char: %d [%c]; length: %d\r\n", cfont.max_x_size, cfont.y_size, c, c, tempPtr);
 | 
						|
}
 | 
						|
*/
 | 
						|
 | 
						|
//===================================================
 | 
						|
void mgos_ili9341_setFont(uint8_t font, const char *font_file)
 | 
						|
{
 | 
						|
  cfont.font = NULL;
 | 
						|
 | 
						|
  if (font == FONT_7SEG) {
 | 
						|
    cfont.bitmap = 2;
 | 
						|
    cfont.x_size = 24;
 | 
						|
    cfont.y_size = 6;
 | 
						|
    cfont.offset = 0;
 | 
						|
    cfont.color  = _fg;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
	  if (font == USER_FONT) {
 | 
						|
		  if (load_file_font(font_file, 0) != 0) cfont.font = tft_DefaultFont;
 | 
						|
		  else cfont.font = userfont;
 | 
						|
	  }
 | 
						|
	  else if (font == DEJAVU18_FONT) cfont.font = tft_Dejavu18;
 | 
						|
	  else if (font == DEJAVU24_FONT) cfont.font = tft_Dejavu24;
 | 
						|
	  else if (font == UBUNTU16_FONT) cfont.font = tft_Ubuntu16;
 | 
						|
	  else if (font == COMIC24_FONT) cfont.font = tft_Comic24;
 | 
						|
	  else if (font == MINYA24_FONT) cfont.font = tft_minya24;
 | 
						|
	  else if (font == TOONEY32_FONT) cfont.font = tft_tooney32;
 | 
						|
	  else if (font == SMALL_FONT) cfont.font = tft_SmallFont;
 | 
						|
	  else if (font == DEF_SMALL_FONT) cfont.font = tft_def_small;
 | 
						|
	  else cfont.font = tft_DefaultFont;
 | 
						|
 | 
						|
	  cfont.bitmap = 1;
 | 
						|
	  cfont.x_size = cfont.font[0];
 | 
						|
	  cfont.y_size = cfont.font[1];
 | 
						|
	  if (cfont.x_size > 0) {
 | 
						|
		  cfont.offset = cfont.font[2];
 | 
						|
		  cfont.numchars = cfont.font[3];
 | 
						|
		  cfont.size = cfont.x_size * cfont.y_size * cfont.numchars;
 | 
						|
	  }
 | 
						|
	  else {
 | 
						|
		  cfont.offset = 4;
 | 
						|
		  getMaxWidthHeight();
 | 
						|
	  }
 | 
						|
	  //_testFont();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void mgos_ili9341_setFontColor(color_t color)
 | 
						|
{
 | 
						|
    cfont.color = color;
 | 
						|
}
 | 
						|
 | 
						|
// -----------------------------------------------------------------------------------------
 | 
						|
// Individual Proportional Font Character Format:
 | 
						|
// -----------------------------------------------------------------------------------------
 | 
						|
// Character Code
 | 
						|
// yOffset				(start Y of visible pixels)
 | 
						|
// Width				(width of the visible pixels)
 | 
						|
// Height				(height of the visible pixels)
 | 
						|
// xOffset				(start X of visible pixels)
 | 
						|
// xDelta				(the distance to move the cursor. Effective width of the character.)
 | 
						|
// Data[n]
 | 
						|
// -----------------------------------------------------------------------------------------
 | 
						|
//---------------------------------------------------------------------------------------------
 | 
						|
// Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1)
 | 
						|
// Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
 | 
						|
//---------------------------------------------------------------------------------------------
 | 
						|
 | 
						|
// print non-rotated proportional character
 | 
						|
// character is already in fontChar
 | 
						|
//----------------------------------------------
 | 
						|
static int printProportionalChar(int x, int y) {
 | 
						|
	uint8_t ch = 0;
 | 
						|
	int i, j, char_width;
 | 
						|
 | 
						|
	char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta);
 | 
						|
 | 
						|
	if ((font_buffered_char) && (!font_transparent)) {
 | 
						|
		int len, bufPos;
 | 
						|
 | 
						|
		// === buffer Glyph data for faster sending ===
 | 
						|
		len = char_width * cfont.y_size;
 | 
						|
		color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
 | 
						|
		if (color_line) {
 | 
						|
			// fill with background color
 | 
						|
			for (int n = 0; n < len; n++) {
 | 
						|
				color_line[n] = _bg;
 | 
						|
			}
 | 
						|
			// set character pixels to foreground color
 | 
						|
			uint8_t mask = 0x80;
 | 
						|
			for (j=0; j < fontChar.height; j++) {
 | 
						|
				for (i=0; i < fontChar.width; i++) {
 | 
						|
					if (((i + (j*fontChar.width)) % 8) == 0) {
 | 
						|
						mask = 0x80;
 | 
						|
						ch = cfont.font[fontChar.dataPtr++];
 | 
						|
					}
 | 
						|
					if ((ch & mask) != 0) {
 | 
						|
						// visible pixel
 | 
						|
						bufPos = ((j + fontChar.adjYOffset) * char_width) + (fontChar.xOffset + i);  // bufY + bufX
 | 
						|
						color_line[bufPos] = _fg;
 | 
						|
						/*
 | 
						|
						bufY = (j + fontChar.adjYOffset) * char_width;
 | 
						|
						bufX = fontChar.xOffset + i;
 | 
						|
						if ((bufX < 0) || (bufX > char_width)) {
 | 
						|
							printf("[%c] X ERR: %d\r\n", fontChar.charCode, bufX);
 | 
						|
						}
 | 
						|
						bufPos = bufY + bufX;
 | 
						|
						if ((bufPos < len) && (bufPos > 0)) color_line[bufPos] = _fg;
 | 
						|
						else printf("[%c] ERR: %d > %d  W=%d  H=%d  bufX=%d  bufY=%d  X=%d  Y=%d\r\n",
 | 
						|
								fontChar.charCode, bufPos, len, char_width, cfont.y_size, bufX, bufY, fontChar.xOffset + i, j + fontChar.adjYOffset);
 | 
						|
						*/
 | 
						|
					}
 | 
						|
					mask >>= 1;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			// send to display in one transaction
 | 
						|
			disp_select();
 | 
						|
			send_data(x, y, x+char_width-1, y+cfont.y_size-1, len, color_line);
 | 
						|
			disp_deselect();
 | 
						|
			free(color_line);
 | 
						|
 | 
						|
			return char_width;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	int cx, cy;
 | 
						|
 | 
						|
	if (!font_transparent) _fillRect(x, y, char_width+1, cfont.y_size, _bg);
 | 
						|
 | 
						|
	// draw Glyph
 | 
						|
	uint8_t mask = 0x80;
 | 
						|
	disp_select();
 | 
						|
	for (j=0; j < fontChar.height; j++) {
 | 
						|
		for (i=0; i < fontChar.width; i++) {
 | 
						|
			if (((i + (j*fontChar.width)) % 8) == 0) {
 | 
						|
				mask = 0x80;
 | 
						|
				ch = cfont.font[fontChar.dataPtr++];
 | 
						|
			}
 | 
						|
 | 
						|
			if ((ch & mask) !=0) {
 | 
						|
				cx = (uint16_t)(x+fontChar.xOffset+i);
 | 
						|
				cy = (uint16_t)(y+j+fontChar.adjYOffset);
 | 
						|
				_drawPixel(cx, cy, _fg, 0);
 | 
						|
			}
 | 
						|
			mask >>= 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	disp_deselect();
 | 
						|
 | 
						|
	return char_width;
 | 
						|
}
 | 
						|
 | 
						|
// non-rotated fixed width character
 | 
						|
//----------------------------------------------
 | 
						|
static void printChar(uint8_t c, int x, int y) {
 | 
						|
	uint8_t i, j, ch, fz, mask;
 | 
						|
	uint16_t k, temp, cx, cy, len;
 | 
						|
 | 
						|
	// fz = bytes per char row
 | 
						|
	fz = cfont.x_size/8;
 | 
						|
	if (cfont.x_size % 8) fz++;
 | 
						|
 | 
						|
	// get character position in buffer
 | 
						|
	temp = ((c-cfont.offset)*((fz)*cfont.y_size))+4;
 | 
						|
 | 
						|
	if ((font_buffered_char) && (!font_transparent)) {
 | 
						|
		// === buffer Glyph data for faster sending ===
 | 
						|
		len = cfont.x_size * cfont.y_size;
 | 
						|
		color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
 | 
						|
		if (color_line) {
 | 
						|
			// fill with background color
 | 
						|
			for (int n = 0; n < len; n++) {
 | 
						|
				color_line[n] = _bg;
 | 
						|
			}
 | 
						|
			// set character pixels to foreground color
 | 
						|
			for (j=0; j<cfont.y_size; j++) {
 | 
						|
				for (k=0; k < fz; k++) {
 | 
						|
					ch = cfont.font[temp+k];
 | 
						|
					mask=0x80;
 | 
						|
					for (i=0; i<8; i++) {
 | 
						|
						if ((ch & mask) !=0) color_line[(j*cfont.x_size) + (i+(k*8))] = _fg;
 | 
						|
						mask >>= 1;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				temp += (fz);
 | 
						|
			}
 | 
						|
			// send to display in one transaction
 | 
						|
			disp_select();
 | 
						|
			send_data(x, y, x+cfont.x_size-1, y+cfont.y_size-1, len, color_line);
 | 
						|
			disp_deselect();
 | 
						|
			free(color_line);
 | 
						|
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (!font_transparent) _fillRect(x, y, cfont.x_size, cfont.y_size, _bg);
 | 
						|
 | 
						|
	disp_select();
 | 
						|
	for (j=0; j<cfont.y_size; j++) {
 | 
						|
		for (k=0; k < fz; k++) {
 | 
						|
			ch = cfont.font[temp+k];
 | 
						|
			mask=0x80;
 | 
						|
			for (i=0; i<8; i++) {
 | 
						|
				if ((ch & mask) !=0) {
 | 
						|
					cx = (uint16_t)(x+i+(k*8));
 | 
						|
					cy = (uint16_t)(y+j);
 | 
						|
					_drawPixel(cx, cy, _fg, 0);
 | 
						|
				}
 | 
						|
				mask >>= 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		temp += (fz);
 | 
						|
	}
 | 
						|
	disp_deselect();
 | 
						|
}
 | 
						|
 | 
						|
// print rotated proportional character
 | 
						|
// character is already in fontChar
 | 
						|
//---------------------------------------------------
 | 
						|
static int rotatePropChar(int x, int y, int offset) {
 | 
						|
  uint8_t ch = 0;
 | 
						|
  double radian = font_rotate * DEG_TO_RAD;
 | 
						|
  float cos_radian = cos(radian);
 | 
						|
  float sin_radian = sin(radian);
 | 
						|
 | 
						|
  uint8_t mask = 0x80;
 | 
						|
  disp_select();
 | 
						|
  for (int j=0; j < fontChar.height; j++) {
 | 
						|
    for (int i=0; i < fontChar.width; i++) {
 | 
						|
      if (((i + (j*fontChar.width)) % 8) == 0) {
 | 
						|
        mask = 0x80;
 | 
						|
        ch = cfont.font[fontChar.dataPtr++];
 | 
						|
      }
 | 
						|
 | 
						|
      int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian)));
 | 
						|
      int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian)));
 | 
						|
 | 
						|
      if ((ch & mask) != 0) _drawPixel(newX,newY,_fg, 0);
 | 
						|
      else if (!font_transparent) _drawPixel(newX,newY,_bg, 0);
 | 
						|
 | 
						|
      mask >>= 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  disp_deselect();
 | 
						|
 | 
						|
  return fontChar.xDelta+1;
 | 
						|
}
 | 
						|
 | 
						|
// rotated fixed width character
 | 
						|
//--------------------------------------------------------
 | 
						|
static void rotateChar(uint8_t c, int x, int y, int pos) {
 | 
						|
  uint8_t i,j,ch,fz,mask;
 | 
						|
  uint16_t temp;
 | 
						|
  int newx,newy;
 | 
						|
  double radian = font_rotate*0.0175;
 | 
						|
  float cos_radian = cos(radian);
 | 
						|
  float sin_radian = sin(radian);
 | 
						|
  int zz;
 | 
						|
 | 
						|
  if( cfont.x_size < 8 ) fz = cfont.x_size;
 | 
						|
  else fz = cfont.x_size/8;
 | 
						|
  temp=((c-cfont.offset)*((fz)*cfont.y_size))+4;
 | 
						|
 | 
						|
  disp_select();
 | 
						|
  for (j=0; j<cfont.y_size; j++) {
 | 
						|
    for (zz=0; zz<(fz); zz++) {
 | 
						|
      ch = cfont.font[temp+zz];
 | 
						|
      mask = 0x80;
 | 
						|
      for (i=0; i<8; i++) {
 | 
						|
        newx=(int)(x+(((i+(zz*8)+(pos*cfont.x_size))*cos_radian)-((j)*sin_radian)));
 | 
						|
        newy=(int)(y+(((j)*cos_radian)+((i+(zz*8)+(pos*cfont.x_size))*sin_radian)));
 | 
						|
 | 
						|
        if ((ch & mask) != 0) _drawPixel(newx,newy,_fg, 0);
 | 
						|
        else if (!font_transparent) _drawPixel(newx,newy,_bg, 0);
 | 
						|
        mask >>= 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    temp+=(fz);
 | 
						|
  }
 | 
						|
  disp_deselect();
 | 
						|
  // calculate x,y for the next char
 | 
						|
  TFT_X = (int)(x + ((pos+1) * cfont.x_size * cos_radian));
 | 
						|
  TFT_Y = (int)(y + ((pos+1) * cfont.x_size * sin_radian));
 | 
						|
}
 | 
						|
 | 
						|
//----------------------
 | 
						|
static int _7seg_width()
 | 
						|
{
 | 
						|
	return (2 * (2 * cfont.y_size + 1)) + cfont.x_size;
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------
 | 
						|
static int _7seg_height()
 | 
						|
{
 | 
						|
	return (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size);
 | 
						|
}
 | 
						|
 | 
						|
// Returns the string width in pixels.
 | 
						|
// Useful for positions strings on the screen.
 | 
						|
//===============================
 | 
						|
int mgos_ili9341_getStringWidth(const char* const str)
 | 
						|
{
 | 
						|
    int strWidth = 0;
 | 
						|
 | 
						|
	if (cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2;	// 7-segment font
 | 
						|
	else if (cfont.x_size != 0) strWidth = strlen(str) * cfont.x_size;			// fixed width font
 | 
						|
	else {
 | 
						|
		// calculate the width of the string of proportional characters
 | 
						|
		const char *tempStrptr = str;
 | 
						|
		while (*tempStrptr != 0) {
 | 
						|
			if (getCharPtr(*tempStrptr++)) {
 | 
						|
				strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		strWidth--;
 | 
						|
	}
 | 
						|
	return strWidth;
 | 
						|
}
 | 
						|
 | 
						|
//===============================================
 | 
						|
void mgos_ili9341_clearStringRect(int x, int y, char *str)
 | 
						|
{
 | 
						|
	int w = mgos_ili9341_getStringWidth(str);
 | 
						|
	int h = mgos_ili9341_getfontheight();
 | 
						|
	mgos_ili9341_fillRect(x+dispWin.x1, y+dispWin.y1, w, h, _bg);
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
/**
 | 
						|
 * bit-encoded bar position of all digits' bcd segments
 | 
						|
 *
 | 
						|
 *           6
 | 
						|
 * 		  +-----+
 | 
						|
 * 		3 |  .	| 2
 | 
						|
 * 		  +--5--+
 | 
						|
 * 		1 |  .	| 0
 | 
						|
 * 		  +--.--+
 | 
						|
 * 		     4
 | 
						|
 */
 | 
						|
static const uint16_t font_bcd[] = {
 | 
						|
  0x200, // 0010 0000 0000  // -
 | 
						|
  0x080, // 0000 1000 0000  // .
 | 
						|
  0x06C, // 0100 0110 1100  // /, degree
 | 
						|
  0x05f, // 0000 0101 1111, // 0
 | 
						|
  0x005, // 0000 0000 0101, // 1
 | 
						|
  0x076, // 0000 0111 0110, // 2
 | 
						|
  0x075, // 0000 0111 0101, // 3
 | 
						|
  0x02d, // 0000 0010 1101, // 4
 | 
						|
  0x079, // 0000 0111 1001, // 5
 | 
						|
  0x07b, // 0000 0111 1011, // 6
 | 
						|
  0x045, // 0000 0100 0101, // 7
 | 
						|
  0x07f, // 0000 0111 1111, // 8
 | 
						|
  0x07d, // 0000 0111 1101  // 9
 | 
						|
  0x900  // 1001 0000 0000  // :
 | 
						|
};
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------------------------
 | 
						|
static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
 | 
						|
  _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color);
 | 
						|
  _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color);
 | 
						|
  _fillRect(x, y+2*w+1, 2*w+1, l, color);
 | 
						|
  if (cfont.offset) {
 | 
						|
    _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline);
 | 
						|
    _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline);
 | 
						|
    _drawRect(x, y+2*w+1, 2*w+1, l, outline);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//----------------------------------------------------------------------------------------------
 | 
						|
static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
 | 
						|
  _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color);
 | 
						|
  _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color);
 | 
						|
  _fillRect(x+2*w+1, y, l, 2*w+1, color);
 | 
						|
  if (cfont.offset) {
 | 
						|
    _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline);
 | 
						|
    _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline);
 | 
						|
    _drawRect(x+2*w+1, y, l, 2*w+1, outline);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//--------------------------------------------------------------------------------------------
 | 
						|
static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) {
 | 
						|
  /* TODO: clipping */
 | 
						|
  if (num < 0x2D || num > 0x3A) return;
 | 
						|
 | 
						|
  int16_t c = font_bcd[num-0x2D];
 | 
						|
  int16_t d = 2*w+l+1;
 | 
						|
 | 
						|
  // === Clear unused segments ===
 | 
						|
  if (!(c & 0x001)) barVert(x+d, y+d, w, l, _bg, _bg);
 | 
						|
  if (!(c & 0x002)) barVert(x,   y+d, w, l, _bg, _bg);
 | 
						|
  if (!(c & 0x004)) barVert(x+d, y, w, l, _bg, _bg);
 | 
						|
  if (!(c & 0x008)) barVert(x,   y, w, l, _bg, _bg);
 | 
						|
  if (!(c & 0x010)) barHor(x, y+2*d, w, l, _bg, _bg);
 | 
						|
  if (!(c & 0x020)) barHor(x, y+d, w, l, _bg, _bg);
 | 
						|
  if (!(c & 0x040)) barHor(x, y, w, l, _bg, _bg);
 | 
						|
 | 
						|
  if (!(c & 0x080)) {
 | 
						|
    // low point
 | 
						|
    _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
 | 
						|
    if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
 | 
						|
  }
 | 
						|
  if (!(c & 0x100)) {
 | 
						|
    // down middle point
 | 
						|
    _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
 | 
						|
    if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
 | 
						|
  }
 | 
						|
  if (!(c & 0x800)) {
 | 
						|
	// up middle point
 | 
						|
    _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
 | 
						|
    if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
 | 
						|
  }
 | 
						|
  if (!(c & 0x200)) {
 | 
						|
    // middle, minus
 | 
						|
    _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg);
 | 
						|
    if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg);
 | 
						|
  }
 | 
						|
 | 
						|
  // === Draw used segments ===
 | 
						|
  if (c & 0x001) barVert(x+d, y+d, w, l, color, cfont.color);	// down right
 | 
						|
  if (c & 0x002) barVert(x,   y+d, w, l, color, cfont.color);	// down left
 | 
						|
  if (c & 0x004) barVert(x+d, y, w, l, color, cfont.color);		// up right
 | 
						|
  if (c & 0x008) barVert(x,   y, w, l, color, cfont.color);		// up left
 | 
						|
  if (c & 0x010) barHor(x, y+2*d, w, l, color, cfont.color);	// down
 | 
						|
  if (c & 0x020) barHor(x, y+d, w, l, color, cfont.color);		// middle
 | 
						|
  if (c & 0x040) barHor(x, y, w, l, color, cfont.color);		// up
 | 
						|
 | 
						|
  if (c & 0x080) {
 | 
						|
    // low point
 | 
						|
    _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color);
 | 
						|
    if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, cfont.color);
 | 
						|
  }
 | 
						|
  if (c & 0x100) {
 | 
						|
    // down middle point
 | 
						|
    _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color);
 | 
						|
    if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, cfont.color);
 | 
						|
  }
 | 
						|
  if (c & 0x800) {
 | 
						|
	// up middle point
 | 
						|
    _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color);
 | 
						|
    if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, cfont.color);
 | 
						|
  }
 | 
						|
  if (c & 0x200) {
 | 
						|
    // middle, minus
 | 
						|
    _fillRect(x+2*w+1, y+d, l, 2*w+1, color);
 | 
						|
    if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, cfont.color);
 | 
						|
  }
 | 
						|
}
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
//======================================
 | 
						|
void mgos_ili9341_print(const char * const st, int x, int y) {
 | 
						|
	int stl, i, tmpw, tmph, fh;
 | 
						|
	uint8_t ch;
 | 
						|
 | 
						|
	if (cfont.bitmap == 0) return; // wrong font selected
 | 
						|
 | 
						|
	// ** Rotated strings cannot be aligned
 | 
						|
	if ((font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return;
 | 
						|
 | 
						|
	if ((x < LASTX) || (font_rotate == 0)) TFT_OFFSET = 0;
 | 
						|
 | 
						|
	if ((x >= LASTX) && (x < LASTY)) x = TFT_X + (x-LASTX);
 | 
						|
	else if (x > CENTER) x += dispWin.x1;
 | 
						|
 | 
						|
	if (y >= LASTY) y = TFT_Y + (y-LASTY);
 | 
						|
	else if (y > CENTER) y += dispWin.y1;
 | 
						|
 | 
						|
	// ** Get number of characters in string to print
 | 
						|
	stl = strlen(st);
 | 
						|
 | 
						|
	// ** Calculate CENTER, RIGHT or BOTTOM position
 | 
						|
	tmpw = mgos_ili9341_getStringWidth(st);	// string width in pixels
 | 
						|
	fh = cfont.y_size;			// font height
 | 
						|
	if ((cfont.x_size != 0) && (cfont.bitmap == 2)) {
 | 
						|
		// 7-segment font
 | 
						|
		fh = (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size);  // 7-seg character height
 | 
						|
	}
 | 
						|
 | 
						|
	if (x == RIGHT) x = dispWin.x2 - tmpw + dispWin.x1;
 | 
						|
	else if (x == CENTER) x = (((dispWin.x2 - dispWin.x1 + 1) - tmpw) / 2) + dispWin.x1;
 | 
						|
 | 
						|
	if (y == BOTTOM) y = dispWin.y2 - fh + dispWin.y1;
 | 
						|
	else if (y==CENTER) y = (((dispWin.y2 - dispWin.y1 + 1) - (fh/2)) / 2) + dispWin.y1;
 | 
						|
 | 
						|
	if (x < dispWin.x1) x = dispWin.x1;
 | 
						|
	if (y < dispWin.y1) y = dispWin.y1;
 | 
						|
	if ((x > dispWin.x2) || (y > dispWin.y2)) return;
 | 
						|
 | 
						|
	TFT_X = x;
 | 
						|
	TFT_Y = y;
 | 
						|
 | 
						|
	// ** Adjust y position
 | 
						|
	tmph = cfont.y_size; // font height
 | 
						|
	// for non-proportional fonts, char width is the same for all chars
 | 
						|
	tmpw = cfont.x_size;
 | 
						|
	if (cfont.x_size != 0) {
 | 
						|
		if (cfont.bitmap == 2) {	// 7-segment font
 | 
						|
			tmpw = _7seg_width();	// character width
 | 
						|
			tmph = _7seg_height();	// character height
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else TFT_OFFSET = 0;	// fixed font; offset not needed
 | 
						|
 | 
						|
	if ((TFT_Y + tmph - 1) > dispWin.y2) return;
 | 
						|
 | 
						|
	int offset = TFT_OFFSET;
 | 
						|
 | 
						|
	for (i=0; i<stl; i++) {
 | 
						|
		ch = st[i]; // get string character
 | 
						|
 | 
						|
		if (ch == 0x0D) { // === '\r', erase to eol ====
 | 
						|
			if ((!font_transparent) && (font_rotate==0)) _fillRect(TFT_X, TFT_Y,  dispWin.x2+1-TFT_X, tmph, _bg);
 | 
						|
		}
 | 
						|
 | 
						|
		else if (ch == 0x0A) { // ==== '\n', new line ====
 | 
						|
			if (cfont.bitmap == 1) {
 | 
						|
				TFT_Y += tmph + font_line_space;
 | 
						|
				if (TFT_Y > (dispWin.y2-tmph)) break;
 | 
						|
				TFT_X = dispWin.x1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		else { // ==== other characters ====
 | 
						|
			if (cfont.x_size == 0) {
 | 
						|
				// for proportional font get character data to 'fontChar'
 | 
						|
				if (getCharPtr(ch)) tmpw = fontChar.xDelta;
 | 
						|
				else continue;
 | 
						|
			}
 | 
						|
 | 
						|
			// check if character can be displayed in the current line
 | 
						|
			if ((TFT_X+tmpw) > (dispWin.x2)) {
 | 
						|
				if (text_wrap == 0) break;
 | 
						|
				TFT_Y += tmph + font_line_space;
 | 
						|
				if (TFT_Y > (dispWin.y2-tmph)) break;
 | 
						|
				TFT_X = dispWin.x1;
 | 
						|
			}
 | 
						|
 | 
						|
			// Let's print the character
 | 
						|
			if (cfont.x_size == 0) {
 | 
						|
				// == proportional font
 | 
						|
				if (font_rotate == 0) TFT_X += printProportionalChar(TFT_X, TFT_Y) + 1;
 | 
						|
				else {
 | 
						|
					// rotated proportional font
 | 
						|
					offset += rotatePropChar(x, y, offset);
 | 
						|
					TFT_OFFSET = offset;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				if (cfont.bitmap == 1) {
 | 
						|
					// == fixed font
 | 
						|
					if ((ch < cfont.offset) || ((ch-cfont.offset) > cfont.numchars)) ch = cfont.offset;
 | 
						|
					if (font_rotate == 0) {
 | 
						|
						printChar(ch, TFT_X, TFT_Y);
 | 
						|
						TFT_X += tmpw;
 | 
						|
					}
 | 
						|
					else rotateChar(ch, x, y, i);
 | 
						|
				}
 | 
						|
				else if (cfont.bitmap == 2) {
 | 
						|
					// == 7-segment font ==
 | 
						|
					_draw7seg(TFT_X, TFT_Y, ch, cfont.y_size, cfont.x_size, _fg);
 | 
						|
					TFT_X += (tmpw + 2);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// ================ Service functions ==========================================
 | 
						|
 | 
						|
// Change the screen rotation.
 | 
						|
// Input: m new rotation value (0 to 3)
 | 
						|
//=================================
 | 
						|
void mgos_ili9341_setRotation(uint8_t rot) {
 | 
						|
    if (rot > 3) {
 | 
						|
        uint8_t madctl = (rot & 0xF8); // for testing, manually set MADCTL register
 | 
						|
		if (disp_select() == ESP_OK) {
 | 
						|
			disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
 | 
						|
			disp_deselect();
 | 
						|
		}
 | 
						|
    }
 | 
						|
	else {
 | 
						|
		orientation = rot;
 | 
						|
        _tft_setRotation(rot);
 | 
						|
	}
 | 
						|
 | 
						|
	dispWin.x1 = 0;
 | 
						|
	dispWin.y1 = 0;
 | 
						|
	dispWin.x2 = _width-1;
 | 
						|
	dispWin.y2 = _height-1;
 | 
						|
 | 
						|
	mgos_ili9341_fillScreen(_bg);
 | 
						|
}
 | 
						|
 | 
						|
// Send the command to invert all of the colors.
 | 
						|
// Input: i 0 to disable inversion; non-zero to enable inversion
 | 
						|
//==========================================
 | 
						|
void mgos_ili9341_invertDisplay(const uint8_t mode) {
 | 
						|
  if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN);
 | 
						|
  else disp_spi_transfer_cmd(TFT_INVOFF);
 | 
						|
}
 | 
						|
 | 
						|
// Select gamma curve
 | 
						|
// Input: gamma = 0~3
 | 
						|
//==================================
 | 
						|
void mgos_ili9341_setGammaCurve(uint8_t gm) {
 | 
						|
  uint8_t gamma_curve = 1 << (gm & 0x03);
 | 
						|
  disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1);
 | 
						|
}
 | 
						|
 | 
						|
//===========================================================
 | 
						|
color_t HSBtoRGB(float _hue, float _sat, float _brightness) {
 | 
						|
 float red = 0.0;
 | 
						|
 float green = 0.0;
 | 
						|
 float blue = 0.0;
 | 
						|
 | 
						|
 if (_sat == 0.0) {
 | 
						|
   red = _brightness;
 | 
						|
   green = _brightness;
 | 
						|
   blue = _brightness;
 | 
						|
 } else {
 | 
						|
   if (_hue == 360.0) {
 | 
						|
     _hue = 0;
 | 
						|
   }
 | 
						|
 | 
						|
   int slice = (int)(_hue / 60.0);
 | 
						|
   float hue_frac = (_hue / 60.0) - slice;
 | 
						|
 | 
						|
   float aa = _brightness * (1.0 - _sat);
 | 
						|
   float bb = _brightness * (1.0 - _sat * hue_frac);
 | 
						|
   float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac));
 | 
						|
 | 
						|
   switch(slice) {
 | 
						|
     case 0:
 | 
						|
         red = _brightness;
 | 
						|
         green = cc;
 | 
						|
         blue = aa;
 | 
						|
         break;
 | 
						|
     case 1:
 | 
						|
         red = bb;
 | 
						|
         green = _brightness;
 | 
						|
         blue = aa;
 | 
						|
         break;
 | 
						|
     case 2:
 | 
						|
         red = aa;
 | 
						|
         green = _brightness;
 | 
						|
         blue = cc;
 | 
						|
         break;
 | 
						|
     case 3:
 | 
						|
         red = aa;
 | 
						|
         green = bb;
 | 
						|
         blue = _brightness;
 | 
						|
         break;
 | 
						|
     case 4:
 | 
						|
         red = cc;
 | 
						|
         green = aa;
 | 
						|
         blue = _brightness;
 | 
						|
         break;
 | 
						|
     case 5:
 | 
						|
         red = _brightness;
 | 
						|
         green = aa;
 | 
						|
         blue = bb;
 | 
						|
         break;
 | 
						|
     default:
 | 
						|
         red = 0.0;
 | 
						|
         green = 0.0;
 | 
						|
         blue = 0.0;
 | 
						|
         break;
 | 
						|
   }
 | 
						|
 }
 | 
						|
 | 
						|
 color_t color;
 | 
						|
 color.r = ((uint8_t)(red * 255.0)) & 0xFC;
 | 
						|
 color.g = ((uint8_t)(green * 255.0)) & 0xFC;
 | 
						|
 color.b = ((uint8_t)(blue * 255.0)) & 0xFC;
 | 
						|
 | 
						|
 return color;
 | 
						|
}
 | 
						|
//=====================================================================
 | 
						|
void mgos_ili9341_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
 | 
						|
{
 | 
						|
	dispWin.x1 = x1;
 | 
						|
	dispWin.y1 = y1;
 | 
						|
	dispWin.x2 = x2;
 | 
						|
	dispWin.y2 = y2;
 | 
						|
 | 
						|
	if (dispWin.x2 >= _width) dispWin.x2 = _width-1;
 | 
						|
	if (dispWin.y2 >= _height) dispWin.y2 = _height-1;
 | 
						|
	if (dispWin.x1 > dispWin.x2) dispWin.x1 = dispWin.x2;
 | 
						|
	if (dispWin.y1 > dispWin.y2) dispWin.y1 = dispWin.y2;
 | 
						|
}
 | 
						|
 | 
						|
//=====================
 | 
						|
void mgos_ili9341_resetclipwin()
 | 
						|
{
 | 
						|
	dispWin.x2 = _width-1;
 | 
						|
	dispWin.y2 = _height-1;
 | 
						|
	dispWin.x1 = 0;
 | 
						|
	dispWin.y1 = 0;
 | 
						|
}
 | 
						|
 | 
						|
//==========================================================================
 | 
						|
void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) {
 | 
						|
	if (cfont.bitmap != 2) return;
 | 
						|
 | 
						|
	if (l < 6) l = 6;
 | 
						|
    if (l > 40) l = 40;
 | 
						|
    if (w < 1) w = 1;
 | 
						|
    if (w > (l/2)) w = l/2;
 | 
						|
    if (w > 12) w = 12;
 | 
						|
 | 
						|
    cfont.x_size = l;
 | 
						|
	cfont.y_size = w;
 | 
						|
	cfont.offset = outline;
 | 
						|
	cfont.color  = color;
 | 
						|
}
 | 
						|
 | 
						|
//==========================================
 | 
						|
int mgos_ili9341_getfontsize(int *width, int* height)
 | 
						|
{
 | 
						|
  if (cfont.bitmap == 1) {
 | 
						|
    if (cfont.x_size != 0) *width = cfont.x_size;	// fixed width font
 | 
						|
    else *width = cfont.max_x_size;					// proportional font
 | 
						|
    *height = cfont.y_size;
 | 
						|
  }
 | 
						|
  else if (cfont.bitmap == 2) {
 | 
						|
	// 7-segment font
 | 
						|
    *width = _7seg_width();
 | 
						|
    *height = _7seg_height();
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    *width = 0;
 | 
						|
    *height = 0;
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
//=====================
 | 
						|
int mgos_ili9341_getfontheight()
 | 
						|
{
 | 
						|
  if (cfont.bitmap == 1) return cfont.y_size;			// Bitmap font
 | 
						|
  else if (cfont.bitmap == 2) return _7seg_height();	// 7-segment font
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
//====================
 | 
						|
void mgos_ili9341_saveClipWin()
 | 
						|
{
 | 
						|
	dispWinTemp.x1 = dispWin.x1;
 | 
						|
	dispWinTemp.y1 = dispWin.y1;
 | 
						|
	dispWinTemp.x2 = dispWin.x2;
 | 
						|
	dispWinTemp.y2 = dispWin.y2;
 | 
						|
}
 | 
						|
 | 
						|
//=======================
 | 
						|
void mgos_ili9341_restoreClipWin()
 | 
						|
{
 | 
						|
	dispWin.x1 = dispWinTemp.x1;
 | 
						|
	dispWin.y1 = dispWinTemp.y1;
 | 
						|
	dispWin.x2 = dispWinTemp.x2;
 | 
						|
	dispWin.y2 = dispWinTemp.y2;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int mgos_ili9341_png(int x, int y, char *fname)
 | 
						|
{
 | 
						|
  upng_t* upng;
 | 
						|
  const uint8_t *png_buf;
 | 
						|
  uint16_t i,j;
 | 
						|
  color_t pixel;
 | 
						|
 | 
						|
  int ret = -1;
 | 
						|
 | 
						|
  if (!(upng = upng_new_from_file(fname))) {
 | 
						|
    LOG(LL_ERROR, ("Can't read %s", fname));
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
 | 
						|
  upng_decode(upng);
 | 
						|
  if (upng_get_error(upng) != UPNG_EOK) {
 | 
						|
    LOG(LL_ERROR, ("PNG decode error"));
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  if (upng_get_format(upng) != UPNG_RGB8) {
 | 
						|
    LOG(LL_ERROR, ("PNG is not in RGB8 format"));
 | 
						|
    goto exit;
 | 
						|
  }
 | 
						|
  // Do stuff with upng data
 | 
						|
  LOG(LL_INFO, ("%s: w=%d h=%d size=%d bpp=%d bitdepth=%d pixelsize=%d", fname, upng_get_width(upng), upng_get_height(upng), upng_get_size(upng), upng_get_bpp(upng), upng_get_bitdepth(upng), upng_get_pixelsize(upng)));
 | 
						|
 | 
						|
  png_buf = upng_get_buffer(upng);
 | 
						|
  disp_select();
 | 
						|
  for(j=0; j<upng_get_height(upng); j++) {
 | 
						|
    for(i=0; i<upng_get_width(upng); i++) {
 | 
						|
      pixel.r = *png_buf++;
 | 
						|
      pixel.g = *png_buf++;
 | 
						|
      pixel.b = *png_buf++;
 | 
						|
      mgos_ili9341_drawPixel(i, j, pixel, 0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  disp_deselect();
 | 
						|
  
 | 
						|
 | 
						|
exit:
 | 
						|
  if (upng) upng_free(upng);
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void mgos_ili9341_set_fg(const color_t *color)
 | 
						|
{
 | 
						|
	_fg = *color;
 | 
						|
//	memcpy(_fg, color, 3);
 | 
						|
}
 | 
						|
 | 
						|
void mgos_ili9341_set_bg(const color_t *color)
 | 
						|
{
 | 
						|
	_bg = *color;
 | 
						|
//	memcpy(_bg, color, 3);
 | 
						|
}
 | 
						|
 | 
						|
color_t *mgos_ili9341_get_fg(void)
 | 
						|
{
 | 
						|
	return &_fg;
 | 
						|
}
 | 
						|
 | 
						|
color_t *mgos_ili9341_get_bg(void)
 | 
						|
{
 | 
						|
	return &_bg;
 | 
						|
}
 | 
						|
 | 
						|
// Mongoose-OS init
 | 
						|
//
 | 
						|
bool mgos_ili9341_init(void) {
 | 
						|
  esp_err_t ret;
 | 
						|
 | 
						|
  max_rdclock = 8000000;
 | 
						|
 | 
						|
  gpio_pad_select_gpio(mgos_sys_config_get_ili9341_cs_pin());
 | 
						|
  gpio_set_direction(mgos_sys_config_get_ili9341_cs_pin(), GPIO_MODE_OUTPUT);
 | 
						|
 | 
						|
  gpio_pad_select_gpio(mgos_sys_config_get_ili9341_dc_pin());
 | 
						|
  gpio_set_direction(mgos_sys_config_get_ili9341_dc_pin(), GPIO_MODE_OUTPUT);
 | 
						|
  gpio_set_level(mgos_sys_config_get_ili9341_dc_pin(), 0);
 | 
						|
 | 
						|
  // Add SPI TFT Screen (ILI9341)
 | 
						|
  spi_lobo_device_handle_t spi;
 | 
						|
 | 
						|
  spi_lobo_bus_config_t buscfg={
 | 
						|
    .miso_io_num=mgos_sys_config_get_spi_miso(),
 | 
						|
    .mosi_io_num=mgos_sys_config_get_spi_mosi(),
 | 
						|
    .sclk_io_num=mgos_sys_config_get_spi_sck(),
 | 
						|
    .quadwp_io_num=-1,
 | 
						|
    .quadhd_io_num=-1,
 | 
						|
    .max_transfer_sz = 6*1024,
 | 
						|
  };
 | 
						|
 | 
						|
  spi_lobo_device_interface_config_t devcfg={
 | 
						|
    .clock_speed_hz=DEFAULT_SPI_CLOCK,
 | 
						|
    .mode=0,
 | 
						|
    .spics_io_num=-1,                       // we will use external CS pin
 | 
						|
    .spics_ext_io_num=mgos_sys_config_get_ili9341_cs_pin(),
 | 
						|
    .flags=SPI_DEVICE_HALFDUPLEX,
 | 
						|
  };
 | 
						|
 | 
						|
  ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &devcfg, &spi);
 | 
						|
  assert(ret==ESP_OK);
 | 
						|
  disp_spi = spi;
 | 
						|
 | 
						|
  ret = spi_lobo_device_select(spi, 1);
 | 
						|
  assert(ret==ESP_OK);
 | 
						|
  ret = spi_lobo_device_deselect(spi);
 | 
						|
  assert(ret==ESP_OK);
 | 
						|
 | 
						|
  mgos_ili9341_display_init();
 | 
						|
 | 
						|
  max_rdclock = find_rd_speed();
 | 
						|
  LOG(LL_INFO, ("SPI init ok (cs=%d, speed=%u, rdspeed=%u)", mgos_sys_config_get_ili9341_cs_pin(), spi_lobo_get_speed(spi), max_rdclock));
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 |