3 * Author: LoBo (loboris@gmail.com, loboris.github)
5 * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers
12 #include "freertos/FreeRTOS.h"
15 #include "esp32/rom/tjpgd.h"
18 #define DEG_TO_RAD 0.01745329252
19 #define deg_to_rad 0.01745329252 + 3.14159265359
20 #define swap(a, b) { int16_t t = a; a = b; b = t; }
23 #define max(A,B) ( (A) > (B) ? (A):(B))
26 #define min(A,B) ( (A) < (B) ? (A):(B))
30 extern uint8_t tft_SmallFont[];
31 extern uint8_t tft_DefaultFont[];
32 extern uint8_t tft_Dejavu18[];
33 extern uint8_t tft_Dejavu24[];
34 extern uint8_t tft_Ubuntu16[];
35 extern uint8_t tft_Comic24[];
36 extern uint8_t tft_minya24[];
37 extern uint8_t tft_tooney32[];
38 extern uint8_t tft_def_small[];
40 // ==== Color definitions constants ==============
41 const color_t TFT_BLACK = { 0, 0, 0 };
42 const color_t TFT_NAVY = { 0, 0, 128 };
43 const color_t TFT_DARKGREEN = { 0, 128, 0 };
44 const color_t TFT_DARKCYAN = { 0, 128, 128 };
45 const color_t TFT_MAROON = { 128, 0, 0 };
46 const color_t TFT_PURPLE = { 128, 0, 128 };
47 const color_t TFT_OLIVE = { 128, 128, 0 };
48 const color_t TFT_LIGHTGREY = { 192, 192, 192 };
49 const color_t TFT_DARKGREY = { 128, 128, 128 };
50 const color_t TFT_BLUE = { 0, 0, 255 };
51 const color_t TFT_GREEN = { 0, 255, 0 };
52 const color_t TFT_CYAN = { 0, 255, 255 };
53 const color_t TFT_RED = { 252, 0, 0 };
54 const color_t TFT_MAGENTA = { 252, 0, 255 };
55 const color_t TFT_YELLOW = { 252, 252, 0 };
56 const color_t TFT_WHITE = { 252, 252, 252 };
57 const color_t TFT_ORANGE = { 252, 164, 0 };
58 const color_t TFT_GREENYELLOW = { 172, 252, 44 };
59 const color_t TFT_PINK = { 252, 192, 202 };
60 // ===============================================
62 // ==============================================================
63 // ==== Set default values of global variables ==================
64 uint8_t tft_orientation = LANDSCAPE;// screen tft_orientation
65 uint16_t tft_font_rotate = 0; // font rotation
66 uint8_t tft_font_transparent = 0;
67 uint8_t tft_font_forceFixed = 0;
68 uint8_t tft_text_wrap = 0; // character wrapping to new line
69 color_t tft_fg = { 0, 255, 0};
70 color_t tft_bg = { 0, 0, 0};
71 uint8_t tft_image_debug = 0;
73 float tft_angleOffset = DEFAULT_ANGLE_OFFSET;
78 uint32_t tft_tp_calx = 7472920;
79 uint32_t tft_tp_caly = 122224794;
81 dispWin_t tft_dispWin = {
82 .x1 = TFT_STATIC_WIDTH_OFFSET,
83 .y1 = TFT_STATIC_HEIGHT_OFFSET,
84 .x2 = DEFAULT_TFT_DISPLAY_WIDTH + TFT_STATIC_WIDTH_OFFSET,
85 .y2 = DEFAULT_TFT_DISPLAY_HEIGHT + TFT_STATIC_HEIGHT_OFFSET,
89 .font = tft_DefaultFont,
97 uint8_t tft_font_buffered_char = 1;
98 uint8_t tft_font_line_space = 0;
99 // ==============================================================
112 static dispWin_t dispWinTemp;
114 static uint8_t *userfont = NULL;
115 static int TFT_OFFSET = 0;
116 static propFont fontChar;
117 static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX;
120 // =========================================================================
121 // ** All drawings are clipped to 'tft_dispWin' **
122 // ** All x,y coordinates in public functions are relative to clip window **
123 // =========== : Public functions
124 // ----------- : Local functions
125 // =========================================================================
128 // Compare two colors; return 0 if equal
129 //============================================
130 int TFT_compare_colors(color_t c1, color_t c2)
132 if ((c1.r & 0xFC) != (c2.r & 0xFC)) return 1;
133 if ((c1.g & 0xFC) != (c2.g & 0xFC)) return 1;
134 if ((c1.b & 0xFC) != (c2.b & 0xFC)) return 1;
139 // draw color pixel on screen
140 //------------------------------------------------------------------------
141 static void _drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
143 if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
144 drawPixel(x, y, color, sel);
147 //====================================================================
148 void TFT_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
150 _drawPixel(x+tft_dispWin.x1, y+tft_dispWin.y1, color, sel);
153 //===========================================
154 color_t TFT_readPixel(int16_t x, int16_t y) {
156 if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return TFT_BLACK;
158 return readPixel(x, y);
161 //--------------------------------------------------------------------------
162 static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
164 if ((x < tft_dispWin.x1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
165 if (y < tft_dispWin.y1) {
166 h -= (tft_dispWin.y1 - y);
170 if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1;
172 TFT_pushColorRep(x, y, x, y+h-1, color, (uint32_t)h);
175 //--------------------------------------------------------------------------
176 static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
178 if ((y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
179 if (x < tft_dispWin.x1) {
180 w -= (tft_dispWin.x1 - x);
184 if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1;
187 TFT_pushColorRep(x, y, x+w-1, y, color, (uint32_t)w);
190 //======================================================================
191 void TFT_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
192 _drawFastVLine(x+tft_dispWin.x1, y+tft_dispWin.y1, h, color);
195 //======================================================================
196 void TFT_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
197 _drawFastHLine(x+tft_dispWin.x1, y+tft_dispWin.y1, w, color);
200 // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses
201 // the eficient FastH/V Line draw routine for segments of 2 pixels or more
202 //----------------------------------------------------------------------------------
203 static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
206 if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color);
207 else _drawFastVLine(x0, y1, y0-y1, color);
211 if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color);
212 else _drawFastHLine(x1, y0, x0-x1, color);
217 if (abs(y1 - y0) > abs(x1 - x0)) steep = 1;
227 int16_t dx = x1 - x0, dy = abs(y1 - y0);
228 int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
230 if (y0 < y1) ystep = 1;
232 // Split into steep and not steep for FastH/V separation
234 for (; x0 <= x1; x0++) {
239 if (dlen == 1) _drawPixel(y0, xs, color, 1);
240 else _drawFastVLine(y0, xs, dlen, color);
241 dlen = 0; y0 += ystep; xs = x0 + 1;
244 if (dlen) _drawFastVLine(y0, xs, dlen, color);
248 for (; x0 <= x1; x0++) {
253 if (dlen == 1) _drawPixel(xs, y0, color, 1);
254 else _drawFastHLine(xs, y0, dlen, color);
255 dlen = 0; y0 += ystep; xs = x0 + 1;
258 if (dlen) _drawFastHLine(xs, y0, dlen, color);
262 //==============================================================================
263 void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
265 _drawLine(x0+tft_dispWin.x1, y0+tft_dispWin.y1, x1+tft_dispWin.x1, y1+tft_dispWin.y1, color);
269 //--------------------------------------------------------------------------------
270 static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
272 if ((x >= tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
274 if (x < tft_dispWin.x1) {
275 w -= (tft_dispWin.x1 - x);
278 if (y < tft_dispWin.y1) {
279 h -= (tft_dispWin.y1 - y);
285 if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1;
286 if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1;
289 TFT_pushColorRep(x, y, x+w-1, y+h-1, color, (uint32_t)(h*w));
292 //============================================================================
293 void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
294 _fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, color);
297 //==================================
298 void TFT_fillScreen(color_t color) {
299 TFT_pushColorRep(TFT_STATIC_X_OFFSET, TFT_STATIC_Y_OFFSET, tft_width + TFT_STATIC_X_OFFSET -1, tft_height + TFT_STATIC_Y_OFFSET -1, color, (uint32_t)(tft_height*tft_width));
302 //==================================
303 void TFT_fillWindow(color_t color) {
304 TFT_pushColorRep(tft_dispWin.x1, tft_dispWin.y1, tft_dispWin.x2, tft_dispWin.y2,
305 color, (uint32_t)((tft_dispWin.x2-tft_dispWin.x1+1) * (tft_dispWin.y2-tft_dispWin.y1+1)));
308 // ^^^============= Basics drawing functions ================================^^^
311 // ================ Graphics drawing functions ==================================
313 //-----------------------------------------------------------------------------------
314 static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
315 _drawFastHLine(x1,y1,w, color);
316 _drawFastVLine(x1+w-1,y1,h, color);
317 _drawFastHLine(x1,y1+h-1,w, color);
318 _drawFastVLine(x1,y1,h, color);
321 //===============================================================================
322 void TFT_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
323 _drawRect(x1+tft_dispWin.x1, y1+tft_dispWin.y1, w, h, color);
326 //-------------------------------------------------------------------------------------------------
327 static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color)
331 int16_t ddF_y = -2 * r;
345 if (cornername & 0x4) {
346 _drawPixel(x0 + x, y0 + y, color, 0);
347 _drawPixel(x0 + y, y0 + x, color, 0);
349 if (cornername & 0x2) {
350 _drawPixel(x0 + x, y0 - y, color, 0);
351 _drawPixel(x0 + y, y0 - x, color, 0);
353 if (cornername & 0x8) {
354 _drawPixel(x0 - y, y0 + x, color, 0);
355 _drawPixel(x0 - x, y0 + y, color, 0);
357 if (cornername & 0x1) {
358 _drawPixel(x0 - y, y0 - x, color, 0);
359 _drawPixel(x0 - x, y0 - y, color, 0);
365 // Used to do circles and roundrects
366 //----------------------------------------------------------------------------------------------------------------
367 static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, color_t color)
371 int16_t ddF_y = -2 * r;
374 int16_t ylm = x0 - r;
378 if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
379 if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
389 if ((x0 - x) > ylm) {
390 if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
391 if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
396 // Draw a rounded rectangle
397 //=============================================================================================
398 void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
404 _drawFastHLine(x + r, y, w - 2 * r, color); // Top
405 _drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
406 _drawFastVLine(x, y + r, h - 2 * r, color); // Left
407 _drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
410 drawCircleHelper(x + r, y + r, r, 1, color);
411 drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
412 drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
413 drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
416 // Fill a rounded rectangle
417 //=============================================================================================
418 void TFT_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
424 _fillRect(x + r, y, w - 2 * r, h, color);
427 fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
428 fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
434 //-----------------------------------------------------------------------------------------------
435 static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color)
440 x + length * cos((angle + tft_angleOffset) * DEG_TO_RAD),
441 y + length * sin((angle + tft_angleOffset) * DEG_TO_RAD), color);
444 //---------------------------------------------------------------------------------------------------------------
445 static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color)
448 x + start * cos((angle + tft_angleOffset) * DEG_TO_RAD),
449 y + start * sin((angle + tft_angleOffset) * DEG_TO_RAD),
450 x + (start + length) * cos((angle + tft_angleOffset) * DEG_TO_RAD),
451 y + (start + length) * sin((angle + tft_angleOffset) * DEG_TO_RAD), color);
454 //===========================================================================================================
455 void TFT_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color)
460 if (start == 0) _drawLineByAngle(x, y, angle, len, color);
461 else _DrawLineByAngle(x, y, angle, start, len, color);
466 //--------------------------------------------------------------------------------------------------------------------
467 static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
469 _drawLine(x0, y0, x1, y1, color);
470 _drawLine(x1, y1, x2, y2, color);
471 _drawLine(x2, y2, x0, y0, color);
474 //================================================================================================================
475 void TFT_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
477 x0 += tft_dispWin.x1;
478 y0 += tft_dispWin.y1;
479 x1 += tft_dispWin.x1;
480 y1 += tft_dispWin.y1;
481 x2 += tft_dispWin.x1;
482 y2 += tft_dispWin.y1;
484 _drawLine(x0, y0, x1, y1, color);
485 _drawLine(x1, y1, x2, y2, color);
486 _drawLine(x2, y2, x0, y0, color);
490 //--------------------------------------------------------------------------------------------------------------------
491 static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
493 int16_t a, b, y, last;
495 // Sort coordinates by Y order (y2 >= y1 >= y0)
497 swap(y0, y1); swap(x0, x1);
500 swap(y2, y1); swap(x2, x1);
503 swap(y0, y1); swap(x0, x1);
506 if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
509 else if(x1 > b) b = x1;
511 else if(x2 > b) b = x2;
512 _drawFastHLine(a, y0, b-a+1, color);
527 // For upper part of triangle, find scanline crossings for segments
528 // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
529 // is included here (and second loop will be skipped, avoiding a /0
530 // error there), otherwise scanline y1 is skipped here and handled
531 // in the second loop...which also avoids a /0 error here if y0=y1
532 // (flat-topped triangle).
533 if(y1 == y2) last = y1; // Include y1 scanline
534 else last = y1-1; // Skip it
536 for(y=y0; y<=last; y++) {
542 a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
543 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
546 _drawFastHLine(a, y, b-a+1, color);
549 // For lower part of triangle, find scanline crossings for segments
550 // 0-2 and 1-2. This loop is skipped if y1=y2.
551 sa = dx12 * (y - y1);
552 sb = dx02 * (y - y0);
559 a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
560 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
563 _drawFastHLine(a, y, b-a+1, color);
567 //================================================================================================================
568 void TFT_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
571 x0 + tft_dispWin.x1, y0 + tft_dispWin.y1,
572 x1 + tft_dispWin.x1, y1 + tft_dispWin.y1,
573 x2 + tft_dispWin.x1, y2 + tft_dispWin.y1,
577 //====================================================================
578 void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color) {
583 int ddF_y = -2 * radius;
588 _drawPixel(x, y + radius, color, 0);
589 _drawPixel(x, y - radius, color, 0);
590 _drawPixel(x + radius, y, color, 0);
591 _drawPixel(x - radius, y, color, 0);
601 _drawPixel(x + x1, y + y1, color, 0);
602 _drawPixel(x - x1, y + y1, color, 0);
603 _drawPixel(x + x1, y - y1, color, 0);
604 _drawPixel(x - x1, y - y1, color, 0);
605 _drawPixel(x + y1, y + x1, color, 0);
606 _drawPixel(x - y1, y + x1, color, 0);
607 _drawPixel(x + y1, y - x1, color, 0);
608 _drawPixel(x - y1, y - x1, color, 0);
613 //====================================================================
614 void TFT_fillCircle(int16_t x, int16_t y, int radius, color_t color) {
618 _drawFastVLine(x, y-radius, 2*radius+1, color);
619 fillCircleHelper(x, y, radius, 3, 0, color);
622 //----------------------------------------------------------------------------------------------------------------
623 static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
627 if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color, 0);
629 if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color, 0);
631 if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color, 0);
633 if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color, 0);
637 //=====================================================================================================
638 void TFT_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
640 x0 += tft_dispWin.x1;
641 y0 += tft_dispWin.y1;
648 int32_t stopx, stopy;
676 while( stopx >= stopy ) {
677 _draw_ellipse_section(x, y, x0, y0, color, option);
682 if ( 2*err+xchg > 0 ) {
709 while( stopx <= stopy ) {
710 _draw_ellipse_section(x, y, x0, y0, color, option);
715 if ( 2*err+ychg > 0 ) {
724 //-----------------------------------------------------------------------------------------------------------------------
725 static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
728 if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color);
730 if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color);
732 if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color);
734 if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color);
737 //=====================================================================================================
738 void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
740 x0 += tft_dispWin.x1;
741 y0 += tft_dispWin.y1;
748 int32_t stopx, stopy;
776 while( stopx >= stopy ) {
777 _draw_filled_ellipse_section(x, y, x0, y0, color, option);
782 if ( 2*err+xchg > 0 ) {
809 while( stopx <= stopy ) {
810 _draw_filled_ellipse_section(x, y, x0, y0, color, option);
815 if ( 2*err+ychg > 0 ) {
825 // ==== ARC DRAWING ===================================================================
827 //---------------------------------------------------------------------------------------------------------------------------------
828 static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color)
830 //float sslope = (float)cos_lookup(start) / (float)sin_lookup(start);
831 //float eslope = (float)cos_lookup(end) / (float)sin_lookup(end);
832 float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ;
833 float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax);
835 if (end == 360) eslope = -1000000;
837 int ir2 = (radius - thickness) * (radius - thickness);
838 int or2 = radius * radius;
841 for (int x = -radius; x <= radius; x++) {
842 for (int y = -radius; y <= radius; y++) {
847 (x2 + y2 < or2 && x2 + y2 >= ir2) &&
849 (y > 0 && start < 180 && x <= y * sslope) ||
850 (y < 0 && start > 180 && x >= y * sslope) ||
851 (y < 0 && start <= 180) ||
852 (y == 0 && start <= 180 && x < 0) ||
853 (y == 0 && start == 0 && x > 0)
856 (y > 0 && end < 180 && x >= y * eslope) ||
857 (y < 0 && end > 180 && x <= y * eslope) ||
858 (y > 0 && end >= 180) ||
859 (y == 0 && end >= 180 && x < 0) ||
860 (y == 0 && start == 0 && x > 0)
863 _drawPixel(cx+x, cy+y, color, 0);
870 //===========================================================================================================================
871 void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor)
873 cx += tft_dispWin.x1;
874 cy += tft_dispWin.y1;
879 int f = TFT_compare_colors(fillcolor, color);
881 float astart = fmodf(start, _arcAngleMax);
882 float aend = fmodf(end, _arcAngleMax);
884 astart += tft_angleOffset;
885 aend += tft_angleOffset;
887 if (astart < 0) astart += (float)360;
888 if (aend < 0) aend += (float)360;
890 if (aend == 0) aend = (float)360;
893 _fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor);
894 _fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor);
896 _fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color);
897 _fillArcOffsetted(cx, cy, r, 1, 0, aend, color);
898 _fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color);
899 _fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color);
903 _fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor);
905 _fillArcOffsetted(cx, cy, r, 1, astart, aend, color);
906 _fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color);
910 _drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD),
911 cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color);
912 _drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD),
913 cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color);
917 //=============================================================================================================
918 void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th)
920 cx += tft_dispWin.x1;
921 cy += tft_dispWin.y1;
923 int deg = rot - tft_angleOffset;
924 int f = TFT_compare_colors(fill, color);
926 if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number
927 if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES; // This ensures the maximum side number
929 int Xpoints[sides], Ypoints[sides]; // Set the arrays based on the number of sides entered
930 int rads = 360 / sides; // This equally spaces the points.
932 for (int idx = 0; idx < sides; idx++) {
933 Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter;
934 Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter;
937 // Draw the polygon on the screen.
939 for(int idx = 0; idx < sides; idx++) {
940 if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill);
941 else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill);
946 for (int n=0; n<th; n++) {
948 for (int idx = 0; idx < sides; idx++) {
949 Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
950 Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
953 for(int idx = 0; idx < sides; idx++) {
955 _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines
957 _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon.
964 // Similar to the Polygon function.
965 //=====================================================================================
966 void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor)
968 cx += tft_dispWin.x1;
969 cy += tft_dispWin.y1;
971 factor = constrain(factor, 1.0, 4.0);
973 uint8_t rads = 360 / sides;
975 int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5];
977 for(int idx = 0; idx < sides; idx++) {
978 // makes the outer points
979 Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter;
980 Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter;
981 // makes the inner points
982 Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
983 // 36 is half of 72, and this will allow the inner and outer points to line up like a triangle.
984 Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
987 for(int idx = 0; idx < sides; idx++) {
988 if((idx+1) < sides) {
989 if(fill) {// this part below should be self explanatory. It fills in the star.
990 _fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
991 _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
994 _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
995 _drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
1000 _fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
1001 _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
1004 _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
1005 _drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
1012 // ================ Font and string functions ==================================
1014 //-------------------------------------------------------
1015 static int load_file_font(const char *fontfile, int info)
1018 char err_msg[256] = {'\0'};
1020 if (userfont != NULL) {
1028 FILE *fhndl = fopen(fontfile, "r");
1030 sprintf(err_msg, "Error opening font file '%s'", fontfile);
1036 if (stat(fontfile, &sb) != 0) {
1037 sprintf(err_msg, "Error getting font file size");
1041 int fsize = sb.st_size;
1043 sprintf(err_msg, "Error getting font file size");
1048 userfont = malloc(fsize+4);
1049 if (userfont == NULL) {
1050 sprintf(err_msg, "Font memory allocation error");
1056 int read = fread(userfont, 1, fsize, fhndl);
1060 if (read != fsize) {
1061 sprintf(err_msg, "Font read error");
1067 if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) {
1068 sprintf(err_msg, "Font ID not found");
1076 int width = userfont[0];
1077 int height = userfont[1];
1078 uint8_t first = 255;
1081 int pminwidth = 255;
1086 numchar = userfont[3];
1087 first = userfont[2];
1088 last = first + numchar - 1;
1089 size = ((width * height * numchar) / 8) + 4;
1092 // Proportional font
1093 size = 4; // point at first char data
1098 charCode = userfont[size];
1099 charwidth = userfont[size+2];
1101 if (charCode != 0xFF) {
1103 if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7);
1107 if (charwidth > pmaxwidth) pmaxwidth = charwidth;
1108 if (charwidth < pminwidth) pminwidth = charwidth;
1109 if (charCode < first) first = charCode;
1110 if (charCode > last) last = charCode;
1114 } while ((size < (read-8)) && (charCode != 0xFF));
1117 if (size != (read-8)) {
1118 sprintf(err_msg, "Font size error: found %d expected %d)", size, (read-8));
1125 printf("Fixed width font:\r\n size: %d width: %d height: %d characters: %d (%d~%d)\n",
1126 size, width, height, numchar, first, last);
1129 printf("Proportional font:\r\n size: %d width: %d~%d height: %d characters: %d (%d~%d)\n",
1130 size, pminwidth, pmaxwidth, height, numchar, first, last);
1140 if (info) printf("Error: %d [%s]\r\n", err, err_msg);
1145 //------------------------------------------------------
1146 int compile_font_file(const char *fontfile, uint8_t dbg)
1149 char err_msg[128] = {'\0'};
1150 char outfile[128] = {'\0'};
1154 FILE *ffd_out = NULL;
1155 char *sourcebuf = NULL;
1157 len = strlen(fontfile);
1159 // check here that filename end with ".c".
1160 if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) {
1161 sprintf(err_msg, "not a .c file");
1166 sprintf(outfile, "%s", fontfile);
1167 sprintf(outfile+strlen(outfile)-1, "fon");
1169 // Open the source file
1170 if (stat(fontfile, &sb) != 0) {
1171 sprintf(err_msg, "Error opening source file '%s'", fontfile);
1176 ffd = fopen(fontfile, "rb");
1178 sprintf(err_msg, "Error opening source file '%s'", fontfile);
1183 // Open the font file
1184 ffd_out= fopen(outfile, "wb");
1186 sprintf(err_msg, "error opening destination file");
1192 int fsize = sb.st_size;
1194 sprintf(err_msg, "source file size error");
1199 sourcebuf = malloc(fsize+4);
1200 if (sourcebuf == NULL) {
1201 sprintf(err_msg, "memory allocation error");
1205 char *fbuf = sourcebuf;
1207 int rdsize = fread(fbuf, 1, fsize, ffd);
1211 if (rdsize != fsize) {
1212 sprintf(err_msg, "error reading from source file");
1217 *(fbuf+rdsize) = '\0';
1219 fbuf = strchr(fbuf, '{'); // beginning of font data
1220 char *fend = strstr(fbuf, "};"); // end of font data
1222 if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) {
1223 sprintf(err_msg, "wrong source file format");
1230 char hexstr[5] = {'\0'};
1233 fbuf = strstr(fbuf, "0x");
1240 while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) {
1241 nextline = strchr(fbuf, '\n'); // beginning of the next line
1242 if (nextline == NULL) {
1248 while (fbuf < nextline) {
1249 numptr = strstr(fbuf, "0x");
1250 if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X");
1251 if ((numptr != NULL) && ((numptr+4) <= nextline)) {
1254 // buffer full, write to file
1255 if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error;
1259 memcpy(hexstr, fbuf, 4);
1261 outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0);
1264 else fbuf = nextline;
1271 if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error;
1275 sprintf(outfile, "RPH_font");
1276 if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error;
1281 // === Test compiled font ===
1282 sprintf(outfile, "%s", fontfile);
1283 sprintf(outfile+strlen(outfile)-1, "fon");
1285 uint8_t *uf = userfont; // save userfont pointer
1287 if (load_file_font(outfile, 1) != 0) {
1288 sprintf(err_msg, "Error compiling file!");
1293 sprintf(err_msg, "File compiled successfully.");
1295 userfont = uf; // restore userfont
1300 sprintf(err_msg, "error writing to destination file");
1304 if (sourcebuf) free(sourcebuf);
1305 if (ffd) fclose(ffd);
1306 if (ffd_out) fclose(ffd_out);
1308 if (dbg) printf("%s\r\n", err_msg);
1314 // -----------------------------------------------------------------------------------------
1315 // Individual Proportional Font Character Format:
1316 // -----------------------------------------------------------------------------------------
1318 // yOffset (start Y of visible pixels)
1319 // Width (width of the visible pixels)
1320 // Height (height of the visible pixels)
1321 // xOffset (start X of visible pixels)
1322 // xDelta (the distance to move the cursor. Effective width of the character.)
1324 // -----------------------------------------------------------------------------------------
1326 //---------------------------------------------------------------------------------------------
1327 // Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1)
1328 // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
1329 //---------------------------------------------------------------------------------------------
1331 //----------------------------------
1332 void getFontCharacters(uint8_t *buf)
1334 if (tft_cfont.bitmap == 2) {
1335 //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
1336 for (uint8_t n=0; n < 11; n++) {
1346 if (tft_cfont.x_size > 0) {
1347 for (uint8_t n=0; n < tft_cfont.numchars; n++) {
1348 buf[n] = tft_cfont.offset + n;
1350 buf[tft_cfont.numchars] = '\0';
1354 uint16_t tempPtr = 4; // point at first char data
1355 uint8_t cc, cw, ch, n;
1358 cc = tft_cfont.font[tempPtr++];
1359 while (cc != 0xFF) {
1360 tft_cfont.numchars++;
1362 cw = tft_cfont.font[tempPtr++];
1363 ch = tft_cfont.font[tempPtr++];
1368 tempPtr += (((cw * ch)-1) / 8) + 1;
1371 cc = tft_cfont.font[tempPtr++];
1376 // Set max width & height of the proportional font
1377 //-----------------------------
1378 static void getMaxWidthHeight()
1380 uint16_t tempPtr = 4; // point at first char data
1381 uint8_t cc, cw, ch, cd, cy;
1383 tft_cfont.numchars = 0;
1384 tft_cfont.max_x_size = 0;
1386 cc = tft_cfont.font[tempPtr++];
1387 while (cc != 0xFF) {
1388 tft_cfont.numchars++;
1389 cy = tft_cfont.font[tempPtr++];
1390 cw = tft_cfont.font[tempPtr++];
1391 ch = tft_cfont.font[tempPtr++];
1393 cd = tft_cfont.font[tempPtr++];
1395 if (cw > tft_cfont.max_x_size) tft_cfont.max_x_size = cw;
1396 if (cd > tft_cfont.max_x_size) tft_cfont.max_x_size = cd;
1397 if (ch > tft_cfont.y_size) tft_cfont.y_size = ch;
1398 if (cy > tft_cfont.y_size) tft_cfont.y_size = cy;
1401 tempPtr += (((cw * ch)-1) / 8) + 1;
1403 cc = tft_cfont.font[tempPtr++];
1405 tft_cfont.size = tempPtr;
1408 // Return the Glyph data for an individual character in the proportional font
1409 //------------------------------------
1410 static uint8_t getCharPtr(uint8_t c) {
1411 uint16_t tempPtr = 4; // point at first char data
1414 fontChar.charCode = tft_cfont.font[tempPtr++];
1415 if (fontChar.charCode == 0xFF) return 0;
1417 fontChar.adjYOffset = tft_cfont.font[tempPtr++];
1418 fontChar.width = tft_cfont.font[tempPtr++];
1419 fontChar.height = tft_cfont.font[tempPtr++];
1420 fontChar.xOffset = tft_cfont.font[tempPtr++];
1421 fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
1422 fontChar.xDelta = tft_cfont.font[tempPtr++];
1424 if (c != fontChar.charCode && fontChar.charCode != 0xFF) {
1425 if (fontChar.width != 0) {
1427 tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
1430 } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF));
1432 fontChar.dataPtr = tempPtr;
1433 if (c == fontChar.charCode) {
1434 if (tft_font_forceFixed > 0) {
1435 // fix width & offset for forced fixed width
1436 fontChar.xDelta = tft_cfont.max_x_size;
1437 fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2;
1446 //-----------------------
1447 static void _testFont() {
1448 if (tft_cfont.x_size) {
1449 printf("FONT TEST: fixed font\r\n");
1452 uint16_t tempPtr = 4; // point at first char data
1454 for (c=0x20; c <0xFF; c++) {
1455 fontChar.charCode = tft_cfont.font[tempPtr++];
1456 if (fontChar.charCode == 0xFF) break;
1457 if (fontChar.charCode != c) {
1458 printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c);
1461 c = fontChar.charCode;
1462 fontChar.adjYOffset = tft_cfont.font[tempPtr++];
1463 fontChar.width = tft_cfont.font[tempPtr++];
1464 fontChar.height = tft_cfont.font[tempPtr++];
1465 fontChar.xOffset = tft_cfont.font[tempPtr++];
1466 fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
1467 fontChar.xDelta = tft_cfont.font[tempPtr++];
1469 if (fontChar.charCode != 0xFF) {
1470 if (fontChar.width != 0) {
1472 tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
1476 printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", tft_cfont.max_x_size, tft_cfont.y_size, c, c, tempPtr);
1480 //===================================================
1481 void TFT_setFont(uint8_t font, const char *font_file)
1483 tft_cfont.font = NULL;
1485 if (font == FONT_7SEG) {
1486 tft_cfont.bitmap = 2;
1487 tft_cfont.x_size = 24;
1488 tft_cfont.y_size = 6;
1489 tft_cfont.offset = 0;
1490 tft_cfont.color = tft_fg;
1493 if (font == USER_FONT) {
1494 if (load_file_font(font_file, 0) != 0) tft_cfont.font = tft_DefaultFont;
1495 else tft_cfont.font = userfont;
1497 else if (font == DEJAVU18_FONT) tft_cfont.font = tft_Dejavu18;
1498 else if (font == DEJAVU24_FONT) tft_cfont.font = tft_Dejavu24;
1499 else if (font == UBUNTU16_FONT) tft_cfont.font = tft_Ubuntu16;
1500 else if (font == COMIC24_FONT) tft_cfont.font = tft_Comic24;
1501 else if (font == MINYA24_FONT) tft_cfont.font = tft_minya24;
1502 else if (font == TOONEY32_FONT) tft_cfont.font = tft_tooney32;
1503 else if (font == SMALL_FONT) tft_cfont.font = tft_SmallFont;
1504 else if (font == DEF_SMALL_FONT) tft_cfont.font = tft_def_small;
1505 else tft_cfont.font = tft_DefaultFont;
1507 tft_cfont.bitmap = 1;
1508 tft_cfont.x_size = tft_cfont.font[0];
1509 tft_cfont.y_size = tft_cfont.font[1];
1510 if (tft_cfont.x_size > 0) {
1511 tft_cfont.offset = tft_cfont.font[2];
1512 tft_cfont.numchars = tft_cfont.font[3];
1513 tft_cfont.size = tft_cfont.x_size * tft_cfont.y_size * tft_cfont.numchars;
1516 tft_cfont.offset = 4;
1517 getMaxWidthHeight();
1523 // -----------------------------------------------------------------------------------------
1524 // Individual Proportional Font Character Format:
1525 // -----------------------------------------------------------------------------------------
1527 // yOffset (start Y of visible pixels)
1528 // Width (width of the visible pixels)
1529 // Height (height of the visible pixels)
1530 // xOffset (start X of visible pixels)
1531 // xDelta (the distance to move the cursor. Effective width of the character.)
1533 // -----------------------------------------------------------------------------------------
1534 //---------------------------------------------------------------------------------------------
1535 // Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1)
1536 // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
1537 //---------------------------------------------------------------------------------------------
1539 // print non-rotated proportional character
1540 // character is already in fontChar
1541 //----------------------------------------------
1542 static int printProportionalChar(int x, int y) {
1544 int i, j, char_width;
1546 char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta);
1548 if ((tft_font_buffered_char) && (!tft_font_transparent)) {
1551 // === buffer Glyph data for faster sending ===
1552 len = char_width * tft_cfont.y_size;
1553 color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
1555 // fill with background color
1556 for (int n = 0; n < len; n++) {
1557 color_line[n] = tft_bg;
1559 // set character pixels to foreground color
1560 uint8_t mask = 0x80;
1561 for (j=0; j < fontChar.height; j++) {
1562 for (i=0; i < fontChar.width; i++) {
1563 if (((i + (j*fontChar.width)) % 8) == 0) {
1565 ch = tft_cfont.font[fontChar.dataPtr++];
1567 if ((ch & mask) != 0) {
1569 bufPos = ((j + fontChar.adjYOffset) * char_width) + (fontChar.xOffset + i); // bufY + bufX
1570 color_line[bufPos] = tft_fg;
1572 bufY = (j + fontChar.adjYOffset) * char_width;
1573 bufX = fontChar.xOffset + i;
1574 if ((bufX < 0) || (bufX > char_width)) {
1575 printf("[%c] X ERR: %d\r\n", fontChar.charCode, bufX);
1577 bufPos = bufY + bufX;
1578 if ((bufPos < len) && (bufPos > 0)) color_line[bufPos] = tft_fg;
1579 else printf("[%c] ERR: %d > %d W=%d H=%d bufX=%d bufY=%d X=%d Y=%d\r\n",
1580 fontChar.charCode, bufPos, len, char_width, tft_cfont.y_size, bufX, bufY, fontChar.xOffset + i, j + fontChar.adjYOffset);
1586 // send to display in one transaction
1588 send_data(x, y, x+char_width-1, y+tft_cfont.y_size-1, len, color_line);
1598 if (!tft_font_transparent) _fillRect(x, y, char_width+1, tft_cfont.y_size, tft_bg);
1601 uint8_t mask = 0x80;
1603 for (j=0; j < fontChar.height; j++) {
1604 for (i=0; i < fontChar.width; i++) {
1605 if (((i + (j*fontChar.width)) % 8) == 0) {
1607 ch = tft_cfont.font[fontChar.dataPtr++];
1610 if ((ch & mask) !=0) {
1611 cx = (uint16_t)(x+fontChar.xOffset+i);
1612 cy = (uint16_t)(y+j+fontChar.adjYOffset);
1613 _drawPixel(cx, cy, tft_fg, 0);
1623 // non-rotated fixed width character
1624 //----------------------------------------------
1625 static void printChar(uint8_t c, int x, int y) {
1626 uint8_t i, j, ch, fz, mask;
1627 uint16_t k, temp, cx, cy, len;
1629 // fz = bytes per char row
1630 fz = tft_cfont.x_size/8;
1631 if (tft_cfont.x_size % 8) fz++;
1633 // get character position in buffer
1634 temp = ((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4;
1636 if ((tft_font_buffered_char) && (!tft_font_transparent)) {
1637 // === buffer Glyph data for faster sending ===
1638 len = tft_cfont.x_size * tft_cfont.y_size;
1639 color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
1641 // fill with background color
1642 for (int n = 0; n < len; n++) {
1643 color_line[n] = tft_bg;
1645 // set character pixels to foreground color
1646 for (j=0; j<tft_cfont.y_size; j++) {
1647 for (k=0; k < fz; k++) {
1648 ch = tft_cfont.font[temp+k];
1650 for (i=0; i<8; i++) {
1651 if ((ch & mask) !=0) color_line[(j*tft_cfont.x_size) + (i+(k*8))] = tft_fg;
1657 // send to display in one transaction
1659 send_data(x, y, x+tft_cfont.x_size-1, y+tft_cfont.y_size-1, len, color_line);
1667 if (!tft_font_transparent) _fillRect(x, y, tft_cfont.x_size, tft_cfont.y_size, tft_bg);
1670 for (j=0; j<tft_cfont.y_size; j++) {
1671 for (k=0; k < fz; k++) {
1672 ch = tft_cfont.font[temp+k];
1674 for (i=0; i<8; i++) {
1675 if ((ch & mask) !=0) {
1676 cx = (uint16_t)(x+i+(k*8));
1677 cy = (uint16_t)(y+j);
1678 _drawPixel(cx, cy, tft_fg, 0);
1688 // print rotated proportional character
1689 // character is already in fontChar
1690 //---------------------------------------------------
1691 static int rotatePropChar(int x, int y, int offset) {
1693 double radian = tft_font_rotate * DEG_TO_RAD;
1694 float cos_radian = cos(radian);
1695 float sin_radian = sin(radian);
1697 uint8_t mask = 0x80;
1699 for (int j=0; j < fontChar.height; j++) {
1700 for (int i=0; i < fontChar.width; i++) {
1701 if (((i + (j*fontChar.width)) % 8) == 0) {
1703 ch = tft_cfont.font[fontChar.dataPtr++];
1706 int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian)));
1707 int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian)));
1709 if ((ch & mask) != 0) _drawPixel(newX,newY,tft_fg, 0);
1710 else if (!tft_font_transparent) _drawPixel(newX,newY,tft_bg, 0);
1717 return fontChar.xDelta+1;
1720 // rotated fixed width character
1721 //--------------------------------------------------------
1722 static void rotateChar(uint8_t c, int x, int y, int pos) {
1723 uint8_t i,j,ch,fz,mask;
1726 double radian = tft_font_rotate*0.0175;
1727 float cos_radian = cos(radian);
1728 float sin_radian = sin(radian);
1731 if( tft_cfont.x_size < 8 ) fz = tft_cfont.x_size;
1732 else fz = tft_cfont.x_size/8;
1733 temp=((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4;
1736 for (j=0; j<tft_cfont.y_size; j++) {
1737 for (zz=0; zz<(fz); zz++) {
1738 ch = tft_cfont.font[temp+zz];
1740 for (i=0; i<8; i++) {
1741 newx=(int)(x+(((i+(zz*8)+(pos*tft_cfont.x_size))*cos_radian)-((j)*sin_radian)));
1742 newy=(int)(y+(((j)*cos_radian)+((i+(zz*8)+(pos*tft_cfont.x_size))*sin_radian)));
1744 if ((ch & mask) != 0) _drawPixel(newx,newy,tft_fg, 0);
1745 else if (!tft_font_transparent) _drawPixel(newx,newy,tft_bg, 0);
1752 // calculate x,y for the next char
1753 tft_x = (int)(x + ((pos+1) * tft_cfont.x_size * cos_radian));
1754 tft_y = (int)(y + ((pos+1) * tft_cfont.x_size * sin_radian));
1757 //----------------------
1758 static int _7seg_width()
1760 return (2 * (2 * tft_cfont.y_size + 1)) + tft_cfont.x_size;
1763 //-----------------------
1764 static int _7seg_height()
1766 return (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size);
1769 // Returns the string width in pixels.
1770 // Useful for positions strings on the screen.
1771 //=====================================
1772 int TFT_getStringWidth(const char *str)
1776 if (tft_cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font
1777 else if (tft_cfont.x_size != 0) strWidth = strlen(str) * tft_cfont.x_size; // fixed width font
1779 // calculate the width of the string of proportional characters
1780 const char *tempStrptr = str;
1781 while (*tempStrptr != 0) {
1782 if (getCharPtr(*tempStrptr++)) {
1783 strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1);
1791 //=====================================================
1792 void TFT_clearStringRect(int x, int y, const char *str)
1794 int w = TFT_getStringWidth(str);
1795 int h = TFT_getfontheight();
1796 TFT_fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, tft_bg);
1799 //==============================================================================
1801 * bit-encoded bar position of all digits' bcd segments
1811 static const uint16_t font_bcd[] = {
1812 0x200, // 0010 0000 0000 // -
1813 0x080, // 0000 1000 0000 // .
1814 0x06C, // 0100 0110 1100 // /, degree
1815 0x05f, // 0000 0101 1111, // 0
1816 0x005, // 0000 0000 0101, // 1
1817 0x076, // 0000 0111 0110, // 2
1818 0x075, // 0000 0111 0101, // 3
1819 0x02d, // 0000 0010 1101, // 4
1820 0x079, // 0000 0111 1001, // 5
1821 0x07b, // 0000 0111 1011, // 6
1822 0x045, // 0000 0100 0101, // 7
1823 0x07f, // 0000 0111 1111, // 8
1824 0x07d, // 0000 0111 1101 // 9
1825 0x900 // 1001 0000 0000 // :
1828 //-----------------------------------------------------------------------------------------------
1829 static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
1830 _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color);
1831 _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color);
1832 _fillRect(x, y+2*w+1, 2*w+1, l, color);
1833 if (tft_cfont.offset) {
1834 _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline);
1835 _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline);
1836 _drawRect(x, y+2*w+1, 2*w+1, l, outline);
1840 //----------------------------------------------------------------------------------------------
1841 static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
1842 _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color);
1843 _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color);
1844 _fillRect(x+2*w+1, y, l, 2*w+1, color);
1845 if (tft_cfont.offset) {
1846 _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline);
1847 _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline);
1848 _drawRect(x+2*w+1, y, l, 2*w+1, outline);
1852 //--------------------------------------------------------------------------------------------
1853 static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) {
1854 /* TODO: clipping */
1855 if (num < 0x2D || num > 0x3A) return;
1857 int16_t c = font_bcd[num-0x2D];
1858 int16_t d = 2*w+l+1;
1860 // === Clear unused segments ===
1861 if (!(c & 0x001)) barVert(x+d, y+d, w, l, tft_bg, tft_bg);
1862 if (!(c & 0x002)) barVert(x, y+d, w, l, tft_bg, tft_bg);
1863 if (!(c & 0x004)) barVert(x+d, y, w, l, tft_bg, tft_bg);
1864 if (!(c & 0x008)) barVert(x, y, w, l, tft_bg, tft_bg);
1865 if (!(c & 0x010)) barHor(x, y+2*d, w, l, tft_bg, tft_bg);
1866 if (!(c & 0x020)) barHor(x, y+d, w, l, tft_bg, tft_bg);
1867 if (!(c & 0x040)) barHor(x, y, w, l, tft_bg, tft_bg);
1871 _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg);
1872 if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg);
1875 // down middle point
1876 _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg);
1877 if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg);
1881 _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg);
1882 if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg);
1886 _fillRect(x+2*w+1, y+d, l, 2*w+1, tft_bg);
1887 if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_bg);
1890 // === Draw used segments ===
1891 if (c & 0x001) barVert(x+d, y+d, w, l, color, tft_cfont.color); // down right
1892 if (c & 0x002) barVert(x, y+d, w, l, color, tft_cfont.color); // down left
1893 if (c & 0x004) barVert(x+d, y, w, l, color, tft_cfont.color); // up right
1894 if (c & 0x008) barVert(x, y, w, l, color, tft_cfont.color); // up left
1895 if (c & 0x010) barHor(x, y+2*d, w, l, color, tft_cfont.color); // down
1896 if (c & 0x020) barHor(x, y+d, w, l, color, tft_cfont.color); // middle
1897 if (c & 0x040) barHor(x, y, w, l, color, tft_cfont.color); // up
1901 _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color);
1902 if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_cfont.color);
1905 // down middle point
1906 _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color);
1907 if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_cfont.color);
1911 _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color);
1912 if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_cfont.color);
1916 _fillRect(x+2*w+1, y+d, l, 2*w+1, color);
1917 if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_cfont.color);
1920 //==============================================================================
1922 //============================================
1923 void TFT_print(const char *st, int x, int y) {
1924 int stl, i, tmpw, tmph, fh;
1927 if (tft_cfont.bitmap == 0) return; // wrong font selected
1929 // ** Rotated strings cannot be aligned
1930 if ((tft_font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return;
1932 if ((x < LASTX) || (tft_font_rotate == 0)) TFT_OFFSET = 0;
1934 if ((x >= LASTX) && (x < LASTY)) x = tft_x + (x-LASTX);
1935 else if (x > CENTER) x += tft_dispWin.x1;
1937 if (y >= LASTY) y = tft_y + (y-LASTY);
1938 else if (y > CENTER) y += tft_dispWin.y1;
1940 // ** Get number of characters in string to print
1943 // ** Calculate CENTER, RIGHT or BOTTOM position
1944 tmpw = TFT_getStringWidth(st); // string width in pixels
1945 fh = tft_cfont.y_size; // font height
1946 if ((tft_cfont.x_size != 0) && (tft_cfont.bitmap == 2)) {
1948 fh = (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size); // 7-seg character height
1951 if (x == RIGHT) x = tft_dispWin.x2 - tmpw + tft_dispWin.x1;
1952 else if (x == CENTER) x = (((tft_dispWin.x2 - tft_dispWin.x1 + 1) - tmpw) / 2) + tft_dispWin.x1;
1954 if (y == BOTTOM) y = tft_dispWin.y2 - fh + tft_dispWin.y1;
1955 else if (y==CENTER) y = (((tft_dispWin.y2 - tft_dispWin.y1 + 1) - (fh/2)) / 2) + tft_dispWin.y1;
1957 if (x < tft_dispWin.x1) x = tft_dispWin.x1;
1958 if (y < tft_dispWin.y1) y = tft_dispWin.y1;
1959 if ((x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
1964 // ** Adjust y position
1965 tmph = tft_cfont.y_size; // font height
1966 // for non-proportional fonts, char width is the same for all chars
1967 tmpw = tft_cfont.x_size;
1968 if (tft_cfont.x_size != 0) {
1969 if (tft_cfont.bitmap == 2) { // 7-segment font
1970 tmpw = _7seg_width(); // character width
1971 tmph = _7seg_height(); // character height
1974 else TFT_OFFSET = 0; // fixed font; offset not needed
1976 if ((tft_y + tmph - 1) > tft_dispWin.y2) return;
1978 int offset = TFT_OFFSET;
1980 for (i=0; i<stl; i++) {
1981 ch = st[i]; // get string character
1983 if (ch == 0x0D) { // === '\r', erase to eol ====
1984 if ((!tft_font_transparent) && (tft_font_rotate==0)) _fillRect(tft_x, tft_y, tft_dispWin.x2+1-tft_x, tmph, tft_bg);
1987 else if (ch == 0x0A) { // ==== '\n', new line ====
1988 if (tft_cfont.bitmap == 1) {
1989 tft_y += tmph + tft_font_line_space;
1990 if (tft_y > (tft_dispWin.y2-tmph)) break;
1991 tft_x = tft_dispWin.x1;
1995 else { // ==== other characters ====
1996 if (tft_cfont.x_size == 0) {
1997 // for proportional font get character data to 'fontChar'
1998 if (getCharPtr(ch)) tmpw = fontChar.xDelta;
2002 // check if character can be displayed in the current line
2003 if ((tft_x+tmpw) > (tft_dispWin.x2)) {
2004 if (tft_text_wrap == 0) break;
2005 tft_y += tmph + tft_font_line_space;
2006 if (tft_y > (tft_dispWin.y2-tmph)) break;
2007 tft_x = tft_dispWin.x1;
2010 // Let's print the character
2011 if (tft_cfont.x_size == 0) {
2012 // == proportional font
2013 if (tft_font_rotate == 0) tft_x += printProportionalChar(tft_x, tft_y) + 1;
2015 // rotated proportional font
2016 offset += rotatePropChar(x, y, offset);
2017 TFT_OFFSET = offset;
2021 if (tft_cfont.bitmap == 1) {
2023 if ((ch < tft_cfont.offset) || ((ch-tft_cfont.offset) > tft_cfont.numchars)) ch = tft_cfont.offset;
2024 if (tft_font_rotate == 0) {
2025 printChar(ch, tft_x, tft_y);
2028 else rotateChar(ch, x, y, i);
2030 else if (tft_cfont.bitmap == 2) {
2031 // == 7-segment font ==
2032 _draw7seg(tft_x, tft_y, ch, tft_cfont.y_size, tft_cfont.x_size, tft_fg);
2033 tft_x += (tmpw + 2);
2041 // ================ Service functions ==========================================
2043 // Change the screen rotation.
2044 // Input: m new rotation value (0 to 3)
2045 //=================================
2046 void TFT_setRotation(uint8_t rot) {
2048 uint8_t madctl = (rot & 0xF8); // for testing, manually set MADCTL register
2049 if (disp_select() == ESP_OK) {
2050 disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
2055 tft_orientation = rot;
2056 _tft_setRotation(rot);
2059 tft_dispWin.x1 = TFT_STATIC_X_OFFSET;
2060 tft_dispWin.y1 = TFT_STATIC_Y_OFFSET;
2061 tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
2062 tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
2064 TFT_fillScreen(tft_bg);
2067 // Send the command to invert all of the colors.
2068 // Input: i 0 to disable inversion; non-zero to enable inversion
2069 //==========================================
2070 void TFT_invertDisplay(const uint8_t mode) {
2071 if (disp_select() == ESP_OK) {
2072 if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN);
2073 else disp_spi_transfer_cmd(TFT_INVOFF);
2078 // Select gamma curve
2079 // Input: gamma = 0~3
2080 //==================================
2081 void TFT_setGammaCurve(uint8_t gm) {
2082 uint8_t gamma_curve = (uint8_t)1 << (gm & (uint8_t)0x03);
2083 if (disp_select() == ESP_OK) {
2084 disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1);
2089 //===========================================================
2090 color_t HSBtoRGB(float _hue, float _sat, float _brightness) {
2097 green = _brightness;
2100 if (_hue == 360.0) {
2104 int slice = (int)(_hue / 60.0);
2105 float hue_frac = (_hue / 60.0) - slice;
2107 float aa = _brightness * (1.0 - _sat);
2108 float bb = _brightness * (1.0 - _sat * hue_frac);
2109 float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac));
2119 green = _brightness;
2124 green = _brightness;
2151 color.r = ((uint8_t)(red * 255.0)) & 0xFC;
2152 color.g = ((uint8_t)(green * 255.0)) & 0xFC;
2153 color.b = ((uint8_t)(blue * 255.0)) & 0xFC;
2157 //=====================================================================
2158 void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
2160 tft_dispWin.x1 = x1 + TFT_STATIC_X_OFFSET;
2161 tft_dispWin.y1 = y1 + TFT_STATIC_Y_OFFSET;
2162 tft_dispWin.x2 = x2 + TFT_STATIC_X_OFFSET;
2163 tft_dispWin.y2 = y2 + TFT_STATIC_Y_OFFSET;
2165 if (tft_dispWin.x2 >= tft_width + TFT_STATIC_X_OFFSET) tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
2166 if (tft_dispWin.y2 >= tft_height + TFT_STATIC_Y_OFFSET) tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
2167 if (tft_dispWin.x1 > tft_dispWin.x2) tft_dispWin.x1 = tft_dispWin.x2;
2168 if (tft_dispWin.y1 > tft_dispWin.y2) tft_dispWin.y1 = tft_dispWin.y2;
2171 //=====================
2172 void TFT_resetclipwin()
2174 tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
2175 tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
2176 tft_dispWin.x1 = TFT_STATIC_X_OFFSET;
2177 tft_dispWin.y1 = TFT_STATIC_Y_OFFSET;
2180 //==========================================================================
2181 void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) {
2182 if (tft_cfont.bitmap != 2) return;
2187 if (w > (l/2)) w = l/2;
2190 tft_cfont.x_size = l;
2191 tft_cfont.y_size = w;
2192 tft_cfont.offset = outline;
2193 tft_cfont.color = color;
2196 //==========================================
2197 int TFT_getfontsize(int *width, int* height)
2199 if (tft_cfont.bitmap == 1) {
2200 if (tft_cfont.x_size != 0) *width = tft_cfont.x_size; // fixed width font
2201 else *width = tft_cfont.max_x_size; // proportional font
2202 *height = tft_cfont.y_size;
2204 else if (tft_cfont.bitmap == 2) {
2206 *width = _7seg_width();
2207 *height = _7seg_height();
2217 //=====================
2218 int TFT_getfontheight()
2220 if (tft_cfont.bitmap == 1) return tft_cfont.y_size; // Bitmap font
2221 else if (tft_cfont.bitmap == 2) return _7seg_height(); // 7-segment font
2225 //====================
2226 void TFT_saveClipWin()
2228 dispWinTemp.x1 = tft_dispWin.x1;
2229 dispWinTemp.y1 = tft_dispWin.y1;
2230 dispWinTemp.x2 = tft_dispWin.x2;
2231 dispWinTemp.y2 = tft_dispWin.y2;
2234 //=======================
2235 void TFT_restoreClipWin()
2237 tft_dispWin.x1 = dispWinTemp.x1;
2238 tft_dispWin.y1 = dispWinTemp.y1;
2239 tft_dispWin.x2 = dispWinTemp.x2;
2240 tft_dispWin.y2 = dispWinTemp.y2;
2244 // ================ JPG SUPPORT ================================================
2245 // User defined device identifier
2247 FILE *fhndl; // File handler for input function
2248 int x; // image top left point X position
2249 int y; // image top left point Y position
2250 uint8_t *membuff; // memory buffer containing the image
2251 uint32_t bufsize; // size of the memory buffer
2252 uint32_t bufptr; // memory buffer current position
2253 color_t *linbuf[2]; // memory buffer used for display output
2258 // User defined call-back function to input JPEG data from file
2259 //---------------------
2260 static UINT tjd_input (
2261 JDEC* jd, // Decompression object
2262 BYTE* buff, // Pointer to the read buffer (NULL:skip)
2263 UINT nd // Number of bytes to read/skip from input stream
2267 // Device identifier for the session (5th argument of jd_prepare function)
2268 JPGIODEV *dev = (JPGIODEV*)jd->device;
2270 if (buff) { // Read nd bytes from the input strem
2271 rb = fread(buff, 1, nd, dev->fhndl);
2272 return rb; // Returns actual number of bytes read
2274 else { // Remove nd bytes from the input stream
2275 if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd;
2280 // User defined call-back function to input JPEG data from memory buffer
2281 //-------------------------
2282 static UINT tjd_buf_input (
2283 JDEC* jd, // Decompression object
2284 BYTE* buff, // Pointer to the read buffer (NULL:skip)
2285 UINT nd // Number of bytes to read/skip from input stream
2288 // Device identifier for the session (5th argument of jd_prepare function)
2289 JPGIODEV *dev = (JPGIODEV*)jd->device;
2290 if (!dev->membuff) return 0;
2291 if (dev->bufptr >= (dev->bufsize + 2)) return 0; // end of stream
2293 if ((dev->bufptr + nd) > (dev->bufsize + 2)) nd = (dev->bufsize + 2) - dev->bufptr;
2295 if (buff) { // Read nd bytes from the input strem
2296 memcpy(buff, dev->membuff + dev->bufptr, nd);
2298 return nd; // Returns number of bytes read
2300 else { // Remove nd bytes from the input stream
2306 // User defined call-back function to output RGB bitmap to display device
2307 //----------------------
2308 static UINT tjd_output (
2309 JDEC* jd, // Decompression object of current session
2310 void* bitmap, // Bitmap data to be output
2311 JRECT* rect // Rectangular region to output
2314 // Device identifier for the session (5th argument of jd_prepare function)
2315 JPGIODEV *dev = (JPGIODEV*)jd->device;
2317 // ** Put the rectangular into the display device **
2320 int dleft, dtop, dright, dbottom;
2321 BYTE *src = (BYTE*)bitmap;
2323 int left = rect->left + dev->x;
2324 int top = rect->top + dev->y;
2325 int right = rect->right + dev->x;
2326 int bottom = rect->bottom + dev->y;
2328 if ((left > tft_dispWin.x2) || (top > tft_dispWin.y2)) return 1; // out of screen area, return
2329 if ((right < tft_dispWin.x1) || (bottom < tft_dispWin.y1)) return 1;// out of screen area, return
2331 if (left < tft_dispWin.x1) dleft = tft_dispWin.x1;
2333 if (top < tft_dispWin.y1) dtop = tft_dispWin.y1;
2335 if (right > tft_dispWin.x2) dright = tft_dispWin.x2;
2336 else dright = right;
2337 if (bottom > tft_dispWin.y2) dbottom = tft_dispWin.y2;
2338 else dbottom = bottom;
2340 if ((dleft > tft_dispWin.x2) || (dtop > tft_dispWin.y2)) return 1; // out of screen area, return
2341 if ((dright < tft_dispWin.x1) || (dbottom < tft_dispWin.y1)) return 1; // out of screen area, return
2343 uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data
2346 if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) {
2347 uint8_t *dest = (uint8_t *)(dev->linbuf[dev->linbuf_idx]);
2349 for (y = top; y <= bottom; y++) {
2350 for (x = left; x <= right; x++) {
2351 // Clip to display area
2352 if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) {
2353 *dest++ = (*src++) & 0xFC;
2354 *dest++ = (*src++) & 0xFC;
2355 *dest++ = (*src++) & 0xFC;
2357 else src += 3; // skip
2360 wait_trans_finish(1);
2361 send_data(dleft, dtop, dright, dbottom, len, dev->linbuf[dev->linbuf_idx]);
2362 dev->linbuf_idx = ((dev->linbuf_idx + 1) & 1);
2365 wait_trans_finish(1);
2366 printf("Data size error: %d jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", len, left,top,right,bottom, dleft,dtop,dright,dbottom);
2367 return 0; // stop decompression
2370 return 1; // Continue to decompression
2373 // tft.jpgimage(X, Y, scale, file_name, buf, size]
2374 // X & Y can be < 0 !
2375 //========================================================================================
2376 void TFT_jpg_image(int x, int y, uint8_t scale, const char *fname, uint8_t *buf, int size)
2380 char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
2381 UINT sz_work = 3800; // Size of the working buffer (must be power of 2)
2382 JDEC jd; // Decompression object (70 bytes)
2385 dev.linbuf[0] = NULL;
2386 dev.linbuf[1] = NULL;
2390 if (fname == NULL) {
2391 // image from buffer
2402 if (stat(fname, &sb) != 0) {
2403 if (tft_image_debug) printf("File error: %ss\r\n", strerror(errno));
2407 dev.fhndl = fopen(fname, "r");
2409 if (tft_image_debug) printf("Error opening file: %s\r\n", strerror(errno));
2414 if (scale > 3) scale = 3;
2416 work = malloc(sz_work);
2418 if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev);
2419 else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev);
2421 if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + tft_dispWin.x1;
2422 else if (x == RIGHT) x = tft_dispWin.x2 + 1 - (int)(jd.width >> scale);
2424 if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + tft_dispWin.y1;
2425 else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - (int)(jd.height >> scale);
2427 if (x < ((tft_dispWin.x2-1) * -1)) x = (tft_dispWin.x2-1) * -1;
2428 if (y < ((tft_dispWin.y2-1)) * -1) y = (tft_dispWin.y2-1) * -1;
2429 if (x > (tft_dispWin.x2-1)) x = tft_dispWin.x2 - 1;
2430 if (y > (tft_dispWin.y2-1)) y = tft_dispWin.y2-1;
2435 dev.linbuf[0] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
2436 if (dev.linbuf[0] == NULL) {
2437 if (tft_image_debug) printf("Error allocating line buffer #0\r\n");
2440 dev.linbuf[1] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
2441 if (dev.linbuf[1] == NULL) {
2442 if (tft_image_debug) printf("Error allocating line buffer #1\r\n");
2446 // Start to decode the JPEG file
2448 rc = jd_decomp(&jd, tjd_output, scale);
2452 if (tft_image_debug) printf("jpg decompression error %d\r\n", rc);
2454 if (tft_image_debug) printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool);
2457 if (tft_image_debug) printf("jpg prepare error %d\r\n", rc);
2461 if (tft_image_debug) printf("work buffer allocation error\r\n");
2465 if (work) free(work); // free work buffer
2466 if (dev.linbuf[0]) free(dev.linbuf[0]);
2467 if (dev.linbuf[1]) free(dev.linbuf[1]);
2468 if (dev.fhndl) fclose(dev.fhndl); // close input file
2472 //==========================================================================================
2473 int TFT_bmp_image(int x, int y, uint8_t scale, const char *fname, uint8_t *imgbuf, int size)
2478 int img_xsize, img_ysize, img_xstart, img_xlen, img_ystart, img_ylen;
2479 int img_pos, img_pix_pos, scan_lines, rd_len;
2483 int disp_xstart, disp_xend, disp_ystart, disp_yend;
2486 uint8_t *line_buf[2] = {NULL,NULL};
2488 uint8_t *scale_buf = NULL;
2490 uint16_t co[3] = {0,0,0}; // RGB sum
2493 if (scale > 7) scale = 7;
2494 scale_pix = scale+1; // scale factor ( 1~8 )
2497 // * File name is given, reading image from file
2498 if (stat(fname, &sb) != 0) {
2499 sprintf(err_buf, "opening file");
2504 fhndl = fopen(fname, "r");
2506 sprintf(err_buf, "opening file");
2511 i = fread(buf, 1, 54, fhndl); // read header
2514 // * Reading image from buffer
2515 if ((imgbuf) && (size > 54)) {
2516 memcpy(buf, imgbuf, 54);
2522 sprintf(err_buf, "reading header");
2523 if (i != 54) {err = -3; goto exit;}
2525 // ** Check image header and get image properties
2526 if ((buf[0] != 'B') || (buf[1] != 'M')) {err=-4; goto exit;} // accept only images with 'BM' id
2528 memcpy(&temp, buf+2, 4); // file size
2529 if (temp != size) {err=-5; goto exit;}
2531 memcpy(&img_pos, buf+10, 4); // start of pixel data
2533 memcpy(&temp, buf+14, 4); // BMP header size
2534 if (temp != 40) {err=-6; goto exit;}
2536 memcpy(&wtemp, buf+26, 2); // the number of color planes
2537 if (wtemp != 1) {err=-7; goto exit;}
2539 memcpy(&wtemp, buf+28, 2); // the number of bits per pixel
2540 if (wtemp != 24) {err=-8; goto exit;}
2542 memcpy(&temp, buf+30, 4); // the compression method being used
2543 if (temp != 0) {err=-9; goto exit;}
2545 memcpy(&img_xsize, buf+18, 4); // the bitmap width in pixels
2546 memcpy(&img_ysize, buf+22, 4); // the bitmap height in pixels
2549 // * scale image dimensions
2551 img_xlen = img_xsize / scale_pix; // image display horizontal size
2552 img_ylen = img_ysize / scale_pix; // image display vertical size
2554 if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - img_xlen) / 2) + tft_dispWin.x1;
2555 else if (x == RIGHT) x = tft_dispWin.x2 + 1 - img_xlen;
2557 if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - img_ylen) / 2) + tft_dispWin.y1;
2558 else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - img_ylen;
2560 if ((x < ((tft_dispWin.x2 + 1) * -1)) || (x > (tft_dispWin.x2 + 1)) || (y < ((tft_dispWin.y2 + 1) * -1)) || (y > (tft_dispWin.y2 + 1))) {
2561 sprintf(err_buf, "out of display area (%d,%d", x, y);
2566 // ** set display and image areas
2567 if (x < tft_dispWin.x1) {
2568 disp_xstart = tft_dispWin.x1;
2569 img_xstart = -x; // image pixel line X offset
2576 if (y < tft_dispWin.y1) {
2577 disp_ystart = tft_dispWin.y1;
2578 img_ystart = -y; // image pixel line Y offset
2585 disp_xend = disp_xstart + img_xlen - 1;
2586 disp_yend = disp_ystart + img_ylen - 1;
2587 if (disp_xend > tft_dispWin.x2) {
2588 disp_xend = tft_dispWin.x2;
2589 img_xlen = disp_xend - disp_xstart + 1;
2591 if (disp_yend > tft_dispWin.y2) {
2592 disp_yend = tft_dispWin.y2;
2593 img_ylen = disp_yend - disp_ystart + 1;
2596 if ((img_xlen < 8) || (img_ylen < 8) || (img_xstart >= (img_xsize-2)) || ((img_ysize - img_ystart) < 2)) {
2597 sprintf(err_buf, "image too small");
2602 // ** Allocate memory for 2 lines of image pixels
2603 line_buf[0] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
2604 if (line_buf[0] == NULL) {
2605 sprintf(err_buf, "allocating line buffer #1");
2610 line_buf[1] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
2611 if (line_buf[1] == NULL) {
2612 sprintf(err_buf, "allocating line buffer #2");
2618 // Allocate memory for scale buffer
2619 rd_len = img_xlen * 3 * scale_pix;
2620 scale_buf = malloc(rd_len*scale_pix);
2621 if (scale_buf == NULL) {
2622 sprintf(err_buf, "allocating scale buffer");
2627 else rd_len = img_xlen * 3;
2629 // ** ***************************************************** **
2630 // ** BMP images are stored in file from LAST to FIRST line **
2631 // ** ***************************************************** **
2634 img_xsize horizontal image size in pixels
2635 img_ysize number of image lines
2636 img_xlen image display horizontal scaled size in pixels
2637 img_ylen image display vertical scaled size in pixels
2638 img_xstart first pixel in line to be displayed
2639 img_ystart first image line to be displayed
2640 img_xlen number of pixels in image line to be displayed, starting with 'img_xstart'
2641 img_ylen number of lines in image to be displayed, starting with 'img_ystart'
2642 rd_len length of color data which are read from image line in bytes
2645 // Set position in image to the first color data (beginning of the LAST line)
2646 img_pos += (img_ystart * (img_xsize*3));
2648 if (fseek(fhndl, img_pos, SEEK_SET) != 0) {
2649 sprintf(err_buf, "file seek at %d", img_pos);
2655 if (tft_image_debug) printf("BMP: image size: (%d,%d) scale: %d disp size: (%d,%d) img xofs: %d img yofs: %d at: %d,%d; line buf: 2* %d scale buf: %d\r\n",
2656 img_xsize, img_ysize, scale_pix, img_xlen, img_ylen, img_xstart, img_ystart, disp_xstart, disp_ystart, img_xsize*3, ((scale) ? (rd_len*scale_pix) : 0));
2658 // * Select the display
2661 while ((disp_yend >= disp_ystart) && ((img_pos + (img_xsize*3)) <= size)) {
2662 if (img_pos > size) {
2663 sprintf(err_buf, "EOF reached: %d > %d", img_pos, size);
2668 // Read the line of color data into color buffer
2670 i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
2671 if (i != (img_xsize*3)) {
2672 sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
2677 else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
2679 if (img_xstart > 0) memmove(line_buf[lb_idx], line_buf[lb_idx]+(img_xstart*3), rd_len);
2680 // Convert colors BGR-888 (BMP) -> RGB-888 (DISPLAY) ===
2681 for (i=0; i < rd_len; i += 3) {
2682 tmpc = line_buf[lb_idx][i+2] & 0xfc; // save R
2683 line_buf[lb_idx][i+2] = line_buf[lb_idx][i] & 0xfc; // B -> R
2684 line_buf[lb_idx][i] = tmpc; // R -> B
2685 line_buf[lb_idx][i+1] &= 0xfc; // G
2687 img_pos += (img_xsize*3);
2690 // scale image, read 'scale_pix' lines and find the average color
2691 for (scan_lines=0; scan_lines<scale_pix; scan_lines++) {
2692 if (img_pos > size) break;
2694 i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
2695 if (i != (img_xsize*3)) {
2696 sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
2701 else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
2702 img_pos += (img_xsize*3);
2704 // copy only data which are displayed to scale buffer
2705 memcpy(scale_buf + (rd_len * scan_lines), line_buf[lb_idx]+img_xstart, rd_len);
2708 // Populate display line buffer
2709 for (int n=0;n<(img_xlen*3);n += 3) {
2710 memset(co, 0, sizeof(co)); // initialize color sum
2711 npix = 0; // initialize number of pixels in scale rectangle
2713 // sum all pixels in scale rectangle
2714 for (int sc_line=0; sc_line<scan_lines; sc_line++) {
2715 // Get colors position in scale buffer
2716 img_pix_pos = (rd_len * sc_line) + (n * scale_pix);
2718 for (int sc_col=0; sc_col<scale_pix; sc_col++) {
2719 co[0] += scale_buf[img_pix_pos];
2720 co[1] += scale_buf[img_pix_pos + 1];
2721 co[2] += scale_buf[img_pix_pos + 2];
2725 // Place the average in display buffer, convert BGR-888 (BMP) -> RGB-888 (DISPLAY)
2726 line_buf[lb_idx][n+2] = (uint8_t)(co[0] / npix); // B
2727 line_buf[lb_idx][n+1] = (uint8_t)(co[1] / npix); // G
2728 line_buf[lb_idx][n] = (uint8_t)(co[2] / npix); // R
2732 wait_trans_finish(1);
2733 send_data(disp_xstart, disp_yend, disp_xend, disp_yend, img_xlen, (color_t *)line_buf[lb_idx]);
2734 lb_idx = (lb_idx + 1) & 1; // change buffer
2742 if (scale_buf) free(scale_buf);
2743 if (line_buf[0]) free(line_buf[0]);
2744 if (line_buf[1]) free(line_buf[1]);
2745 if (fhndl) fclose(fhndl);
2746 if ((err) && (tft_image_debug)) printf("Error: %d [%s]\r\n", err, err_buf);
2752 // ============= Touch panel functions =========================================
2754 #if USE_TOUCH == TOUCH_TYPE_XPT2046
2755 //-------------------------------------------------------
2756 static int tp_get_data_xpt2046(uint8_t type, int samples)
2758 if (tft_ts_spi == NULL) return 0;
2760 int n, result, val = 0;
2763 uint32_t minval, maxval, dif;
2765 if (samples < 3) samples = 1;
2766 if (samples > 18) samples = 18;
2769 result = touch_get_data(type);
2776 for (n=0;n<samples;n++) {
2777 result = touch_get_data(type);
2778 if (result < 0) break;
2781 if (result < minval) minval = result;
2782 if (result > maxval) maxval = result;
2784 if (result < 0) break;
2785 dif = maxval - minval;
2786 if (dif < 40) break;
2789 if (result < 0) return -1;
2792 // remove one min value
2793 for (n = 0; n < samples; n++) {
2794 if (vbuf[n] == minval) {
2799 // remove one max value
2800 for (n = 0; n < samples; n++) {
2801 if (vbuf[n] == maxval) {
2806 for (n = 0; n < samples; n++) {
2807 if (vbuf[n] < 5000) val += vbuf[n];
2816 //-----------------------------------------------
2817 static int TFT_read_touch_xpt2046(int *x, int* y)
2819 int res = 0, result = -1;
2820 if (spi_lobo_device_select(tft_ts_spi, 0) != ESP_OK) return 0;
2822 result = tp_get_data_xpt2046(0xB0, 3); // Z; pressure; touch detect
2823 if (result <= 50) goto exit;
2825 // touch panel pressed
2826 result = tp_get_data_xpt2046(0xD0, 10);
2827 if (result < 0) goto exit;
2831 result = tp_get_data_xpt2046(0x90, 10);
2832 if (result < 0) goto exit;
2837 spi_lobo_device_deselect(tft_ts_spi);
2842 //=============================================
2843 int TFT_read_touch(int *x, int* y, uint8_t raw)
2847 if (tft_ts_spi == NULL) return 0;
2848 #if USE_TOUCH == TOUCH_TYPE_NONE
2854 #if USE_TOUCH == TOUCH_TYPE_XPT2046
2855 uint32_t tft_tp_calx = TP_CALX_XPT2046;
2856 uint32_t tft_tp_caly = TP_CALY_XPT2046;
2857 result = TFT_read_touch_xpt2046(&X, &Y);
2858 if (result == 0) return 0;
2859 #elif USE_TOUCH == TOUCH_TYPE_STMPE610
2860 uint32_t tft_tp_calx = TP_CALX_STMPE610;
2861 uint32_t tft_tp_caly = TP_CALY_STMPE610;
2862 uint16_t Xx, Yy, Z=0;
2863 result = stmpe610_get_touch(&Xx, &Yy, &Z);
2864 if (result == 0) return 0;
2877 // Calibrate the result
2879 int xleft = (tft_tp_calx >> 16) & 0x3FFF;
2880 int xright = tft_tp_calx & 0x3FFF;
2881 int ytop = (tft_tp_caly >> 16) & 0x3FFF;
2882 int ybottom = tft_tp_caly & 0x3FFF;
2884 if (((xright - xleft) <= 0) || ((ybottom - ytop) <= 0)) return 0;
2886 #if USE_TOUCH == TOUCH_TYPE_XPT2046
2887 int width = tft_width;
2888 int height = tft_height;
2889 X = ((X - xleft) * height) / (xright - xleft);
2890 Y = ((Y - ytop) * width) / (ybottom - ytop);
2893 if (X > height-1) X = height-1;
2895 if (Y > width-1) Y = width-1;
2897 switch (tft_orientation) {
2906 Y = height - tmp - 1;
2908 case LANDSCAPE_FLIP:
2913 #elif USE_TOUCH == TOUCH_TYPE_STMPE610
2914 int width = tft_width;
2915 int height = tft_height;
2916 if (tft_width > tft_height) {
2920 X = ((X - xleft) * width) / (xright - xleft);
2921 Y = ((Y - ytop) * height) / (ybottom - ytop);
2924 if (X > width-1) X = width-1;
2926 if (Y > height-1) Y = height-1;
2928 switch (tft_orientation) {
2938 case LANDSCAPE_FLIP: