components/tft/tft.c

changeset 0
b74b0e4902c3
child 18
5d4a40fe9967
equal deleted inserted replaced
-1:000000000000 0:b74b0e4902c3
1 /* TFT module
2 *
3 * Author: LoBo (loboris@gmail.com, loboris.github)
4 *
5 * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers
6 */
7
8 #include <stdio.h>
9 #include <errno.h>
10 #include <sys/stat.h>
11 #include <string.h>
12 #include "freertos/FreeRTOS.h"
13 #include "freertos/task.h"
14 #include "esp_system.h"
15 #include "tft.h"
16 #include "time.h"
17 #include <math.h>
18 #include "rom/tjpgd.h"
19 #include "esp_heap_caps.h"
20 #include "tftspi.h"
21
22 #include "vnc-server.h"
23
24 #define DEG_TO_RAD 0.01745329252
25 #define RAD_TO_DEG 57.295779513
26 #define deg_to_rad 0.01745329252 + 3.14159265359
27 #define swap(a, b) { int16_t t = a; a = b; b = t; }
28 #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
29 #if !defined(max)
30 #define max(A,B) ( (A) > (B) ? (A):(B))
31 #endif
32 #if !defined(min)
33 #define min(A,B) ( (A) < (B) ? (A):(B))
34 #endif
35
36 // Embedded fonts
37 extern uint8_t tft_SmallFont[];
38 extern uint8_t tft_DefaultFont[];
39 extern uint8_t tft_Dejavu18[];
40 extern uint8_t tft_Dejavu24[];
41 extern uint8_t tft_Ubuntu16[];
42 extern uint8_t tft_Comic24[];
43 extern uint8_t tft_minya24[];
44 extern uint8_t tft_tooney32[];
45 extern uint8_t tft_def_small[];
46
47 // ==== Color definitions constants ==============
48 const color_t TFT_BLACK = { 0, 0, 0 };
49 const color_t TFT_NAVY = { 0, 0, 128 };
50 const color_t TFT_DARKGREEN = { 0, 128, 0 };
51 const color_t TFT_DARKCYAN = { 0, 128, 128 };
52 const color_t TFT_MAROON = { 128, 0, 0 };
53 const color_t TFT_PURPLE = { 128, 0, 128 };
54 const color_t TFT_OLIVE = { 128, 128, 0 };
55 const color_t TFT_LIGHTGREY = { 192, 192, 192 };
56 const color_t TFT_DARKGREY = { 128, 128, 128 };
57 const color_t TFT_BLUE = { 0, 0, 255 };
58 const color_t TFT_GREEN = { 0, 255, 0 };
59 const color_t TFT_CYAN = { 0, 255, 255 };
60 const color_t TFT_RED = { 255, 0, 0 };
61 const color_t TFT_MAGENTA = { 255, 0, 255 };
62 const color_t TFT_YELLOW = { 255, 255, 0 };
63 const color_t TFT_WHITE = { 255, 255, 255 };
64 const color_t TFT_ORANGE = { 255, 164, 0 };
65 const color_t TFT_GREENYELLOW = { 172, 255, 44 };
66 const color_t TFT_PINK = { 255, 192, 202 };
67 // ===============================================
68
69 // ==============================================================
70 // ==== Set default values of global variables ==================
71 uint8_t orientation = LANDSCAPE;// screen orientation
72 uint16_t font_rotate = 0; // font rotation
73 uint8_t font_transparent = 0;
74 uint8_t font_forceFixed = 0;
75 uint8_t text_wrap = 0; // character wrapping to new line
76 color_t _fg = { 0, 255, 0};
77 color_t _bg = { 0, 0, 0};
78 uint8_t image_debug = 0;
79
80 float _angleOffset = DEFAULT_ANGLE_OFFSET;
81
82 int TFT_X = 0;
83 int TFT_Y = 0;
84
85 uint16_t tp_xleft = 300;
86 uint16_t tp_xright = 3550;
87 uint16_t tp_ytop = 3800;
88 uint16_t tp_ybottom = 300;
89
90 dispWin_t dispWin = {
91 .x1 = 0,
92 .y1 = 0,
93 .x2 = DEFAULT_TFT_DISPLAY_WIDTH -1,
94 .y2 = DEFAULT_TFT_DISPLAY_HEIGHT -1,
95 };
96
97 Font cfont = {
98 .font = tft_DefaultFont,
99 .x_size = 0,
100 .y_size = 0x0B,
101 .offset = 0,
102 .numchars = 95,
103 .bitmap = 1,
104 };
105
106 uint8_t font_buffered_char = 1;
107 uint8_t font_line_space = 0;
108 // ==============================================================
109
110
111 typedef struct {
112 uint8_t charCode;
113 int adjYOffset;
114 int width;
115 int height;
116 int xOffset;
117 int xDelta;
118 uint16_t dataPtr;
119 } propFont;
120
121 static dispWin_t dispWinTemp;
122
123 static uint8_t *userfont = NULL;
124 static int TFT_OFFSET = 0;
125 static propFont fontChar;
126 static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX;
127
128
129 // =========================================================================
130 // ** All drawings are clipped to 'dispWin' **
131 // ** All x,y coordinates in public functions are relative to clip window **
132 // =========== : Public functions
133 // ----------- : Local functions
134 // =========================================================================
135
136
137 // Compare two colors; return 0 if equal
138 //============================================
139 int TFT_compare_colors(color_t c1, color_t c2)
140 {
141 if ((c1.r & 0xFC) != (c2.r & 0xFC)) return 1;
142 if ((c1.g & 0xFC) != (c2.g & 0xFC)) return 1;
143 if ((c1.b & 0xFC) != (c2.b & 0xFC)) return 1;
144
145 return 0;
146 }
147
148 // draw color pixel on screen
149 //------------------------------------------------------------------------
150 static void _drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
151
152 if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
153 drawPixel(x, y, color, sel);
154 VncDrawPixel(x, y, VNC_RGB2COL(color.r, color.g, color.b));
155 }
156
157 //====================================================================
158 void TFT_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
159
160 _drawPixel(x+dispWin.x1, y+dispWin.y1, color, sel);
161 }
162
163 //===========================================
164 color_t TFT_readPixel(int16_t x, int16_t y) {
165
166 if ((x < dispWin.x1) || (y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return TFT_BLACK;
167
168 return readPixel(x, y);
169 }
170
171 //--------------------------------------------------------------------------
172 static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
173 // clipping
174 if ((x < dispWin.x1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
175 if (y < dispWin.y1) {
176 h -= (dispWin.y1 - y);
177 y = dispWin.y1;
178 }
179 if (h < 0) h = 0;
180 if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1;
181 if (h == 0) h = 1;
182 TFT_pushColorRep(x, y, x, y+h-1, color, (uint32_t)h);
183 VncDrawVertLine(x, y, y+h-1, VNC_RGB2COL(color.r, color.g, color.b));
184 }
185
186 //--------------------------------------------------------------------------
187 static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
188 // clipping
189 if ((y < dispWin.y1) || (x > dispWin.x2) || (y > dispWin.y2)) return;
190 if (x < dispWin.x1) {
191 w -= (dispWin.x1 - x);
192 x = dispWin.x1;
193 }
194 if (w < 0) w = 0;
195 if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1;
196 if (w == 0) w = 1;
197
198 TFT_pushColorRep(x, y, x+w-1, y, color, (uint32_t)w);
199 VncDrawHorzLine(x, x+w-1, y, VNC_RGB2COL(color.r, color.g, color.b));
200 }
201
202 //======================================================================
203 void TFT_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
204 _drawFastVLine(x+dispWin.x1, y+dispWin.y1, h, color);
205 }
206
207 //======================================================================
208 void TFT_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
209 _drawFastHLine(x+dispWin.x1, y+dispWin.y1, w, color);
210 }
211
212 // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses
213 // the eficient FastH/V Line draw routine for segments of 2 pixels or more
214 //----------------------------------------------------------------------------------
215 static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
216 {
217 if (x0 == x1) {
218 if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color);
219 else _drawFastVLine(x0, y1, y0-y1, color);
220 return;
221 }
222 if (y0 == y1) {
223 if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color);
224 else _drawFastHLine(x1, y0, x0-x1, color);
225 return;
226 }
227
228 int steep = 0;
229 if (abs(y1 - y0) > abs(x1 - x0)) steep = 1;
230 if (steep) {
231 swap(x0, y0);
232 swap(x1, y1);
233 }
234 if (x0 > x1) {
235 swap(x0, x1);
236 swap(y0, y1);
237 }
238
239 int16_t dx = x1 - x0, dy = abs(y1 - y0);
240 int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
241
242 if (y0 < y1) ystep = 1;
243
244 // Split into steep and not steep for FastH/V separation
245 if (steep) {
246 for (; x0 <= x1; x0++) {
247 dlen++;
248 err -= dy;
249 if (err < 0) {
250 err += dx;
251 if (dlen == 1) _drawPixel(y0, xs, color, 1);
252 else _drawFastVLine(y0, xs, dlen, color);
253 dlen = 0; y0 += ystep; xs = x0 + 1;
254 }
255 }
256 if (dlen) _drawFastVLine(y0, xs, dlen, color);
257 }
258 else
259 {
260 for (; x0 <= x1; x0++) {
261 dlen++;
262 err -= dy;
263 if (err < 0) {
264 err += dx;
265 if (dlen == 1) _drawPixel(xs, y0, color, 1);
266 else _drawFastHLine(xs, y0, dlen, color);
267 dlen = 0; y0 += ystep; xs = x0 + 1;
268 }
269 }
270 if (dlen) _drawFastHLine(xs, y0, dlen, color);
271 }
272 }
273
274 //==============================================================================
275 void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
276 {
277 _drawLine(x0+dispWin.x1, y0+dispWin.y1, x1+dispWin.x1, y1+dispWin.y1, color);
278 }
279
280 // fill a rectangle
281 //--------------------------------------------------------------------------------
282 static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
283 // clipping
284 if ((x >= dispWin.x2) || (y > dispWin.y2)) return;
285
286 if (x < dispWin.x1) {
287 w -= (dispWin.x1 - x);
288 x = dispWin.x1;
289 }
290 if (y < dispWin.y1) {
291 h -= (dispWin.y1 - y);
292 y = dispWin.y1;
293 }
294 if (w < 0) w = 0;
295 if (h < 0) h = 0;
296
297 if ((x + w) > (dispWin.x2+1)) w = dispWin.x2 - x + 1;
298 if ((y + h) > (dispWin.y2+1)) h = dispWin.y2 - y + 1;
299 if (w == 0) w = 1;
300 if (h == 0) h = 1;
301 TFT_pushColorRep(x, y, x+w-1, y+h-1, color, (uint32_t)(h*w));
302 VncFillRect(x, y, x+w-1, y+h-1, VNC_RGB2COL(color.r, color.g, color.b));
303 }
304
305 //============================================================================
306 void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
307 _fillRect(x+dispWin.x1, y+dispWin.y1, w, h, color);
308 }
309
310 //==================================
311 void TFT_fillScreen(color_t color) {
312 TFT_pushColorRep(0, 0, _width-1, _height-1, color, (uint32_t)(_height*_width));
313 VncCls(VNC_RGB2COL(color.r, color.g, color.b));
314 }
315
316 //==================================
317 void TFT_fillWindow(color_t color) {
318 TFT_pushColorRep(dispWin.x1, dispWin.y1, dispWin.x2, dispWin.y2,
319 color, (uint32_t)((dispWin.x2-dispWin.x1+1) * (dispWin.y2-dispWin.y1+1)));
320 VncFillRect(dispWin.x1, dispWin.y1, dispWin.x2, dispWin.y2, VNC_RGB2COL(color.r, color.g, color.b));
321 }
322
323 // ^^^============= Basics drawing functions ================================^^^
324
325
326 // ================ Graphics drawing functions ==================================
327
328 //-----------------------------------------------------------------------------------
329 static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
330 _drawFastHLine(x1,y1,w, color);
331 _drawFastVLine(x1+w-1,y1,h, color);
332 _drawFastHLine(x1,y1+h-1,w, color);
333 _drawFastVLine(x1,y1,h, color);
334 }
335
336 //===============================================================================
337 void TFT_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
338 _drawRect(x1+dispWin.x1, y1+dispWin.y1, w, h, color);
339 }
340
341 //-------------------------------------------------------------------------------------------------
342 static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color)
343 {
344 int16_t f = 1 - r;
345 int16_t ddF_x = 1;
346 int16_t ddF_y = -2 * r;
347 int16_t x = 0;
348 int16_t y = r;
349
350 disp_select();
351 while (x < y) {
352 if (f >= 0) {
353 y--;
354 ddF_y += 2;
355 f += ddF_y;
356 }
357 x++;
358 ddF_x += 2;
359 f += ddF_x;
360 if (cornername & 0x4) {
361 _drawPixel(x0 + x, y0 + y, color, 0);
362 _drawPixel(x0 + y, y0 + x, color, 0);
363 }
364 if (cornername & 0x2) {
365 _drawPixel(x0 + x, y0 - y, color, 0);
366 _drawPixel(x0 + y, y0 - x, color, 0);
367 }
368 if (cornername & 0x8) {
369 _drawPixel(x0 - y, y0 + x, color, 0);
370 _drawPixel(x0 - x, y0 + y, color, 0);
371 }
372 if (cornername & 0x1) {
373 _drawPixel(x0 - y, y0 - x, color, 0);
374 _drawPixel(x0 - x, y0 - y, color, 0);
375 }
376 }
377 disp_deselect();
378 }
379
380 // Used to do circles and roundrects
381 //----------------------------------------------------------------------------------------------------------------
382 static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, color_t color)
383 {
384 int16_t f = 1 - r;
385 int16_t ddF_x = 1;
386 int16_t ddF_y = -2 * r;
387 int16_t x = 0;
388 int16_t y = r;
389 int16_t ylm = x0 - r;
390
391 while (x < y) {
392 if (f >= 0) {
393 if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
394 if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
395 ylm = x0 - y;
396 y--;
397 ddF_y += 2;
398 f += ddF_y;
399 }
400 x++;
401 ddF_x += 2;
402 f += ddF_x;
403
404 if ((x0 - x) > ylm) {
405 if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
406 if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
407 }
408 }
409 }
410
411 // Draw a rounded rectangle
412 //=============================================================================================
413 void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
414 {
415 x += dispWin.x1;
416 y += dispWin.y1;
417
418 // smarter version
419 _drawFastHLine(x + r, y, w - 2 * r, color); // Top
420 _drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
421 _drawFastVLine(x, y + r, h - 2 * r, color); // Left
422 _drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
423
424 // draw four corners
425 drawCircleHelper(x + r, y + r, r, 1, color);
426 drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
427 drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
428 drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
429 }
430
431 // Fill a rounded rectangle
432 //=============================================================================================
433 void TFT_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
434 {
435 x += dispWin.x1;
436 y += dispWin.y1;
437
438 // smarter version
439 _fillRect(x + r, y, w - 2 * r, h, color);
440
441 // draw four corners
442 fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
443 fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
444 }
445
446
447
448
449 //-----------------------------------------------------------------------------------------------
450 static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color)
451 {
452 _drawLine(
453 x,
454 y,
455 x + length * cos((angle + _angleOffset) * DEG_TO_RAD),
456 y + length * sin((angle + _angleOffset) * DEG_TO_RAD), color);
457 }
458
459 //---------------------------------------------------------------------------------------------------------------
460 static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color)
461 {
462 _drawLine(
463 x + start * cos((angle + _angleOffset) * DEG_TO_RAD),
464 y + start * sin((angle + _angleOffset) * DEG_TO_RAD),
465 x + (start + length) * cos((angle + _angleOffset) * DEG_TO_RAD),
466 y + (start + length) * sin((angle + _angleOffset) * DEG_TO_RAD), color);
467 }
468
469 //===========================================================================================================
470 void TFT_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color)
471 {
472 x += dispWin.x1;
473 y += dispWin.y1;
474
475 if (start == 0) _drawLineByAngle(x, y, angle, len, color);
476 else _DrawLineByAngle(x, y, angle, start, len, color);
477 }
478
479
480 // Draw a triangle
481 //--------------------------------------------------------------------------------------------------------------------
482 static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
483 {
484 _drawLine(x0, y0, x1, y1, color);
485 _drawLine(x1, y1, x2, y2, color);
486 _drawLine(x2, y2, x0, y0, color);
487 }
488
489 //================================================================================================================
490 void TFT_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
491 {
492 x0 += dispWin.x1;
493 y0 += dispWin.y1;
494 x1 += dispWin.x1;
495 y1 += dispWin.y1;
496 x2 += dispWin.x1;
497 y2 += dispWin.y1;
498
499 _drawLine(x0, y0, x1, y1, color);
500 _drawLine(x1, y1, x2, y2, color);
501 _drawLine(x2, y2, x0, y0, color);
502 }
503
504 // Fill a triangle
505 //--------------------------------------------------------------------------------------------------------------------
506 static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
507 {
508 int16_t a, b, y, last;
509
510 // Sort coordinates by Y order (y2 >= y1 >= y0)
511 if (y0 > y1) {
512 swap(y0, y1); swap(x0, x1);
513 }
514 if (y1 > y2) {
515 swap(y2, y1); swap(x2, x1);
516 }
517 if (y0 > y1) {
518 swap(y0, y1); swap(x0, x1);
519 }
520
521 if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
522 a = b = x0;
523 if(x1 < a) a = x1;
524 else if(x1 > b) b = x1;
525 if(x2 < a) a = x2;
526 else if(x2 > b) b = x2;
527 _drawFastHLine(a, y0, b-a+1, color);
528 return;
529 }
530
531 int16_t
532 dx01 = x1 - x0,
533 dy01 = y1 - y0,
534 dx02 = x2 - x0,
535 dy02 = y2 - y0,
536 dx12 = x2 - x1,
537 dy12 = y2 - y1;
538 int32_t
539 sa = 0,
540 sb = 0;
541
542 // For upper part of triangle, find scanline crossings for segments
543 // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
544 // is included here (and second loop will be skipped, avoiding a /0
545 // error there), otherwise scanline y1 is skipped here and handled
546 // in the second loop...which also avoids a /0 error here if y0=y1
547 // (flat-topped triangle).
548 if(y1 == y2) last = y1; // Include y1 scanline
549 else last = y1-1; // Skip it
550
551 for(y=y0; y<=last; y++) {
552 a = x0 + sa / dy01;
553 b = x0 + sb / dy02;
554 sa += dx01;
555 sb += dx02;
556 /* longhand:
557 a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
558 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
559 */
560 if(a > b) swap(a,b);
561 _drawFastHLine(a, y, b-a+1, color);
562 }
563
564 // For lower part of triangle, find scanline crossings for segments
565 // 0-2 and 1-2. This loop is skipped if y1=y2.
566 sa = dx12 * (y - y1);
567 sb = dx02 * (y - y0);
568 for(; y<=y2; y++) {
569 a = x1 + sa / dy12;
570 b = x0 + sb / dy02;
571 sa += dx12;
572 sb += dx02;
573 /* longhand:
574 a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
575 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
576 */
577 if(a > b) swap(a,b);
578 _drawFastHLine(a, y, b-a+1, color);
579 }
580 }
581
582 //================================================================================================================
583 void TFT_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
584 {
585 _fillTriangle(
586 x0 + dispWin.x1, y0 + dispWin.y1,
587 x1 + dispWin.x1, y1 + dispWin.y1,
588 x2 + dispWin.x1, y2 + dispWin.y1,
589 color);
590 }
591
592 //====================================================================
593 void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color) {
594 x += dispWin.x1;
595 y += dispWin.y1;
596 int f = 1 - radius;
597 int ddF_x = 1;
598 int ddF_y = -2 * radius;
599 int x1 = 0;
600 int y1 = radius;
601
602 disp_select();
603 _drawPixel(x, y + radius, color, 0);
604 _drawPixel(x, y - radius, color, 0);
605 _drawPixel(x + radius, y, color, 0);
606 _drawPixel(x - radius, y, color, 0);
607 while(x1 < y1) {
608 if (f >= 0) {
609 y1--;
610 ddF_y += 2;
611 f += ddF_y;
612 }
613 x1++;
614 ddF_x += 2;
615 f += ddF_x;
616 _drawPixel(x + x1, y + y1, color, 0);
617 _drawPixel(x - x1, y + y1, color, 0);
618 _drawPixel(x + x1, y - y1, color, 0);
619 _drawPixel(x - x1, y - y1, color, 0);
620 _drawPixel(x + y1, y + x1, color, 0);
621 _drawPixel(x - y1, y + x1, color, 0);
622 _drawPixel(x + y1, y - x1, color, 0);
623 _drawPixel(x - y1, y - x1, color, 0);
624 }
625 disp_deselect();
626 }
627
628 //====================================================================
629 void TFT_fillCircle(int16_t x, int16_t y, int radius, color_t color) {
630 x += dispWin.x1;
631 y += dispWin.y1;
632
633 _drawFastVLine(x, y-radius, 2*radius+1, color);
634 fillCircleHelper(x, y, radius, 3, 0, color);
635 }
636
637 //----------------------------------------------------------------------------------------------------------------
638 static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
639 {
640 disp_select();
641 // upper right
642 if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color, 0);
643 // upper left
644 if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color, 0);
645 // lower right
646 if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color, 0);
647 // lower left
648 if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color, 0);
649 disp_deselect();
650 }
651
652 //=====================================================================================================
653 void TFT_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
654 {
655 x0 += dispWin.x1;
656 y0 += dispWin.y1;
657
658 uint16_t x, y;
659 int32_t xchg, ychg;
660 int32_t err;
661 int32_t rxrx2;
662 int32_t ryry2;
663 int32_t stopx, stopy;
664
665 rxrx2 = rx;
666 rxrx2 *= rx;
667 rxrx2 *= 2;
668
669 ryry2 = ry;
670 ryry2 *= ry;
671 ryry2 *= 2;
672
673 x = rx;
674 y = 0;
675
676 xchg = 1;
677 xchg -= rx;
678 xchg -= rx;
679 xchg *= ry;
680 xchg *= ry;
681
682 ychg = rx;
683 ychg *= rx;
684
685 err = 0;
686
687 stopx = ryry2;
688 stopx *= rx;
689 stopy = 0;
690
691 while( stopx >= stopy ) {
692 _draw_ellipse_section(x, y, x0, y0, color, option);
693 y++;
694 stopy += rxrx2;
695 err += ychg;
696 ychg += rxrx2;
697 if ( 2*err+xchg > 0 ) {
698 x--;
699 stopx -= ryry2;
700 err += xchg;
701 xchg += ryry2;
702 }
703 }
704
705 x = 0;
706 y = ry;
707
708 xchg = ry;
709 xchg *= ry;
710
711 ychg = 1;
712 ychg -= ry;
713 ychg -= ry;
714 ychg *= rx;
715 ychg *= rx;
716
717 err = 0;
718
719 stopx = 0;
720
721 stopy = rxrx2;
722 stopy *= ry;
723
724 while( stopx <= stopy ) {
725 _draw_ellipse_section(x, y, x0, y0, color, option);
726 x++;
727 stopx += ryry2;
728 err += xchg;
729 xchg += ryry2;
730 if ( 2*err+ychg > 0 ) {
731 y--;
732 stopy -= rxrx2;
733 err += ychg;
734 ychg += rxrx2;
735 }
736 }
737 }
738
739 //-----------------------------------------------------------------------------------------------------------------------
740 static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
741 {
742 // upper right
743 if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color);
744 // upper left
745 if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color);
746 // lower right
747 if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color);
748 // lower left
749 if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color);
750 }
751
752 //=====================================================================================================
753 void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
754 {
755 x0 += dispWin.x1;
756 y0 += dispWin.y1;
757
758 uint16_t x, y;
759 int32_t xchg, ychg;
760 int32_t err;
761 int32_t rxrx2;
762 int32_t ryry2;
763 int32_t stopx, stopy;
764
765 rxrx2 = rx;
766 rxrx2 *= rx;
767 rxrx2 *= 2;
768
769 ryry2 = ry;
770 ryry2 *= ry;
771 ryry2 *= 2;
772
773 x = rx;
774 y = 0;
775
776 xchg = 1;
777 xchg -= rx;
778 xchg -= rx;
779 xchg *= ry;
780 xchg *= ry;
781
782 ychg = rx;
783 ychg *= rx;
784
785 err = 0;
786
787 stopx = ryry2;
788 stopx *= rx;
789 stopy = 0;
790
791 while( stopx >= stopy ) {
792 _draw_filled_ellipse_section(x, y, x0, y0, color, option);
793 y++;
794 stopy += rxrx2;
795 err += ychg;
796 ychg += rxrx2;
797 if ( 2*err+xchg > 0 ) {
798 x--;
799 stopx -= ryry2;
800 err += xchg;
801 xchg += ryry2;
802 }
803 }
804
805 x = 0;
806 y = ry;
807
808 xchg = ry;
809 xchg *= ry;
810
811 ychg = 1;
812 ychg -= ry;
813 ychg -= ry;
814 ychg *= rx;
815 ychg *= rx;
816
817 err = 0;
818
819 stopx = 0;
820
821 stopy = rxrx2;
822 stopy *= ry;
823
824 while( stopx <= stopy ) {
825 _draw_filled_ellipse_section(x, y, x0, y0, color, option);
826 x++;
827 stopx += ryry2;
828 err += xchg;
829 xchg += ryry2;
830 if ( 2*err+ychg > 0 ) {
831 y--;
832 stopy -= rxrx2;
833 err += ychg;
834 ychg += rxrx2;
835 }
836 }
837 }
838
839
840 // ==== ARC DRAWING ===================================================================
841
842 //---------------------------------------------------------------------------------------------------------------------------------
843 static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color)
844 {
845 //float sslope = (float)cos_lookup(start) / (float)sin_lookup(start);
846 //float eslope = (float)cos_lookup(end) / (float)sin_lookup(end);
847 float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ;
848 float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax);
849
850 if (end == 360) eslope = -1000000;
851
852 int ir2 = (radius - thickness) * (radius - thickness);
853 int or2 = radius * radius;
854
855 disp_select();
856 for (int x = -radius; x <= radius; x++) {
857 for (int y = -radius; y <= radius; y++) {
858 int x2 = x * x;
859 int y2 = y * y;
860
861 if (
862 (x2 + y2 < or2 && x2 + y2 >= ir2) &&
863 (
864 (y > 0 && start < 180 && x <= y * sslope) ||
865 (y < 0 && start > 180 && x >= y * sslope) ||
866 (y < 0 && start <= 180) ||
867 (y == 0 && start <= 180 && x < 0) ||
868 (y == 0 && start == 0 && x > 0)
869 ) &&
870 (
871 (y > 0 && end < 180 && x >= y * eslope) ||
872 (y < 0 && end > 180 && x <= y * eslope) ||
873 (y > 0 && end >= 180) ||
874 (y == 0 && end >= 180 && x < 0) ||
875 (y == 0 && start == 0 && x > 0)
876 )
877 )
878 _drawPixel(cx+x, cy+y, color, 0);
879 }
880 }
881 disp_deselect();
882 }
883
884
885 //===========================================================================================================================
886 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)
887 {
888 cx += dispWin.x1;
889 cy += dispWin.y1;
890
891 if (th < 1) th = 1;
892 if (th > r) th = r;
893
894 int f = TFT_compare_colors(fillcolor, color);
895
896 float astart = fmodf(start, _arcAngleMax);
897 float aend = fmodf(end, _arcAngleMax);
898
899 astart += _angleOffset;
900 aend += _angleOffset;
901
902 if (astart < 0) astart += (float)360;
903 if (aend < 0) aend += (float)360;
904
905 if (aend == 0) aend = (float)360;
906
907 if (astart > aend) {
908 _fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor);
909 _fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor);
910 if (f) {
911 _fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color);
912 _fillArcOffsetted(cx, cy, r, 1, 0, aend, color);
913 _fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color);
914 _fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color);
915 }
916 }
917 else {
918 _fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor);
919 if (f) {
920 _fillArcOffsetted(cx, cy, r, 1, astart, aend, color);
921 _fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color);
922 }
923 }
924 if (f) {
925 _drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD),
926 cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color);
927 _drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD),
928 cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color);
929 }
930 }
931
932 //=============================================================================================================
933 void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th)
934 {
935 cx += dispWin.x1;
936 cy += dispWin.y1;
937
938 int deg = rot - _angleOffset;
939 int f = TFT_compare_colors(fill, color);
940
941 if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number
942 if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES; // This ensures the maximum side number
943
944 int Xpoints[sides], Ypoints[sides]; // Set the arrays based on the number of sides entered
945 int rads = 360 / sides; // This equally spaces the points.
946
947 for (int idx = 0; idx < sides; idx++) {
948 Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter;
949 Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter;
950 }
951
952 // Draw the polygon on the screen.
953 if (f) {
954 for(int idx = 0; idx < sides; idx++) {
955 if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill);
956 else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill);
957 }
958 }
959
960 if (th) {
961 for (int n=0; n<th; n++) {
962 if (n > 0) {
963 for (int idx = 0; idx < sides; idx++) {
964 Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
965 Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
966 }
967 }
968 for(int idx = 0; idx < sides; idx++) {
969 if( (idx+1) < sides)
970 _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines
971 else
972 _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon.
973 }
974 }
975 }
976 }
977
978 /*
979 // Similar to the Polygon function.
980 //=====================================================================================
981 void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor)
982 {
983 cx += dispWin.x1;
984 cy += dispWin.y1;
985
986 factor = constrain(factor, 1.0, 4.0);
987 uint8_t sides = 5;
988 uint8_t rads = 360 / sides;
989
990 int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5];
991
992 for(int idx = 0; idx < sides; idx++) {
993 // makes the outer points
994 Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter;
995 Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter;
996 // makes the inner points
997 Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
998 // 36 is half of 72, and this will allow the inner and outer points to line up like a triangle.
999 Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
1000 }
1001
1002 for(int idx = 0; idx < sides; idx++) {
1003 if((idx+1) < sides) {
1004 if(fill) {// this part below should be self explanatory. It fills in the star.
1005 _fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
1006 _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
1007 }
1008 else {
1009 _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
1010 _drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
1011 }
1012 }
1013 else {
1014 if(fill) {
1015 _fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
1016 _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
1017 }
1018 else {
1019 _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
1020 _drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
1021 }
1022 }
1023 }
1024 }
1025 */
1026
1027 // ================ Font and string functions ==================================
1028
1029 //--------------------------------------------------------
1030 static int load_file_font(const char * fontfile, int info)
1031 {
1032 int err = 0;
1033 char err_msg[256] = {'\0'};
1034
1035 if (userfont != NULL) {
1036 free(userfont);
1037 userfont = NULL;
1038 }
1039
1040 struct stat sb;
1041
1042 // Open the file
1043 FILE *fhndl = fopen(fontfile, "r");
1044 if (!fhndl) {
1045 sprintf(err_msg, "Error opening font file '%s'", fontfile);
1046 err = 1;
1047 goto exit;
1048 }
1049
1050 // Get file size
1051 if (stat(fontfile, &sb) != 0) {
1052 sprintf(err_msg, "Error getting font file size");
1053 err = 2;
1054 goto exit;
1055 }
1056 int fsize = sb.st_size;
1057 if (fsize < 30) {
1058 sprintf(err_msg, "Error getting font file size");
1059 err = 3;
1060 goto exit;
1061 }
1062
1063 userfont = malloc(fsize+4);
1064 if (userfont == NULL) {
1065 sprintf(err_msg, "Font memory allocation error");
1066 fclose(fhndl);
1067 err = 4;
1068 goto exit;
1069 }
1070
1071 int read = fread(userfont, 1, fsize, fhndl);
1072
1073 fclose(fhndl);
1074
1075 if (read != fsize) {
1076 sprintf(err_msg, "Font read error");
1077 err = 5;
1078 goto exit;
1079 }
1080
1081 userfont[read] = 0;
1082 if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) {
1083 sprintf(err_msg, "Font ID not found");
1084 err = 6;
1085 goto exit;
1086 }
1087
1088 // Check size
1089 int size = 0;
1090 int numchar = 0;
1091 int width = userfont[0];
1092 int height = userfont[1];
1093 uint8_t first = 255;
1094 uint8_t last = 0;
1095 //int offst = 0;
1096 int pminwidth = 255;
1097 int pmaxwidth = 0;
1098
1099 if (width != 0) {
1100 // Fixed font
1101 numchar = userfont[3];
1102 first = userfont[2];
1103 last = first + numchar - 1;
1104 size = ((width * height * numchar) / 8) + 4;
1105 }
1106 else {
1107 // Proportional font
1108 size = 4; // point at first char data
1109 uint8_t charCode;
1110 int charwidth;
1111
1112 do {
1113 charCode = userfont[size];
1114 charwidth = userfont[size+2];
1115
1116 if (charCode != 0xFF) {
1117 numchar++;
1118 if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7);
1119 else size += 6;
1120
1121 if (info) {
1122 if (charwidth > pmaxwidth) pmaxwidth = charwidth;
1123 if (charwidth < pminwidth) pminwidth = charwidth;
1124 if (charCode < first) first = charCode;
1125 if (charCode > last) last = charCode;
1126 }
1127 }
1128 else size++;
1129 } while ((size < (read-8)) && (charCode != 0xFF));
1130 }
1131
1132 if (size != (read-8)) {
1133 sprintf(err_msg, "Font size error: found %d expected %d)", size, (read-8));
1134 err = 7;
1135 goto exit;
1136 }
1137
1138 if (info) {
1139 if (width != 0) {
1140 printf("Fixed width font:\r\n size: %d width: %d height: %d characters: %d (%d~%d)\n",
1141 size, width, height, numchar, first, last);
1142 }
1143 else {
1144 printf("Proportional font:\r\n size: %d width: %d~%d height: %d characters: %d (%d~%d)\n",
1145 size, pminwidth, pmaxwidth, height, numchar, first, last);
1146 }
1147 }
1148
1149 exit:
1150 if (err) {
1151 if (userfont) {
1152 free(userfont);
1153 userfont = NULL;
1154 }
1155 if (info) printf("Error: %d [%s]\r\n", err, err_msg);
1156 }
1157 return err;
1158 }
1159
1160 //------------------------------------------------
1161 int compile_font_file(char *fontfile, uint8_t dbg)
1162 {
1163 int err = 0;
1164 char err_msg[128] = {'\0'};
1165 char outfile[128] = {'\0'};
1166 size_t len;
1167 struct stat sb;
1168 FILE *ffd = NULL;
1169 FILE *ffd_out = NULL;
1170 char *sourcebuf = NULL;
1171
1172 len = strlen(fontfile);
1173
1174 // check here that filename end with ".c".
1175 if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) {
1176 sprintf(err_msg, "not a .c file");
1177 err = 1;
1178 goto exit;
1179 }
1180
1181 sprintf(outfile, "%s", fontfile);
1182 sprintf(outfile+strlen(outfile)-1, "fon");
1183
1184 // Open the source file
1185 if (stat(fontfile, &sb) != 0) {
1186 sprintf(err_msg, "Error opening source file '%s'", fontfile);
1187 err = 2;
1188 goto exit;
1189 }
1190 // Open the file
1191 ffd = fopen(fontfile, "rb");
1192 if (!ffd) {
1193 sprintf(err_msg, "Error opening source file '%s'", fontfile);
1194 err = 3;
1195 goto exit;
1196 }
1197
1198 // Open the font file
1199 ffd_out= fopen(outfile, "wb");
1200 if (!ffd_out) {
1201 sprintf(err_msg, "error opening destination file");
1202 err = 4;
1203 goto exit;
1204 }
1205
1206 // Get file size
1207 int fsize = sb.st_size;
1208 if (fsize <= 0) {
1209 sprintf(err_msg, "source file size error");
1210 err = 5;
1211 goto exit;
1212 }
1213
1214 sourcebuf = malloc(fsize+4);
1215 if (sourcebuf == NULL) {
1216 sprintf(err_msg, "memory allocation error");
1217 err = 6;
1218 goto exit;
1219 }
1220 char *fbuf = sourcebuf;
1221
1222 int rdsize = fread(fbuf, 1, fsize, ffd);
1223 fclose(ffd);
1224 ffd = NULL;
1225
1226 if (rdsize != fsize) {
1227 sprintf(err_msg, "error reading from source file");
1228 err = 7;
1229 goto exit;
1230 }
1231
1232 *(fbuf+rdsize) = '\0';
1233
1234 fbuf = strchr(fbuf, '{'); // beginning of font data
1235 char *fend = strstr(fbuf, "};"); // end of font data
1236
1237 if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) {
1238 sprintf(err_msg, "wrong source file format");
1239 err = 8;
1240 goto exit;
1241 }
1242
1243 fbuf++;
1244 *fend = '\0';
1245 char hexstr[5] = {'\0'};
1246 int lastline = 0;
1247
1248 fbuf = strstr(fbuf, "0x");
1249 int size = 0;
1250 char *nextline;
1251 char *numptr;
1252
1253 int bptr = 0;
1254
1255 while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) {
1256 nextline = strchr(fbuf, '\n'); // beginning of the next line
1257 if (nextline == NULL) {
1258 nextline = fend-1;
1259 lastline++;
1260 }
1261 else nextline++;
1262
1263 while (fbuf < nextline) {
1264 numptr = strstr(fbuf, "0x");
1265 if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X");
1266 if ((numptr != NULL) && ((numptr+4) <= nextline)) {
1267 fbuf = numptr;
1268 if (bptr >= 128) {
1269 // buffer full, write to file
1270 if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error;
1271 bptr = 0;
1272 size += 128;
1273 }
1274 memcpy(hexstr, fbuf, 4);
1275 hexstr[4] = 0;
1276 outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0);
1277 fbuf += 4;
1278 }
1279 else fbuf = nextline;
1280 }
1281 fbuf = nextline;
1282 }
1283
1284 if (bptr > 0) {
1285 size += bptr;
1286 if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error;
1287 }
1288
1289 // write font ID
1290 sprintf(outfile, "RPH_font");
1291 if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error;
1292
1293 fclose(ffd_out);
1294 ffd_out = NULL;
1295
1296 // === Test compiled font ===
1297 sprintf(outfile, "%s", fontfile);
1298 sprintf(outfile+strlen(outfile)-1, "fon");
1299
1300 uint8_t *uf = userfont; // save userfont pointer
1301 userfont = NULL;
1302 if (load_file_font(outfile, 1) != 0) {
1303 sprintf(err_msg, "Error compiling file!");
1304 err = 10;
1305 }
1306 else {
1307 free(userfont);
1308 sprintf(err_msg, "File compiled successfully.");
1309 }
1310 userfont = uf; // restore userfont
1311
1312 goto exit;
1313
1314 error:
1315 sprintf(err_msg, "error writing to destination file");
1316 err = 9;
1317
1318 exit:
1319 if (sourcebuf) free(sourcebuf);
1320 if (ffd) fclose(ffd);
1321 if (ffd_out) fclose(ffd_out);
1322
1323 if (dbg) printf("%s\r\n", err_msg);
1324
1325 return err;
1326 }
1327
1328
1329 // -----------------------------------------------------------------------------------------
1330 // Individual Proportional Font Character Format:
1331 // -----------------------------------------------------------------------------------------
1332 // Character Code
1333 // yOffset (start Y of visible pixels)
1334 // Width (width of the visible pixels)
1335 // Height (height of the visible pixels)
1336 // xOffset (start X of visible pixels)
1337 // xDelta (the distance to move the cursor. Effective width of the character.)
1338 // Data[n]
1339 // -----------------------------------------------------------------------------------------
1340
1341 //---------------------------------------------------------------------------------------------
1342 // Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1)
1343 // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
1344 //---------------------------------------------------------------------------------------------
1345
1346 //----------------------------------
1347 void getFontCharacters(uint8_t *buf)
1348 {
1349 if (cfont.bitmap == 2) {
1350 //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
1351 for (uint8_t n=0; n < 11; n++) {
1352 buf[n] = n + 0x30;
1353 }
1354 buf[11] = '.';
1355 buf[12] = '-';
1356 buf[13] = '/';
1357 buf[14] = '\0';
1358 return;
1359 }
1360
1361 if (cfont.x_size > 0) {
1362 for (uint8_t n=0; n < cfont.numchars; n++) {
1363 buf[n] = cfont.offset + n;
1364 }
1365 buf[cfont.numchars] = '\0';
1366 return;
1367 }
1368
1369 uint16_t tempPtr = 4; // point at first char data
1370 uint8_t cc, cw, ch, n;
1371
1372 n = 0;
1373 cc = cfont.font[tempPtr++];
1374 while (cc != 0xFF) {
1375 cfont.numchars++;
1376 tempPtr++;
1377 cw = cfont.font[tempPtr++];
1378 ch = cfont.font[tempPtr++];
1379 tempPtr++;
1380 tempPtr++;
1381 if (cw != 0) {
1382 // packed bits
1383 tempPtr += (((cw * ch)-1) / 8) + 1;
1384 }
1385 buf[n++] = cc;
1386 cc = cfont.font[tempPtr++];
1387 }
1388 buf[n] = '\0';
1389 }
1390
1391 // Set max width & height of the proportional font
1392 //-----------------------------
1393 static void getMaxWidthHeight()
1394 {
1395 uint16_t tempPtr = 4; // point at first char data
1396 uint8_t cc, cw, ch, cd, cy;
1397
1398 cfont.numchars = 0;
1399 cfont.max_x_size = 0;
1400
1401 cc = cfont.font[tempPtr++];
1402 while (cc != 0xFF) {
1403 cfont.numchars++;
1404 cy = cfont.font[tempPtr++];
1405 cw = cfont.font[tempPtr++];
1406 ch = cfont.font[tempPtr++];
1407 tempPtr++;
1408 cd = cfont.font[tempPtr++];
1409 cy += ch;
1410 if (cw > cfont.max_x_size) cfont.max_x_size = cw;
1411 if (cd > cfont.max_x_size) cfont.max_x_size = cd;
1412 if (ch > cfont.y_size) cfont.y_size = ch;
1413 if (cy > cfont.y_size) cfont.y_size = cy;
1414 if (cw != 0) {
1415 // packed bits
1416 tempPtr += (((cw * ch)-1) / 8) + 1;
1417 }
1418 cc = cfont.font[tempPtr++];
1419 }
1420 cfont.size = tempPtr;
1421 }
1422
1423 // Return the Glyph data for an individual character in the proportional font
1424 //------------------------------------
1425 static uint8_t getCharPtr(uint8_t c) {
1426 uint16_t tempPtr = 4; // point at first char data
1427
1428 do {
1429 fontChar.charCode = cfont.font[tempPtr++];
1430 if (fontChar.charCode == 0xFF) return 0;
1431
1432 fontChar.adjYOffset = cfont.font[tempPtr++];
1433 fontChar.width = cfont.font[tempPtr++];
1434 fontChar.height = cfont.font[tempPtr++];
1435 fontChar.xOffset = cfont.font[tempPtr++];
1436 fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
1437 fontChar.xDelta = cfont.font[tempPtr++];
1438
1439 if (c != fontChar.charCode && fontChar.charCode != 0xFF) {
1440 if (fontChar.width != 0) {
1441 // packed bits
1442 tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
1443 }
1444 }
1445 } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF));
1446
1447 fontChar.dataPtr = tempPtr;
1448 if (c == fontChar.charCode) {
1449 if (font_forceFixed > 0) {
1450 // fix width & offset for forced fixed width
1451 fontChar.xDelta = cfont.max_x_size;
1452 fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2;
1453 }
1454 }
1455 else return 0;
1456
1457 return 1;
1458 }
1459
1460 /*
1461 //-----------------------
1462 static void _testFont() {
1463 if (cfont.x_size) {
1464 printf("FONT TEST: fixed font\r\n");
1465 return;
1466 }
1467 uint16_t tempPtr = 4; // point at first char data
1468 uint8_t c = 0x20;
1469 for (c=0x20; c <0xFF; c++) {
1470 fontChar.charCode = cfont.font[tempPtr++];
1471 if (fontChar.charCode == 0xFF) break;
1472 if (fontChar.charCode != c) {
1473 printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c);
1474 break;
1475 }
1476 c = fontChar.charCode;
1477 fontChar.adjYOffset = cfont.font[tempPtr++];
1478 fontChar.width = cfont.font[tempPtr++];
1479 fontChar.height = cfont.font[tempPtr++];
1480 fontChar.xOffset = cfont.font[tempPtr++];
1481 fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
1482 fontChar.xDelta = cfont.font[tempPtr++];
1483
1484 if (fontChar.charCode != 0xFF) {
1485 if (fontChar.width != 0) {
1486 // packed bits
1487 tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
1488 }
1489 }
1490 }
1491 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);
1492 }
1493 */
1494
1495 //===================================================
1496 void TFT_setFont(uint8_t font, const char *font_file)
1497 {
1498 cfont.font = NULL;
1499
1500 if (font == FONT_7SEG) {
1501 cfont.bitmap = 2;
1502 cfont.x_size = 24;
1503 cfont.y_size = 6;
1504 cfont.offset = 0;
1505 cfont.color = _fg;
1506 } else {
1507 if (font == USER_FONT) {
1508 if (load_file_font(font_file, 0) != 0)
1509 cfont.font = tft_DefaultFont;
1510 else
1511 cfont.font = userfont;
1512 } else if (font == DEJAVU18_FONT)
1513 cfont.font = tft_Dejavu18;
1514 else if (font == DEJAVU24_FONT)
1515 cfont.font = tft_Dejavu24;
1516 else if (font == UBUNTU16_FONT)
1517 cfont.font = tft_Ubuntu16;
1518 else if (font == COMIC24_FONT)
1519 cfont.font = tft_Comic24;
1520 else if (font == MINYA24_FONT)
1521 cfont.font = tft_minya24;
1522 else if (font == TOONEY32_FONT)
1523 cfont.font = tft_tooney32;
1524 else if (font == SMALL_FONT)
1525 cfont.font = tft_SmallFont;
1526 else if (font == DEF_SMALL_FONT)
1527 cfont.font = tft_def_small;
1528 else
1529 cfont.font = tft_DefaultFont;
1530
1531 cfont.bitmap = 1;
1532 cfont.x_size = cfont.font[0];
1533 cfont.y_size = cfont.font[1];
1534 if (cfont.x_size > 0) {
1535 cfont.offset = cfont.font[2];
1536 cfont.numchars = cfont.font[3];
1537 cfont.size = cfont.x_size * cfont.y_size * cfont.numchars;
1538 } else {
1539 cfont.offset = 4;
1540 getMaxWidthHeight();
1541 }
1542 //_testFont();
1543 }
1544 }
1545
1546
1547
1548 // -----------------------------------------------------------------------------------------
1549 // Individual Proportional Font Character Format:
1550 // -----------------------------------------------------------------------------------------
1551 // Character Code
1552 // yOffset (start Y of visible pixels)
1553 // Width (width of the visible pixels)
1554 // Height (height of the visible pixels)
1555 // xOffset (start X of visible pixels)
1556 // xDelta (the distance to move the cursor. Effective width of the character.)
1557 // Data[n]
1558 // -----------------------------------------------------------------------------------------
1559 //---------------------------------------------------------------------------------------------
1560 // Character drawing rectangle is (0, 0) (xDelta-1, cfont.y_size-1)
1561 // Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
1562 //---------------------------------------------------------------------------------------------
1563
1564 // print non-rotated proportional character
1565 // character is already in fontChar
1566 //----------------------------------------------
1567 static int printProportionalChar(int x, int y) {
1568 uint8_t ch = 0;
1569 int i, j, char_width;
1570
1571 char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta);
1572
1573 if ((font_buffered_char) && (!font_transparent)) {
1574 int len, bufPos;
1575
1576 // === buffer Glyph data for faster sending ===
1577 len = char_width * cfont.y_size;
1578 color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
1579 if (color_line) {
1580 // fill with background color
1581 for (int n = 0; n < len; n++) {
1582 color_line[n] = _bg;
1583 }
1584 // set character pixels to foreground color
1585 uint8_t mask = 0x80;
1586 for (j = 0; j < fontChar.height; j++) {
1587 for (i = 0; i < fontChar.width; i++) {
1588 if (((i + (j*fontChar.width)) % 8) == 0) {
1589 mask = 0x80;
1590 ch = cfont.font[fontChar.dataPtr++];
1591 }
1592 if ((ch & mask) != 0) {
1593 // visible pixel
1594 bufPos = ((j + fontChar.adjYOffset) * char_width) + (fontChar.xOffset + i); // bufY + bufX
1595 color_line[bufPos] = _fg;
1596 VncDrawPixel(x + (fontChar.xOffset + i), y + (j + fontChar.adjYOffset), VNC_RGB2COL(_fg.r, _fg.g, _fg.b));
1597 } else {
1598 VncDrawPixel(x + (fontChar.xOffset + i), y + (j + fontChar.adjYOffset), VNC_RGB2COL(_bg.r, _bg.g, _bg.b));
1599 }
1600 mask >>= 1;
1601 }
1602 }
1603 // send to display in one transaction
1604 disp_select();
1605 send_data(x, y, x+char_width-1, y+cfont.y_size-1, len, color_line);
1606 disp_deselect();
1607 free(color_line);
1608
1609 return char_width;
1610 }
1611 }
1612
1613 int cx, cy;
1614
1615 if (!font_transparent)
1616 _fillRect(x, y, char_width+1, cfont.y_size, _bg);
1617
1618 // draw Glyph
1619 uint8_t mask = 0x80;
1620 disp_select();
1621 for (j=0; j < fontChar.height; j++) {
1622 for (i=0; i < fontChar.width; i++) {
1623 if (((i + (j*fontChar.width)) % 8) == 0) {
1624 mask = 0x80;
1625 ch = cfont.font[fontChar.dataPtr++];
1626 }
1627
1628 if ((ch & mask) !=0) {
1629 cx = (uint16_t)(x+fontChar.xOffset+i);
1630 cy = (uint16_t)(y+j+fontChar.adjYOffset);
1631 _drawPixel(cx, cy, _fg, 0);
1632 }
1633 mask >>= 1;
1634 }
1635 }
1636 disp_deselect();
1637
1638 return char_width;
1639 }
1640
1641
1642
1643 // non-rotated fixed width character
1644 //----------------------------------------------
1645 static void printChar(uint8_t c, int x, int y) {
1646 uint8_t i, j, ch, fz, mask;
1647 uint16_t k, temp, cx, cy, len;
1648
1649 // fz = bytes per char row
1650 fz = cfont.x_size/8;
1651 if (cfont.x_size % 8)
1652 fz++;
1653
1654 // get character position in buffer
1655 temp = ((c-cfont.offset)*((fz)*cfont.y_size))+4;
1656
1657 if ((font_buffered_char) && (!font_transparent)) {
1658 // === buffer Glyph data for faster sending ===
1659 len = cfont.x_size * cfont.y_size;
1660 color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
1661 if (color_line) {
1662 // fill with background color
1663 for (int n = 0; n < len; n++) {
1664 color_line[n] = _bg;
1665 }
1666 // set character pixels to foreground color
1667 for (j = 0; j < cfont.y_size; j++) {
1668 for (k = 0; k < fz; k++) {
1669 ch = cfont.font[temp + k];
1670 mask = 0x80;
1671 for (i = 0; i < 8; i++) {
1672 if ((ch & mask) !=0) {
1673 color_line[(j*cfont.x_size) + (i+(k*8))] = _fg;
1674 VncDrawPixel(x+i+(k*8), y+j, VNC_RGB2COL(_fg.r, _fg.g, _fg.b));
1675 } else {
1676 VncDrawPixel(x+i+(k*8), y+j, VNC_RGB2COL(_bg.r, _bg.g, _bg.b));
1677 }
1678 mask >>= 1;
1679 }
1680 }
1681 temp += (fz);
1682 }
1683 // send to display in one transaction
1684 disp_select();
1685 send_data(x, y, x+cfont.x_size-1, y+cfont.y_size-1, len, color_line);
1686 disp_deselect();
1687 free(color_line);
1688
1689 return;
1690 }
1691 }
1692
1693 if (!font_transparent)
1694 _fillRect(x, y, cfont.x_size, cfont.y_size, _bg);
1695
1696 disp_select();
1697 for (j = 0; j < cfont.y_size; j++) {
1698 for (k = 0; k < fz; k++) {
1699 ch = cfont.font[temp + k];
1700 mask = 0x80;
1701 for (i = 0; i < 8; i++) {
1702 if ((ch & mask) !=0) {
1703 cx = (uint16_t)(x+i+(k*8));
1704 cy = (uint16_t)(y+j);
1705 _drawPixel(cx, cy, _fg, 0);
1706 }
1707 mask >>= 1;
1708 }
1709 }
1710 temp += (fz);
1711 }
1712 disp_deselect();
1713 }
1714
1715
1716
1717 // print rotated proportional character
1718 // character is already in fontChar
1719 //---------------------------------------------------
1720 static int rotatePropChar(int x, int y, int offset) {
1721 uint8_t ch = 0;
1722 double radian = font_rotate * DEG_TO_RAD;
1723 float cos_radian = cos(radian);
1724 float sin_radian = sin(radian);
1725
1726 uint8_t mask = 0x80;
1727 disp_select();
1728 for (int j=0; j < fontChar.height; j++) {
1729 for (int i=0; i < fontChar.width; i++) {
1730 if (((i + (j*fontChar.width)) % 8) == 0) {
1731 mask = 0x80;
1732 ch = cfont.font[fontChar.dataPtr++];
1733 }
1734
1735 int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian)));
1736 int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian)));
1737
1738 if ((ch & mask) != 0) _drawPixel(newX,newY,_fg, 0);
1739 else if (!font_transparent) _drawPixel(newX,newY,_bg, 0);
1740
1741 mask >>= 1;
1742 }
1743 }
1744 disp_deselect();
1745
1746 return fontChar.xDelta+1;
1747 }
1748
1749 // rotated fixed width character
1750 //--------------------------------------------------------
1751 static void rotateChar(uint8_t c, int x, int y, int pos) {
1752 uint8_t i,j,ch,fz,mask;
1753 uint16_t temp;
1754 int newx,newy;
1755 double radian = font_rotate*0.0175;
1756 float cos_radian = cos(radian);
1757 float sin_radian = sin(radian);
1758 int zz;
1759
1760 if( cfont.x_size < 8 ) fz = cfont.x_size;
1761 else fz = cfont.x_size/8;
1762 temp=((c-cfont.offset)*((fz)*cfont.y_size))+4;
1763
1764 disp_select();
1765 for (j=0; j<cfont.y_size; j++) {
1766 for (zz=0; zz<(fz); zz++) {
1767 ch = cfont.font[temp+zz];
1768 mask = 0x80;
1769 for (i=0; i<8; i++) {
1770 newx=(int)(x+(((i+(zz*8)+(pos*cfont.x_size))*cos_radian)-((j)*sin_radian)));
1771 newy=(int)(y+(((j)*cos_radian)+((i+(zz*8)+(pos*cfont.x_size))*sin_radian)));
1772
1773 if ((ch & mask) != 0) _drawPixel(newx,newy,_fg, 0);
1774 else if (!font_transparent) _drawPixel(newx,newy,_bg, 0);
1775 mask >>= 1;
1776 }
1777 }
1778 temp+=(fz);
1779 }
1780 disp_deselect();
1781 // calculate x,y for the next char
1782 TFT_X = (int)(x + ((pos+1) * cfont.x_size * cos_radian));
1783 TFT_Y = (int)(y + ((pos+1) * cfont.x_size * sin_radian));
1784 }
1785
1786 //----------------------
1787 static int _7seg_width()
1788 {
1789 return (2 * (2 * cfont.y_size + 1)) + cfont.x_size;
1790 }
1791
1792 //-----------------------
1793 static int _7seg_height()
1794 {
1795 return (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size);
1796 }
1797
1798 // Returns the string width in pixels.
1799 // Useful for positions strings on the screen.
1800 //===============================
1801 int TFT_getStringWidth(char* str)
1802 {
1803 int strWidth = 0;
1804
1805 if (cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font
1806 else if (cfont.x_size != 0) strWidth = strlen(str) * cfont.x_size; // fixed width font
1807 else {
1808 // calculate the width of the string of proportional characters
1809 char* tempStrptr = str;
1810 while (*tempStrptr != 0) {
1811 if (getCharPtr(*tempStrptr++)) {
1812 strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1);
1813 }
1814 }
1815 strWidth--;
1816 }
1817 return strWidth;
1818 }
1819
1820 //===============================================
1821 void TFT_clearStringRect(int x, int y, char *str)
1822 {
1823 int w = TFT_getStringWidth(str);
1824 int h = TFT_getfontheight();
1825 TFT_fillRect(x+dispWin.x1, y+dispWin.y1, w, h, _bg);
1826 }
1827
1828 //==============================================================================
1829 /**
1830 * bit-encoded bar position of all digits' bcd segments
1831 *
1832 * 6
1833 * +-----+
1834 * 3 | . | 2
1835 * +--5--+
1836 * 1 | . | 0
1837 * +--.--+
1838 * 4
1839 */
1840 static const uint16_t font_bcd[] = {
1841 0x200, // 0010 0000 0000 // -
1842 0x080, // 0000 1000 0000 // .
1843 0x06C, // 0100 0110 1100 // /, degree
1844 0x05f, // 0000 0101 1111, // 0
1845 0x005, // 0000 0000 0101, // 1
1846 0x076, // 0000 0111 0110, // 2
1847 0x075, // 0000 0111 0101, // 3
1848 0x02d, // 0000 0010 1101, // 4
1849 0x079, // 0000 0111 1001, // 5
1850 0x07b, // 0000 0111 1011, // 6
1851 0x045, // 0000 0100 0101, // 7
1852 0x07f, // 0000 0111 1111, // 8
1853 0x07d, // 0000 0111 1101 // 9
1854 0x900 // 1001 0000 0000 // :
1855 };
1856
1857 //-----------------------------------------------------------------------------------------------
1858 static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
1859 _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color);
1860 _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color);
1861 _fillRect(x, y+2*w+1, 2*w+1, l, color);
1862 if (cfont.offset) {
1863 _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline);
1864 _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline);
1865 _drawRect(x, y+2*w+1, 2*w+1, l, outline);
1866 }
1867 }
1868
1869 //----------------------------------------------------------------------------------------------
1870 static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
1871 _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color);
1872 _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color);
1873 _fillRect(x+2*w+1, y, l, 2*w+1, color);
1874 if (cfont.offset) {
1875 _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline);
1876 _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline);
1877 _drawRect(x+2*w+1, y, l, 2*w+1, outline);
1878 }
1879 }
1880
1881 //--------------------------------------------------------------------------------------------
1882 static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) {
1883 /* TODO: clipping */
1884 if (num < 0x2D || num > 0x3A) return;
1885
1886 int16_t c = font_bcd[num-0x2D];
1887 int16_t d = 2*w+l+1;
1888
1889 // === Clear unused segments ===
1890 if (!(c & 0x001)) barVert(x+d, y+d, w, l, _bg, _bg);
1891 if (!(c & 0x002)) barVert(x, y+d, w, l, _bg, _bg);
1892 if (!(c & 0x004)) barVert(x+d, y, w, l, _bg, _bg);
1893 if (!(c & 0x008)) barVert(x, y, w, l, _bg, _bg);
1894 if (!(c & 0x010)) barHor(x, y+2*d, w, l, _bg, _bg);
1895 if (!(c & 0x020)) barHor(x, y+d, w, l, _bg, _bg);
1896 if (!(c & 0x040)) barHor(x, y, w, l, _bg, _bg);
1897
1898 if (!(c & 0x080)) {
1899 // low point
1900 _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
1901 if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, _bg);
1902 }
1903 if (!(c & 0x100)) {
1904 // down middle point
1905 _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
1906 if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, _bg);
1907 }
1908 if (!(c & 0x800)) {
1909 // up middle point
1910 _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
1911 if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, _bg);
1912 }
1913 if (!(c & 0x200)) {
1914 // middle, minus
1915 _fillRect(x+2*w+1, y+d, l, 2*w+1, _bg);
1916 if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, _bg);
1917 }
1918
1919 // === Draw used segments ===
1920 if (c & 0x001) barVert(x+d, y+d, w, l, color, cfont.color); // down right
1921 if (c & 0x002) barVert(x, y+d, w, l, color, cfont.color); // down left
1922 if (c & 0x004) barVert(x+d, y, w, l, color, cfont.color); // up right
1923 if (c & 0x008) barVert(x, y, w, l, color, cfont.color); // up left
1924 if (c & 0x010) barHor(x, y+2*d, w, l, color, cfont.color); // down
1925 if (c & 0x020) barHor(x, y+d, w, l, color, cfont.color); // middle
1926 if (c & 0x040) barHor(x, y, w, l, color, cfont.color); // up
1927
1928 if (c & 0x080) {
1929 // low point
1930 _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color);
1931 if (cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, cfont.color);
1932 }
1933 if (c & 0x100) {
1934 // down middle point
1935 _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color);
1936 if (cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, cfont.color);
1937 }
1938 if (c & 0x800) {
1939 // up middle point
1940 _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color);
1941 if (cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, cfont.color);
1942 }
1943 if (c & 0x200) {
1944 // middle, minus
1945 _fillRect(x+2*w+1, y+d, l, 2*w+1, color);
1946 if (cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, cfont.color);
1947 }
1948 }
1949 //==============================================================================
1950
1951 //======================================
1952 void TFT_print(char *st, int x, int y) {
1953 int stl, i, tmpw, tmph, fh;
1954 uint8_t ch;
1955
1956 if (cfont.bitmap == 0) return; // wrong font selected
1957
1958 // ** Rotated strings cannot be aligned
1959 if ((font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return;
1960
1961 if ((x < LASTX) || (font_rotate == 0)) TFT_OFFSET = 0;
1962
1963 if ((x >= LASTX) && (x < LASTY)) x = TFT_X + (x-LASTX);
1964 else if (x > CENTER) x += dispWin.x1;
1965
1966 if (y >= LASTY) y = TFT_Y + (y-LASTY);
1967 else if (y > CENTER) y += dispWin.y1;
1968
1969 // ** Get number of characters in string to print
1970 stl = strlen(st);
1971
1972 // ** Calculate CENTER, RIGHT or BOTTOM position
1973 tmpw = TFT_getStringWidth(st); // string width in pixels
1974 fh = cfont.y_size; // font height
1975 if ((cfont.x_size != 0) && (cfont.bitmap == 2)) {
1976 // 7-segment font
1977 fh = (3 * (2 * cfont.y_size + 1)) + (2 * cfont.x_size); // 7-seg character height
1978 }
1979
1980 if (x == RIGHT) x = dispWin.x2 - tmpw + dispWin.x1;
1981 else if (x == CENTER) x = (((dispWin.x2 - dispWin.x1 + 1) - tmpw) / 2) + dispWin.x1;
1982
1983 if (y == BOTTOM) y = dispWin.y2 - fh + dispWin.y1;
1984 else if (y==CENTER) y = (((dispWin.y2 - dispWin.y1 + 1) - (fh/2)) / 2) + dispWin.y1;
1985
1986 if (x < dispWin.x1) x = dispWin.x1;
1987 if (y < dispWin.y1) y = dispWin.y1;
1988 if ((x > dispWin.x2) || (y > dispWin.y2)) return;
1989
1990 TFT_X = x;
1991 TFT_Y = y;
1992
1993 // ** Adjust y position
1994 tmph = cfont.y_size; // font height
1995 // for non-proportional fonts, char width is the same for all chars
1996 tmpw = cfont.x_size;
1997 if (cfont.x_size != 0) {
1998 if (cfont.bitmap == 2) { // 7-segment font
1999 tmpw = _7seg_width(); // character width
2000 tmph = _7seg_height(); // character height
2001 }
2002 }
2003 else TFT_OFFSET = 0; // fixed font; offset not needed
2004
2005 if ((TFT_Y + tmph - 1) > dispWin.y2) return;
2006
2007 int offset = TFT_OFFSET;
2008
2009 for (i=0; i<stl; i++) {
2010 ch = st[i]; // get string character
2011
2012 if (ch == 0x0D) { // === '\r', erase to eol ====
2013 if ((!font_transparent) && (font_rotate==0)) _fillRect(TFT_X, TFT_Y, dispWin.x2+1-TFT_X, tmph, _bg);
2014 }
2015
2016 else if (ch == 0x0A) { // ==== '\n', new line ====
2017 if (cfont.bitmap == 1) {
2018 TFT_Y += tmph + font_line_space;
2019 if (TFT_Y > (dispWin.y2-tmph)) break;
2020 TFT_X = dispWin.x1;
2021 }
2022 }
2023
2024 else { // ==== other characters ====
2025 if (cfont.x_size == 0) {
2026 // for proportional font get character data to 'fontChar'
2027 if (getCharPtr(ch)) tmpw = fontChar.xDelta;
2028 else continue;
2029 }
2030
2031 // check if character can be displayed in the current line
2032 if ((TFT_X+tmpw) > (dispWin.x2)) {
2033 if (text_wrap == 0) break;
2034 TFT_Y += tmph + font_line_space;
2035 if (TFT_Y > (dispWin.y2-tmph)) break;
2036 TFT_X = dispWin.x1;
2037 }
2038
2039 // Let's print the character
2040 if (cfont.x_size == 0) {
2041 // == proportional font
2042 if (font_rotate == 0) TFT_X += printProportionalChar(TFT_X, TFT_Y) + 1;
2043 else {
2044 // rotated proportional font
2045 offset += rotatePropChar(x, y, offset);
2046 TFT_OFFSET = offset;
2047 }
2048 }
2049 else {
2050 if (cfont.bitmap == 1) {
2051 // == fixed font
2052 if ((ch < cfont.offset) || ((ch-cfont.offset) > cfont.numchars)) ch = cfont.offset;
2053 if (font_rotate == 0) {
2054 printChar(ch, TFT_X, TFT_Y);
2055 TFT_X += tmpw;
2056 }
2057 else rotateChar(ch, x, y, i);
2058 }
2059 else if (cfont.bitmap == 2) {
2060 // == 7-segment font ==
2061 _draw7seg(TFT_X, TFT_Y, ch, cfont.y_size, cfont.x_size, _fg);
2062 TFT_X += (tmpw + 2);
2063 }
2064 }
2065 }
2066 }
2067 }
2068
2069
2070 // ================ Service functions ==========================================
2071
2072 // Change the screen rotation.
2073 // Input: m new rotation value (0 to 3)
2074 //=================================
2075 void TFT_setRotation(uint8_t rot) {
2076 if (rot > 3) {
2077 uint8_t madctl = (rot & 0xF8); // for testing, manually set MADCTL register
2078 if (disp_select() == ESP_OK) {
2079 disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
2080 disp_deselect();
2081 }
2082 }
2083 else {
2084 orientation = rot;
2085 _tft_setRotation(rot);
2086 }
2087
2088 dispWin.x1 = 0;
2089 dispWin.y1 = 0;
2090 dispWin.x2 = _width-1;
2091 dispWin.y2 = _height-1;
2092
2093 TFT_fillScreen(_bg);
2094 }
2095
2096 // Send the command to invert all of the colors.
2097 // Input: i 0 to disable inversion; non-zero to enable inversion
2098 //==========================================
2099 void TFT_invertDisplay(const uint8_t mode) {
2100 if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN);
2101 else disp_spi_transfer_cmd(TFT_INVOFF);
2102 }
2103
2104 // Select gamma curve
2105 // Input: gamma = 0~3
2106 //==================================
2107 void TFT_setGammaCurve(uint8_t gm) {
2108 uint8_t gamma_curve = 1 << (gm & 0x03);
2109 disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1);
2110 }
2111
2112 //===========================================================
2113 color_t HSBtoRGB(float _hue, float _sat, float _brightness) {
2114 float red = 0.0;
2115 float green = 0.0;
2116 float blue = 0.0;
2117
2118 if (_sat == 0.0) {
2119 red = _brightness;
2120 green = _brightness;
2121 blue = _brightness;
2122 } else {
2123 if (_hue == 360.0) {
2124 _hue = 0;
2125 }
2126
2127 int slice = (int)(_hue / 60.0);
2128 float hue_frac = (_hue / 60.0) - slice;
2129
2130 float aa = _brightness * (1.0 - _sat);
2131 float bb = _brightness * (1.0 - _sat * hue_frac);
2132 float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac));
2133
2134 switch(slice) {
2135 case 0:
2136 red = _brightness;
2137 green = cc;
2138 blue = aa;
2139 break;
2140 case 1:
2141 red = bb;
2142 green = _brightness;
2143 blue = aa;
2144 break;
2145 case 2:
2146 red = aa;
2147 green = _brightness;
2148 blue = cc;
2149 break;
2150 case 3:
2151 red = aa;
2152 green = bb;
2153 blue = _brightness;
2154 break;
2155 case 4:
2156 red = cc;
2157 green = aa;
2158 blue = _brightness;
2159 break;
2160 case 5:
2161 red = _brightness;
2162 green = aa;
2163 blue = bb;
2164 break;
2165 default:
2166 red = 0.0;
2167 green = 0.0;
2168 blue = 0.0;
2169 break;
2170 }
2171 }
2172
2173 color_t color;
2174 color.r = ((uint8_t)(red * 255.0)) & 0xFC;
2175 color.g = ((uint8_t)(green * 255.0)) & 0xFC;
2176 color.b = ((uint8_t)(blue * 255.0)) & 0xFC;
2177
2178 return color;
2179 }
2180 //=====================================================================
2181 void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
2182 {
2183 dispWin.x1 = x1;
2184 dispWin.y1 = y1;
2185 dispWin.x2 = x2;
2186 dispWin.y2 = y2;
2187
2188 if (dispWin.x2 >= _width) dispWin.x2 = _width-1;
2189 if (dispWin.y2 >= _height) dispWin.y2 = _height-1;
2190 if (dispWin.x1 > dispWin.x2) dispWin.x1 = dispWin.x2;
2191 if (dispWin.y1 > dispWin.y2) dispWin.y1 = dispWin.y2;
2192 }
2193
2194 //=====================
2195 void TFT_resetclipwin()
2196 {
2197 dispWin.x2 = _width-1;
2198 dispWin.y2 = _height-1;
2199 dispWin.x1 = 0;
2200 dispWin.y1 = 0;
2201 }
2202
2203 //==========================================================================
2204 void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) {
2205 if (cfont.bitmap != 2) return;
2206
2207 if (l < 6) l = 6;
2208 if (l > 40) l = 40;
2209 if (w < 1) w = 1;
2210 if (w > (l/2)) w = l/2;
2211 if (w > 12) w = 12;
2212
2213 cfont.x_size = l;
2214 cfont.y_size = w;
2215 cfont.offset = outline;
2216 cfont.color = color;
2217 }
2218
2219 //==========================================
2220 int TFT_getfontsize(int *width, int* height)
2221 {
2222 if (cfont.bitmap == 1) {
2223 if (cfont.x_size != 0) *width = cfont.x_size; // fixed width font
2224 else *width = cfont.max_x_size; // proportional font
2225 *height = cfont.y_size;
2226 }
2227 else if (cfont.bitmap == 2) {
2228 // 7-segment font
2229 *width = _7seg_width();
2230 *height = _7seg_height();
2231 }
2232 else {
2233 *width = 0;
2234 *height = 0;
2235 return 0;
2236 }
2237 return 1;
2238 }
2239
2240 //=====================
2241 int TFT_getfontheight()
2242 {
2243 if (cfont.bitmap == 1) return cfont.y_size; // Bitmap font
2244 else if (cfont.bitmap == 2) return _7seg_height(); // 7-segment font
2245 return 0;
2246 }
2247
2248 //====================
2249 void TFT_saveClipWin()
2250 {
2251 dispWinTemp.x1 = dispWin.x1;
2252 dispWinTemp.y1 = dispWin.y1;
2253 dispWinTemp.x2 = dispWin.x2;
2254 dispWinTemp.y2 = dispWin.y2;
2255 }
2256
2257 //=======================
2258 void TFT_restoreClipWin()
2259 {
2260 dispWin.x1 = dispWinTemp.x1;
2261 dispWin.y1 = dispWinTemp.y1;
2262 dispWin.x2 = dispWinTemp.x2;
2263 dispWin.y2 = dispWinTemp.y2;
2264 }
2265
2266
2267 // ================ JPG SUPPORT ================================================
2268 // User defined device identifier
2269 typedef struct {
2270 FILE *fhndl; // File handler for input function
2271 int x; // image top left point X position
2272 int y; // image top left point Y position
2273 uint8_t *membuff; // memory buffer containing the image
2274 uint32_t bufsize; // size of the memory buffer
2275 uint32_t bufptr; // memory buffer current position
2276 color_t *linbuf[2]; // memory buffer used for display output
2277 uint8_t linbuf_idx;
2278 } JPGIODEV;
2279
2280
2281 // User defined call-back function to input JPEG data from file
2282 //---------------------
2283 static UINT tjd_input (
2284 JDEC* jd, // Decompression object
2285 BYTE* buff, // Pointer to the read buffer (NULL:skip)
2286 UINT nd // Number of bytes to read/skip from input stream
2287 )
2288 {
2289 int rb = 0;
2290 // Device identifier for the session (5th argument of jd_prepare function)
2291 JPGIODEV *dev = (JPGIODEV*)jd->device;
2292
2293 if (buff) { // Read nd bytes from the input strem
2294 rb = fread(buff, 1, nd, dev->fhndl);
2295 return rb; // Returns actual number of bytes read
2296 }
2297 else { // Remove nd bytes from the input stream
2298 if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd;
2299 else return 0;
2300 }
2301 }
2302
2303 // User defined call-back function to input JPEG data from memory buffer
2304 //-------------------------
2305 static UINT tjd_buf_input (
2306 JDEC* jd, // Decompression object
2307 BYTE* buff, // Pointer to the read buffer (NULL:skip)
2308 UINT nd // Number of bytes to read/skip from input stream
2309 )
2310 {
2311 // Device identifier for the session (5th argument of jd_prepare function)
2312 JPGIODEV *dev = (JPGIODEV*)jd->device;
2313 if (!dev->membuff) return 0;
2314 if (dev->bufptr >= (dev->bufsize + 2)) return 0; // end of stream
2315
2316 if ((dev->bufptr + nd) > (dev->bufsize + 2)) nd = (dev->bufsize + 2) - dev->bufptr;
2317
2318 if (buff) { // Read nd bytes from the input strem
2319 memcpy(buff, dev->membuff + dev->bufptr, nd);
2320 dev->bufptr += nd;
2321 return nd; // Returns number of bytes read
2322 }
2323 else { // Remove nd bytes from the input stream
2324 dev->bufptr += nd;
2325 return nd;
2326 }
2327 }
2328
2329 // User defined call-back function to output RGB bitmap to display device
2330 //----------------------
2331 static UINT tjd_output (
2332 JDEC* jd, // Decompression object of current session
2333 void* bitmap, // Bitmap data to be output
2334 JRECT* rect // Rectangular region to output
2335 )
2336 {
2337 // Device identifier for the session (5th argument of jd_prepare function)
2338 JPGIODEV *dev = (JPGIODEV*)jd->device;
2339
2340 // ** Put the rectangular into the display device **
2341 int x;
2342 int y;
2343 int dleft, dtop, dright, dbottom;
2344 BYTE *src = (BYTE*)bitmap;
2345
2346 int left = rect->left + dev->x;
2347 int top = rect->top + dev->y;
2348 int right = rect->right + dev->x;
2349 int bottom = rect->bottom + dev->y;
2350
2351 if ((left > dispWin.x2) || (top > dispWin.y2)) return 1; // out of screen area, return
2352 if ((right < dispWin.x1) || (bottom < dispWin.y1)) return 1;// out of screen area, return
2353
2354 if (left < dispWin.x1) dleft = dispWin.x1;
2355 else dleft = left;
2356 if (top < dispWin.y1) dtop = dispWin.y1;
2357 else dtop = top;
2358 if (right > dispWin.x2) dright = dispWin.x2;
2359 else dright = right;
2360 if (bottom > dispWin.y2) dbottom = dispWin.y2;
2361 else dbottom = bottom;
2362
2363 if ((dleft > dispWin.x2) || (dtop > dispWin.y2)) return 1; // out of screen area, return
2364 if ((dright < dispWin.x1) || (dbottom < dispWin.y1)) return 1; // out of screen area, return
2365
2366 uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data
2367
2368
2369 if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) {
2370 uint8_t *dest = (uint8_t *)(dev->linbuf[dev->linbuf_idx]);
2371
2372 for (y = top; y <= bottom; y++) {
2373 for (x = left; x <= right; x++) {
2374 // Clip to display area
2375 if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) {
2376 *dest++ = (*src++) & 0xFC;
2377 *dest++ = (*src++) & 0xFC;
2378 *dest++ = (*src++) & 0xFC;
2379 }
2380 else src += 3; // skip
2381 }
2382 }
2383 wait_trans_finish(1);
2384 send_data(dleft, dtop, dright, dbottom, len, dev->linbuf[dev->linbuf_idx]);
2385 dev->linbuf_idx = ((dev->linbuf_idx + 1) & 1);
2386 }
2387 else {
2388 wait_trans_finish(1);
2389 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);
2390 return 0; // stop decompression
2391 }
2392
2393 return 1; // Continue to decompression
2394 }
2395
2396 // tft.jpgimage(X, Y, scale, file_name, buf, size]
2397 // X & Y can be < 0 !
2398 //==================================================================================
2399 void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size)
2400 {
2401 JPGIODEV dev;
2402 struct stat sb;
2403 char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
2404 UINT sz_work = 3800; // Size of the working buffer (must be power of 2)
2405 JDEC jd; // Decompression object (70 bytes)
2406 JRESULT rc;
2407
2408 dev.linbuf[0] = NULL;
2409 dev.linbuf[1] = NULL;
2410 dev.linbuf_idx = 0;
2411
2412 dev.fhndl = NULL;
2413 if (fname == NULL) {
2414 // image from buffer
2415 dev.membuff = buf;
2416 dev.bufsize = size;
2417 dev.bufptr = 0;
2418 }
2419 else {
2420 // image from file
2421 dev.membuff = NULL;
2422 dev.bufsize = 0;
2423 dev.bufptr = 0;
2424
2425 if (stat(fname, &sb) != 0) {
2426 if (image_debug) printf("File error: %ss\r\n", strerror(errno));
2427 goto exit;
2428 }
2429
2430 dev.fhndl = fopen(fname, "r");
2431 if (!dev.fhndl) {
2432 if (image_debug) printf("Error opening file: %s\r\n", strerror(errno));
2433 goto exit;
2434 }
2435 }
2436
2437 if (scale > 3) scale = 3;
2438
2439 work = malloc(sz_work);
2440 if (work) {
2441 if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev);
2442 else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev);
2443 if (rc == JDR_OK) {
2444 if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + dispWin.x1;
2445 else if (x == RIGHT) x = dispWin.x2 + 1 - (int)(jd.width >> scale);
2446
2447 if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + dispWin.y1;
2448 else if (y == BOTTOM) y = dispWin.y2 + 1 - (int)(jd.height >> scale);
2449
2450 if (x < ((dispWin.x2-1) * -1)) x = (dispWin.x2-1) * -1;
2451 if (y < ((dispWin.y2-1)) * -1) y = (dispWin.y2-1) * -1;
2452 if (x > (dispWin.x2-1)) x = dispWin.x2 - 1;
2453 if (y > (dispWin.y2-1)) y = dispWin.y2-1;
2454
2455 dev.x = x;
2456 dev.y = y;
2457
2458 dev.linbuf[0] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
2459 if (dev.linbuf[0] == NULL) {
2460 if (image_debug) printf("Error allocating line buffer #0\r\n");
2461 goto exit;
2462 }
2463 dev.linbuf[1] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
2464 if (dev.linbuf[1] == NULL) {
2465 if (image_debug) printf("Error allocating line buffer #1\r\n");
2466 goto exit;
2467 }
2468
2469 // Start to decode the JPEG file
2470 disp_select();
2471 rc = jd_decomp(&jd, tjd_output, scale);
2472 disp_deselect();
2473
2474 if (rc != JDR_OK) {
2475 if (image_debug) printf("jpg decompression error %d\r\n", rc);
2476 }
2477 if (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);
2478 }
2479 else {
2480 if (image_debug) printf("jpg prepare error %d\r\n", rc);
2481 }
2482 }
2483 else {
2484 if (image_debug) printf("work buffer allocation error\r\n");
2485 }
2486
2487 exit:
2488 if (work) free(work); // free work buffer
2489 if (dev.linbuf[0]) free(dev.linbuf[0]);
2490 if (dev.linbuf[1]) free(dev.linbuf[1]);
2491 if (dev.fhndl) fclose(dev.fhndl); // close input file
2492 }
2493
2494
2495 //====================================================================================
2496 int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size)
2497 {
2498 FILE *fhndl = NULL;
2499 struct stat sb;
2500 int i, err=0;
2501 int img_xsize, img_ysize, img_xstart, img_xlen, img_ystart, img_ylen;
2502 int img_pos, img_pix_pos, scan_lines, rd_len;
2503 uint8_t tmpc;
2504 uint16_t wtemp;
2505 uint32_t temp;
2506 int disp_xstart, disp_xend, disp_ystart, disp_yend;
2507 uint8_t buf[56];
2508 char err_buf[64];
2509 uint8_t *line_buf[2] = {NULL,NULL};
2510 uint8_t lb_idx = 0;
2511 uint8_t *scale_buf = NULL;
2512 uint8_t scale_pix;
2513 uint16_t co[3] = {0,0,0}; // RGB sum
2514 uint8_t npix;
2515
2516 if (scale > 7) scale = 7;
2517 scale_pix = scale+1; // scale factor ( 1~8 )
2518
2519 if (fname) {
2520 // * File name is given, reading image from file
2521 if (stat(fname, &sb) != 0) {
2522 sprintf(err_buf, "opening file");
2523 err = -1;
2524 goto exit;
2525 }
2526 size = sb.st_size;
2527 fhndl = fopen(fname, "r");
2528 if (!fhndl) {
2529 sprintf(err_buf, "opening file");
2530 err = -2;
2531 goto exit;
2532 }
2533
2534 i = fread(buf, 1, 54, fhndl); // read header
2535 }
2536 else {
2537 // * Reading image from buffer
2538 if ((imgbuf) && (size > 54)) {
2539 memcpy(buf, imgbuf, 54);
2540 i = 54;
2541 }
2542 else i = 0;
2543 }
2544
2545 sprintf(err_buf, "reading header");
2546 if (i != 54) {err = -3; goto exit;}
2547
2548 // ** Check image header and get image properties
2549 if ((buf[0] != 'B') || (buf[1] != 'M')) {err=-4; goto exit;} // accept only images with 'BM' id
2550
2551 memcpy(&temp, buf+2, 4); // file size
2552 if (temp != size) {err=-5; goto exit;}
2553
2554 memcpy(&img_pos, buf+10, 4); // start of pixel data
2555
2556 memcpy(&temp, buf+14, 4); // BMP header size
2557 if (temp != 40) {err=-6; goto exit;}
2558
2559 memcpy(&wtemp, buf+26, 2); // the number of color planes
2560 if (wtemp != 1) {err=-7; goto exit;}
2561
2562 memcpy(&wtemp, buf+28, 2); // the number of bits per pixel
2563 if (wtemp != 24) {err=-8; goto exit;}
2564
2565 memcpy(&temp, buf+30, 4); // the compression method being used
2566 if (temp != 0) {err=-9; goto exit;}
2567
2568 memcpy(&img_xsize, buf+18, 4); // the bitmap width in pixels
2569 memcpy(&img_ysize, buf+22, 4); // the bitmap height in pixels
2570
2571
2572 // * scale image dimensions
2573
2574 img_xlen = img_xsize / scale_pix; // image display horizontal size
2575 img_ylen = img_ysize / scale_pix; // image display vertical size
2576
2577 if (x == CENTER) x = ((dispWin.x2 - dispWin.x1 + 1 - img_xlen) / 2) + dispWin.x1;
2578 else if (x == RIGHT) x = dispWin.x2 + 1 - img_xlen;
2579
2580 if (y == CENTER) y = ((dispWin.y2 - dispWin.y1 + 1 - img_ylen) / 2) + dispWin.y1;
2581 else if (y == BOTTOM) y = dispWin.y2 + 1 - img_ylen;
2582
2583 if ((x < ((dispWin.x2 + 1) * -1)) || (x > (dispWin.x2 + 1)) || (y < ((dispWin.y2 + 1) * -1)) || (y > (dispWin.y2 + 1))) {
2584 sprintf(err_buf, "out of display area (%d,%d", x, y);
2585 err = -10;
2586 goto exit;
2587 }
2588
2589 // ** set display and image areas
2590 if (x < dispWin.x1) {
2591 disp_xstart = dispWin.x1;
2592 img_xstart = -x; // image pixel line X offset
2593 img_xlen += x;
2594 }
2595 else {
2596 disp_xstart = x;
2597 img_xstart = 0;
2598 }
2599 if (y < dispWin.y1) {
2600 disp_ystart = dispWin.y1;
2601 img_ystart = -y; // image pixel line Y offset
2602 img_ylen += y;
2603 }
2604 else {
2605 disp_ystart = y;
2606 img_ystart = 0;
2607 }
2608 disp_xend = disp_xstart + img_xlen - 1;
2609 disp_yend = disp_ystart + img_ylen - 1;
2610 if (disp_xend > dispWin.x2) {
2611 disp_xend = dispWin.x2;
2612 img_xlen = disp_xend - disp_xstart + 1;
2613 }
2614 if (disp_yend > dispWin.y2) {
2615 disp_yend = dispWin.y2;
2616 img_ylen = disp_yend - disp_ystart + 1;
2617 }
2618
2619 if ((img_xlen < 8) || (img_ylen < 8) || (img_xstart >= (img_xsize-2)) || ((img_ysize - img_ystart) < 2)) {
2620 sprintf(err_buf, "image too small");
2621 err = -11;
2622 goto exit;
2623 }
2624
2625 // ** Allocate memory for 2 lines of image pixels
2626 line_buf[0] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
2627 if (line_buf[0] == NULL) {
2628 sprintf(err_buf, "allocating line buffer #1");
2629 err=-12;
2630 goto exit;
2631 }
2632
2633 line_buf[1] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
2634 if (line_buf[1] == NULL) {
2635 sprintf(err_buf, "allocating line buffer #2");
2636 err=-13;
2637 goto exit;
2638 }
2639
2640 if (scale) {
2641 // Allocate memory for scale buffer
2642 rd_len = img_xlen * 3 * scale_pix;
2643 scale_buf = malloc(rd_len*scale_pix);
2644 if (scale_buf == NULL) {
2645 sprintf(err_buf, "allocating scale buffer");
2646 err=-14;
2647 goto exit;
2648 }
2649 }
2650 else rd_len = img_xlen * 3;
2651
2652 // ** ***************************************************** **
2653 // ** BMP images are stored in file from LAST to FIRST line **
2654 // ** ***************************************************** **
2655
2656 /* Used variables:
2657 img_xsize horizontal image size in pixels
2658 img_ysize number of image lines
2659 img_xlen image display horizontal scaled size in pixels
2660 img_ylen image display vertical scaled size in pixels
2661 img_xstart first pixel in line to be displayed
2662 img_ystart first image line to be displayed
2663 img_xlen number of pixels in image line to be displayed, starting with 'img_xstart'
2664 img_ylen number of lines in image to be displayed, starting with 'img_ystart'
2665 rd_len length of color data which are read from image line in bytes
2666 */
2667
2668 // Set position in image to the first color data (beginning of the LAST line)
2669 img_pos += (img_ystart * (img_xsize*3));
2670 if (fhndl) {
2671 if (fseek(fhndl, img_pos, SEEK_SET) != 0) {
2672 sprintf(err_buf, "file seek at %d", img_pos);
2673 err = -15;
2674 goto exit;
2675 }
2676 }
2677
2678 if (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",
2679 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));
2680
2681 // * Select the display
2682 disp_select();
2683
2684 while ((disp_yend >= disp_ystart) && ((img_pos + (img_xsize*3)) <= size)) {
2685 if (img_pos > size) {
2686 sprintf(err_buf, "EOF reached: %d > %d", img_pos, size);
2687 err = -16;
2688 goto exit1;
2689 }
2690 if (scale == 0) {
2691 // Read the line of color data into color buffer
2692 if (fhndl) {
2693 i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
2694 if (i != (img_xsize*3)) {
2695 sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
2696 err = -16;
2697 goto exit1;
2698 }
2699 }
2700 else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
2701
2702 if (img_xstart > 0) memmove(line_buf[lb_idx], line_buf[lb_idx]+(img_xstart*3), rd_len);
2703 // Convert colors BGR-888 (BMP) -> RGB-888 (DISPLAY) ===
2704 for (i=0; i < rd_len; i += 3) {
2705 tmpc = line_buf[lb_idx][i+2] & 0xfc; // save R
2706 line_buf[lb_idx][i+2] = line_buf[lb_idx][i] & 0xfc; // B -> R
2707 line_buf[lb_idx][i] = tmpc; // R -> B
2708 line_buf[lb_idx][i+1] &= 0xfc; // G
2709 }
2710 img_pos += (img_xsize*3);
2711 }
2712 else {
2713 // scale image, read 'scale_pix' lines and find the average color
2714 for (scan_lines=0; scan_lines<scale_pix; scan_lines++) {
2715 if (img_pos > size) break;
2716 if (fhndl) {
2717 i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
2718 if (i != (img_xsize*3)) {
2719 sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
2720 err = -17;
2721 goto exit1;
2722 }
2723 }
2724 else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
2725 img_pos += (img_xsize*3);
2726
2727 // copy only data which are displayed to scale buffer
2728 memcpy(scale_buf + (rd_len * scan_lines), line_buf[lb_idx]+img_xstart, rd_len);
2729 }
2730
2731 // Populate display line buffer
2732 for (int n=0;n<(img_xlen*3);n += 3) {
2733 memset(co, 0, sizeof(co)); // initialize color sum
2734 npix = 0; // initialize number of pixels in scale rectangle
2735
2736 // sum all pixels in scale rectangle
2737 for (int sc_line=0; sc_line<scan_lines; sc_line++) {
2738 // Get colors position in scale buffer
2739 img_pix_pos = (rd_len * sc_line) + (n * scale_pix);
2740
2741 for (int sc_col=0; sc_col<scale_pix; sc_col++) {
2742 co[0] += scale_buf[img_pix_pos];
2743 co[1] += scale_buf[img_pix_pos + 1];
2744 co[2] += scale_buf[img_pix_pos + 2];
2745 npix++;
2746 }
2747 }
2748 // Place the average in display buffer, convert BGR-888 (BMP) -> RGB-888 (DISPLAY)
2749 line_buf[lb_idx][n+2] = (uint8_t)(co[0] / npix); // B
2750 line_buf[lb_idx][n+1] = (uint8_t)(co[1] / npix); // G
2751 line_buf[lb_idx][n] = (uint8_t)(co[2] / npix); // R
2752 }
2753 }
2754
2755 wait_trans_finish(1);
2756 send_data(disp_xstart, disp_yend, disp_xend, disp_yend, img_xlen, (color_t *)line_buf[lb_idx]);
2757 lb_idx = (lb_idx + 1) & 1; // change buffer
2758
2759 disp_yend--;
2760 }
2761 err = 0;
2762 exit1:
2763 disp_deselect();
2764 exit:
2765 if (scale_buf) free(scale_buf);
2766 if (line_buf[0]) free(line_buf[0]);
2767 if (line_buf[1]) free(line_buf[1]);
2768 if (fhndl) fclose(fhndl);
2769 if ((err) && (image_debug)) printf("Error: %d [%s]\r\n", err, err_buf);
2770
2771 return err;
2772 }
2773
2774
2775 // ============= Touch panel functions =========================================
2776
2777 #if USE_TOUCH == TOUCH_TYPE_XPT2046
2778 //-------------------------------------------------------
2779 static int tp_get_data_xpt2046(uint8_t type, int samples)
2780 {
2781 if (ts_spi == NULL) return 0;
2782
2783 int n, result, val = 0;
2784 uint32_t i = 0;
2785 uint32_t vbuf[18];
2786 uint32_t minval, maxval, dif;
2787
2788 if (samples < 3) samples = 1;
2789 if (samples > 18) samples = 18;
2790
2791 // one dummy read
2792 result = touch_get_data(type);
2793
2794 // read data
2795 while (i < 10) {
2796 minval = 5000;
2797 maxval = 0;
2798 // get values
2799 for (n=0;n<samples;n++) {
2800 result = touch_get_data(type);
2801 if (result < 0) break;
2802
2803 vbuf[n] = result;
2804 if (result < minval) minval = result;
2805 if (result > maxval) maxval = result;
2806 }
2807 if (result < 0) break;
2808 dif = maxval - minval;
2809 if (dif < 40) break;
2810 i++;
2811 }
2812 if (result < 0) return -1;
2813
2814 if (samples > 2) {
2815 // remove one min value
2816 for (n = 0; n < samples; n++) {
2817 if (vbuf[n] == minval) {
2818 vbuf[n] = 5000;
2819 break;
2820 }
2821 }
2822 // remove one max value
2823 for (n = 0; n < samples; n++) {
2824 if (vbuf[n] == maxval) {
2825 vbuf[n] = 5000;
2826 break;
2827 }
2828 }
2829 for (n = 0; n < samples; n++) {
2830 if (vbuf[n] < 5000) val += vbuf[n];
2831 }
2832 val /= (samples-2);
2833 }
2834 else val = vbuf[0];
2835
2836 return val;
2837 }
2838
2839 //-----------------------------------------------
2840 static int TFT_read_touch_xpt2046(int *x, int* y)
2841 {
2842 int res = 0, result = -1;
2843
2844 if (spi_lobo_device_select(ts_spi, 0) != ESP_OK)
2845 return 0;
2846
2847 result = tp_get_data_xpt2046(0xB0, 3); // Z; pressure; touch detect
2848 if (result <= 15) goto exit; // Z was 50, but near the origin it's just above 10.
2849 // 26-6-2018 from 10 to 15.
2850
2851 // touch panel pressed
2852 result = tp_get_data_xpt2046(0xD0, 6);
2853 if (result < 0) goto exit;
2854 *x = result;
2855
2856 result = tp_get_data_xpt2046(0x90, 6);
2857 if (result < 0) goto exit;
2858 *y = result;
2859 res = 1;
2860
2861 exit:
2862 spi_lobo_device_deselect(ts_spi);
2863 return res;
2864 }
2865 #endif
2866
2867 //=============================================
2868 int TFT_read_touch(int *x, int* y, uint8_t raw)
2869 {
2870 *x = 0;
2871 *y = 0;
2872
2873 if (ts_spi == NULL)
2874 return 0;
2875 #if USE_TOUCH == TOUCH_TYPE_NONE
2876 return 0;
2877 #else
2878 int result = -1;
2879 int X=0, Y=0;
2880
2881 #if USE_TOUCH == TOUCH_TYPE_XPT2046
2882 result = TFT_read_touch_xpt2046(&X, &Y);
2883 if (result == 0)
2884 return 0;
2885 #elif USE_TOUCH == TOUCH_TYPE_STMPE610
2886 uint32_t tp_calx = TP_CALX_STMPE610;
2887 uint32_t tp_caly = TP_CALY_STMPE610;
2888 uint16_t Xx, Yy, Z=0;
2889 result = stmpe610_get_touch(&Xx, &Yy, &Z);
2890 if (result == 0) return 0;
2891 X = Xx;
2892 Y = Yy;
2893 #else
2894 return 0;
2895 #endif
2896
2897 if (raw) {
2898 *x = X;
2899 *y = Y;
2900 return 1;
2901 }
2902
2903 // Calibrate the result
2904 int tmp;
2905 int xleft = tp_xleft; //(tp_calx >> 16) & 0x3FFF;
2906 int xright = tp_xright; //tp_calx & 0x3FFF;
2907 int ytop = tp_ytop; //(tp_caly >> 16) & 0x3FFF;
2908 int ybottom = tp_ybottom; //tp_caly & 0x3FFF;
2909
2910 if (((xright - xleft) <= 0) || ((ytop - ybottom) <= 0))
2911 return 0;
2912
2913 #if USE_TOUCH == TOUCH_TYPE_XPT2046
2914 // printf("Raw %dx%d ", X, Y);
2915 // Received coordinates are always in portrait, origin left bottom.
2916 int width = DEFAULT_TFT_DISPLAY_WIDTH;
2917 int height = DEFAULT_TFT_DISPLAY_HEIGHT;
2918 X = ((X - xleft) * width) / (xright - xleft);
2919 Y = ((Y - ybottom) * height) / (ytop - ybottom);
2920
2921 if (X < 0)
2922 X = 0;
2923 if (X > width-1)
2924 X = width-1;
2925 if (Y < 0)
2926 Y = 0;
2927 if (Y > height-1)
2928 Y = height-1;
2929
2930 switch (orientation) {
2931 case PORTRAIT:
2932 Y = height - Y - 1;
2933 break;
2934 case LANDSCAPE:
2935 tmp = X;
2936 X = height - Y - 1;
2937 Y = width - tmp - 1;
2938 break;
2939 case PORTRAIT_FLIP:
2940 X = width - X - 1;
2941 break;
2942 case LANDSCAPE_FLIP:
2943 tmp = X;
2944 X = Y;
2945 Y = tmp;
2946 break;
2947 }
2948 // printf("Cal %dx%d %dx%d XxY %dx%d\n", xleft, ybottom, xright, ytop, X, Y);
2949 #elif USE_TOUCH == TOUCH_TYPE_STMPE610
2950 int width = _width;
2951 int height = _height;
2952 if (_width > _height) {
2953 width = _height;
2954 height = _width;
2955 }
2956 X = ((X - xleft) * width) / (xright - xleft);
2957 Y = ((Y - ytop) * height) / (ybottom - ytop);
2958
2959 if (X < 0) X = 0;
2960 if (X > width-1) X = width-1;
2961 if (Y < 0) Y = 0;
2962 if (Y > height-1) Y = height-1;
2963
2964 switch (orientation) {
2965 case PORTRAIT_FLIP:
2966 X = width - X - 1;
2967 Y = height - Y - 1;
2968 break;
2969 case LANDSCAPE:
2970 tmp = X;
2971 X = Y;
2972 Y = width - tmp -1;
2973 break;
2974 case LANDSCAPE_FLIP:
2975 tmp = X;
2976 X = height - Y -1;
2977 Y = tmp;
2978 break;
2979 }
2980 #endif
2981 *x = X;
2982 *y = Y;
2983 return 1;
2984 #endif
2985 }
2986

mercurial