Sat, 20 Oct 2018 13:23:15 +0200
Initial checkin brewboard
0 | 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 |