FabGL
ESP32 Display Controller and Graphics Library
TFTControllerGeneric.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27
28#include <string.h>
29
30#include "freertos/FreeRTOS.h"
31#include "freertos/task.h"
32
33#include "fabutils.h"
35
36
37#pragma GCC optimize ("O2")
38
39
40#define TFT_UPDATETASK_STACK 1024
41#define TFT_UPDATETASK_PRIORITY 5
42
43#define TFT_BACKGROUND_PRIMITIVE_TIMEOUT 10000 // uS
44
45#define TFT_SPI_WRITE_FREQUENCY 40000000
46#define TFT_SPI_MODE 3
47#define TFT_DMACHANNEL 2
48
49
50
51
52namespace fabgl {
53
54
55
56// ESP32 is little-endian (in SPI, low byte of 16bit word is sent first), so the 16bit word must be converted from
57// RRRRRGGG GGGBBBBB
58// to
59// GGGBBBBB RRRRRGGG
60inline uint16_t preparePixel(RGB888 const & px)
61{
62 return ((uint16_t)(px.G & 0xe0) >> 5) | // 0 .. 2: bits 5..7 of G
63 ((uint16_t)(px.R & 0xf8)) | // 3 .. 7: bits 3..7 of R
64 ((uint16_t)(px.B & 0xf8) << 5) | // 8 .. 12: bits 3..7 of B
65 ((uint16_t)(px.G & 0x1c) << 11); // 13 .. 15: bits 2..4 of G
66}
67
68
69inline RGB888 nativeToRGB888(uint16_t pattern)
70{
71 return RGB888(
72 (pattern & 0xf8),
73 ((pattern & 7) << 5) | ((pattern & 0xe000) >> 11),
74 ((pattern & 0x1f00) >> 5)
75 );
76}
77
78
79inline RGBA8888 nativeToRGBA8888(uint16_t pattern)
80{
81 return RGBA8888(
82 (pattern & 0xf8),
83 ((pattern & 7) << 5) | ((pattern & 0xe000) >> 11),
84 ((pattern & 0x1f00) >> 5),
85 0xff
86 );
87}
88
89
90inline uint16_t RGBA2222toNative(uint8_t rgba2222)
91{
92 return preparePixel(RGB888((rgba2222 & 3) * 85, ((rgba2222 >> 2) & 3) * 85, ((rgba2222 >> 4) & 3) * 85));
93}
94
95
96inline uint16_t RGBA8888toNative(RGBA8888 const & rgba8888)
97{
98 return preparePixel(RGB888(rgba8888.R, rgba8888.G, rgba8888.B));
99}
100
101
102TFTController::TFTController()
103 :
104 #ifdef ARDUINO
105 m_spi(nullptr),
106 #endif
107 m_SPIDevHandle(nullptr),
108 m_viewPort(nullptr),
109 m_controllerWidth(240),
110 m_controllerHeight(320),
111 m_rotOffsetX(0),
112 m_rotOffsetY(0),
113 m_updateTaskHandle(nullptr),
114 m_updateTaskRunning(false),
115 m_orientation(TFTOrientation::Rotate0),
116 m_reverseHorizontal(false)
117{
118}
119
120
121TFTController::~TFTController()
122{
123 end();
124}
125
126
128void TFTController::setupGPIO()
129{
130 // DC GPIO
131 configureGPIO(m_DC, GPIO_MODE_OUTPUT);
132 gpio_set_level(m_DC, 1);
133
134 // reset GPIO
135 if (m_RESX != GPIO_UNUSED) {
136 configureGPIO(m_RESX, GPIO_MODE_OUTPUT);
137 gpio_set_level(m_RESX, 1);
138 }
139
140 // CS GPIO
141 if (m_CS != GPIO_UNUSED) {
142 configureGPIO(m_CS, GPIO_MODE_OUTPUT);
143 gpio_set_level(m_CS, 1);
144 }
145}
146
147
148// use SPIClass
149// without CS it is not possible to share SPI with other devices
150#ifdef ARDUINO
151void TFTController::begin(SPIClass * spi, gpio_num_t DC, gpio_num_t RESX, gpio_num_t CS)
152{
153 #ifdef ARDUINO
154 m_spi = spi;
155 #endif
156
157 m_DC = DC;
158 m_RESX = RESX;
159 m_CS = CS;
160
161 setupGPIO();
162}
163#endif
164
165
166// use SPIClass
167// without CS it is not possible to share SPI with other devices
168#ifdef ARDUINO
169void TFTController::begin(SPIClass * spi, int DC, int RESX, int CS)
170{
171 begin(spi, int2gpio(DC), int2gpio(RESX), int2gpio(CS));
172}
173#endif
174
175
176// use SDK driver
177// without CS it is not possible to share SPI with other devices
178void TFTController::begin(int SCK, int MOSI, int DC, int RESX, int CS, int host)
179{
180 m_SPIHost = (spi_host_device_t)host;
181 m_SCK = int2gpio(SCK);
182 m_MOSI = int2gpio(MOSI);
183 m_DC = int2gpio(DC);
184 m_RESX = int2gpio(RESX);
185 m_CS = int2gpio(CS);
186
187 setupGPIO();
188 SPIBegin();
189}
190
191
192void TFTController::begin()
193{
194 begin(18, 23, 22, 21, 5, VSPI_HOST);
195}
196
197
198void TFTController::end()
199{
200 if (m_updateTaskHandle)
201 vTaskDelete(m_updateTaskHandle);
202 m_updateTaskHandle = nullptr;
203
204 freeViewPort();
205
206 SPIEnd();
207}
208
209
210void TFTController::setResolution(char const * modeline, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
211{
212 char label[32];
213 int pos = 0, swidth, sheight;
214 int count = sscanf(modeline, "\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
215 if (count != 3 || pos == 0)
216 return; // invalid modeline
217
218 m_screenWidth = swidth;
219 m_screenHeight = sheight;
220 m_screenCol = 0;
221 m_screenRow = 0;
222
223 // inform base class about screen size
224 setScreenSize(m_screenWidth, m_screenHeight);
225
226 setDoubleBuffered(doubleBuffered);
227
228 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
229 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
230
231 m_rot0ViewPortWidth = m_viewPortWidth;
232 m_rot0ViewPortHeight = m_viewPortHeight;
233
234 resetPaintState();
235
236 hardReset();
237 softReset();
238
239 // setup update task
240 xTaskCreate(&updateTaskFunc, "", TFT_UPDATETASK_STACK, this, TFT_UPDATETASK_PRIORITY, &m_updateTaskHandle);
241
242 // allows updateTaskFunc() to run
243 m_updateTaskFuncSuspended = 0;
244}
245
246
247void TFTController::setScreenCol(int value)
248{
249 if (value != m_screenCol) {
250 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
251 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
252 addPrimitive(p);
253 }
254}
255
256
257void TFTController::setScreenRow(int value)
258{
259 if (value != m_screenRow) {
260 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
261 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
262 addPrimitive(p);
263 }
264}
265
266
267void TFTController::hardReset()
268{
269 if (m_RESX != GPIO_UNUSED) {
270 SPIBeginWrite();
271
272 configureGPIO(m_RESX, GPIO_MODE_OUTPUT);
273 gpio_set_level(m_RESX, 1);
274 vTaskDelay(5 / portTICK_PERIOD_MS);
275 gpio_set_level(m_RESX, 0);
276 vTaskDelay(20 / portTICK_PERIOD_MS);
277 gpio_set_level(m_RESX, 1);
278
279 SPIEndWrite();
280
281 vTaskDelay(150 / portTICK_PERIOD_MS);
282 }
283}
284
285
286void TFTController::setupOrientation()
287{
288 freeViewPort();
289 m_viewPortWidth = m_rot0ViewPortWidth;
290 m_viewPortHeight = m_rot0ViewPortHeight;
291 m_rotOffsetX = 0;
292 m_rotOffsetY = 0;
293 uint8_t MX = m_reverseHorizontal ? 0x40 : 0;
294 uint8_t madclt = 0x08 | MX; // BGR
295 switch (m_orientation) {
296 case TFTOrientation::Rotate90:
297 tswap(m_viewPortWidth, m_viewPortHeight);
298 madclt |= 0x20; // MV = 1
299 madclt ^= 0x40; // inv MX
300 break;
301 case TFTOrientation::Rotate180:
302 madclt |= 0x80; // MY = 1
303 madclt ^= 0x40; // inv MX
304 m_rotOffsetY = m_controllerHeight - m_viewPortHeight;
305 m_rotOffsetX = m_controllerWidth - m_viewPortWidth;
306 break;
307 case TFTOrientation::Rotate270:
308 tswap(m_viewPortWidth, m_viewPortHeight);
309 madclt |= 0x20 | 0x80; // MV = 1, MY = 1
310 m_rotOffsetX = m_controllerHeight - m_viewPortWidth;
311 break;
312 default:
313 break;
314 }
315 // Memory Access Control
316 writeCommand(TFT_MADCTL);
317 writeByte(madclt);
318
319 // alloc viewport
320 allocViewPort();
321
322 // resets scrolling region, clipping rect, etc...
323 Primitive p;
324 p.cmd = PrimitiveCmd::Reset;
325 addPrimitive(p);
326}
327
328
329void TFTController::setOrientation(TFTOrientation value, bool force)
330{
331 if (m_orientation != value || force) {
332 suspendBackgroundPrimitiveExecution();
333 m_orientation = value;
334 SPIBeginWrite();
335 setupOrientation();
336 SPIEndWrite();
337 resumeBackgroundPrimitiveExecution();
338 sendRefresh();
339 }
340}
341
342
343void TFTController::setReverseHorizontal(bool value)
344{
345 m_reverseHorizontal = value;
346 setOrientation(m_orientation, true);
347}
348
349
350void TFTController::SPIBegin()
351{
352 #ifdef ARDUINO
353 if (m_spi)
354 return;
355 #endif
356
357 spi_bus_config_t busconf;
358 memset(&busconf, 0, sizeof(busconf));
359 busconf.mosi_io_num = m_MOSI;
360 busconf.miso_io_num = -1;
361 busconf.sclk_io_num = m_SCK;
362 busconf.quadwp_io_num = -1;
363 busconf.quadhd_io_num = -1;
364 busconf.flags = SPICOMMON_BUSFLAG_MASTER;
365 auto r = spi_bus_initialize(m_SPIHost, &busconf, TFT_DMACHANNEL);
366 if (r == ESP_OK || r == ESP_ERR_INVALID_STATE) { // ESP_ERR_INVALID_STATE, maybe spi_bus_initialize already called
367 spi_device_interface_config_t devconf;
368 memset(&devconf, 0, sizeof(devconf));
369 devconf.mode = TFT_SPI_MODE;
370 devconf.clock_speed_hz = TFT_SPI_WRITE_FREQUENCY;
371 devconf.spics_io_num = -1;
372 devconf.flags = 0;
373 devconf.queue_size = 1;
374 spi_bus_add_device(m_SPIHost, &devconf, &m_SPIDevHandle);
375 }
376
377 if (m_updateTaskFuncSuspended)
378 resumeBackgroundPrimitiveExecution();
379}
380
381
382void TFTController::SPIEnd()
383{
384 #ifdef ARDUINO
385 if (m_spi)
386 return;
387 #endif
388
389 suspendBackgroundPrimitiveExecution();
390
391 if (m_SPIDevHandle) {
392 spi_bus_remove_device(m_SPIDevHandle);
393 m_SPIDevHandle = nullptr;
394 spi_bus_free(m_SPIHost); // this will not free bus if there is a device still connected (ie sdcard)
395 }
396}
397
398
399void TFTController::SPIBeginWrite()
400{
401 #ifdef ARDUINO
402 if (m_spi) {
403 m_spi->beginTransaction(SPISettings(TFT_SPI_WRITE_FREQUENCY, SPI_MSBFIRST, TFT_SPI_MODE));
404 }
405 #endif
406
407 if (m_SPIDevHandle) {
408 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
409 }
410
411 if (m_CS != GPIO_UNUSED) {
412 gpio_set_level(m_CS, 0);
413 }
414}
415
416
417void TFTController::SPIEndWrite()
418{
419 if (m_CS != GPIO_UNUSED) {
420 gpio_set_level(m_CS, 1);
421 }
422
423 // leave in data mode
424 gpio_set_level(m_DC, 1); // 1 = DATA
425
426 #ifdef ARDUINO
427 if (m_spi) {
428 m_spi->endTransaction();
429 }
430 #endif
431
432 if (m_SPIDevHandle) {
433 spi_device_release_bus(m_SPIDevHandle);
434 }
435}
436
437
438void TFTController::SPIWriteByte(uint8_t data)
439{
440 #ifdef ARDUINO
441 if (m_spi) {
442 m_spi->write(data);
443 }
444 #endif
445
446 if (m_SPIDevHandle) {
447 spi_transaction_t ta;
448 ta.flags = SPI_TRANS_USE_TXDATA;
449 ta.length = 8;
450 ta.rxlength = 0;
451 ta.tx_data[0] = data;
452 ta.rx_buffer = nullptr;
453 spi_device_polling_transmit(m_SPIDevHandle, &ta);
454 }
455}
456
457
458void TFTController::SPIWriteWord(uint16_t data)
459{
460 #ifdef ARDUINO
461 if (m_spi) {
462 m_spi->write(data >> 8);
463 m_spi->write(data & 0xff);
464 }
465 #endif
466
467 if (m_SPIDevHandle) {
468 spi_transaction_t ta;
469 ta.flags = SPI_TRANS_USE_TXDATA;
470 ta.length = 16;
471 ta.rxlength = 0;
472 ta.tx_data[0] = data >> 8;
473 ta.tx_data[1] = data & 0xff;
474 ta.rx_buffer = nullptr;
475 spi_device_polling_transmit(m_SPIDevHandle, &ta);
476 }
477}
478
479
480void TFTController::SPIWriteBuffer(void * data, size_t size)
481{
482 #ifdef ARDUINO
483 if (m_spi) {
484 m_spi->writeBytes((uint8_t*)data, size);
485 }
486 #endif
487
488 if (m_SPIDevHandle) {
489 spi_transaction_t ta;
490 ta.flags = 0;
491 ta.length = 8 * size;
492 ta.rxlength = 0;
493 ta.tx_buffer = data;
494 ta.rx_buffer = nullptr;
495 spi_device_polling_transmit(m_SPIDevHandle, &ta);
496 }
497}
498
499
500void TFTController::writeCommand(uint8_t cmd)
501{
502 gpio_set_level(m_DC, 0); // 0 = CMD
503 SPIWriteByte(cmd);
504}
505
506
507void TFTController::writeByte(uint8_t data)
508{
509 gpio_set_level(m_DC, 1); // 1 = DATA
510 SPIWriteByte(data);
511}
512
513
514void TFTController::writeData(void * data, size_t size)
515{
516 gpio_set_level(m_DC, 1); // 1 = DATA
517 SPIWriteBuffer(data, size);
518}
519
520
521// high byte first
522void TFTController::writeWord(uint16_t data)
523{
524 gpio_set_level(m_DC, 1); // 1 = DATA
525 SPIWriteWord(data);
526}
527
528
529void TFTController::sendRefresh()
530{
531 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
532 addPrimitive(p);
533}
534
535
536void TFTController::sendScreenBuffer(Rect updateRect)
537{
538 SPIBeginWrite();
539
540 updateRect = updateRect.intersection(Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
541
542 // Column Address Set
543 writeCommand(TFT_CASET);
544 writeWord(m_rotOffsetX + updateRect.X1); // XS (X Start)
545 writeWord(m_rotOffsetX + updateRect.X2); // XE (X End)
546
547 // Row Address Set
548 writeCommand(TFT_RASET);
549 writeWord(m_rotOffsetY + updateRect.Y1); // YS (Y Start)
550 writeWord(m_rotOffsetY + updateRect.Y2); // YE (Y End)
551
552 writeCommand(TFT_RAMWR);
553 const int width = updateRect.width();
554 for (int row = updateRect.Y1; row <= updateRect.Y2; ++row) {
555 writeData(m_viewPort[row] + updateRect.X1, sizeof(uint16_t) * width);
556 }
557
558 SPIEndWrite();
559}
560
561
562void TFTController::allocViewPort()
563{
564 m_viewPort = (uint16_t**) heap_caps_malloc(m_viewPortHeight * sizeof(uint16_t*), MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
565 for (int i = 0; i < m_viewPortHeight; ++i) {
566 m_viewPort[i] = (uint16_t*) heap_caps_malloc(m_viewPortWidth * sizeof(uint16_t), MALLOC_CAP_DMA);
567 memset(m_viewPort[i], 0, m_viewPortWidth * sizeof(uint16_t));
568 }
569}
570
571
572void TFTController::freeViewPort()
573{
574 if (m_viewPort) {
575 for (int i = 0; i < m_viewPortHeight; ++i)
576 heap_caps_free(m_viewPort[i]);
577 heap_caps_free(m_viewPort);
578 m_viewPort = nullptr;
579 }
580}
581
582
583void TFTController::updateTaskFunc(void * pvParameters)
584{
585 TFTController * ctrl = (TFTController*) pvParameters;
586
587 while (true) {
588
589 ctrl->waitForPrimitives();
590
591 // primitive processing blocked?
592 if (ctrl->m_updateTaskFuncSuspended > 0)
593 ulTaskNotifyTake(true, portMAX_DELAY); // yes, wait for a notify
594
595 ctrl->m_updateTaskRunning = true;
596
597 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
598
599 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
600 do {
601
602 Primitive prim;
603 if (ctrl->getPrimitive(&prim, TFT_BACKGROUND_PRIMITIVE_TIMEOUT / 1000) == false)
604 break;
605
606 ctrl->execPrimitive(prim, updateRect, false);
607
608 if (ctrl->m_updateTaskFuncSuspended > 0)
609 break;
610
611 } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + TFT_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
612
613 ctrl->showSprites(updateRect);
614
615 ctrl->m_updateTaskRunning = false;
616
617 if (!ctrl->isDoubleBuffered())
618 ctrl->sendScreenBuffer(updateRect);
619 }
620}
621
622
623
624void TFTController::suspendBackgroundPrimitiveExecution()
625{
626 ++m_updateTaskFuncSuspended;
627 while (m_updateTaskRunning)
628 taskYIELD();
629}
630
631
632void TFTController::resumeBackgroundPrimitiveExecution()
633{
634 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
635 if (m_updateTaskFuncSuspended == 0)
636 xTaskNotifyGive(m_updateTaskHandle); // resume updateTaskFunc()
637}
638
639
640void TFTController::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
641{
642 genericSetPixelAt(pixelDesc, updateRect,
643 [&] (RGB888 const & color) { return preparePixel(color); },
644 [&] (int X, int Y, uint16_t pattern) { m_viewPort[Y][X] = pattern; }
645 );
646}
647
648
649// coordinates are absolute values (not relative to origin)
650// line clipped on current absolute clipping rectangle
651void TFTController::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
652{
653 genericAbsDrawLine(X1, Y1, X2, Y2, color,
654 [&] (RGB888 const & color) { return preparePixel(color); },
655 [&] (int Y, int X1, int X2, uint16_t pattern) { rawFillRow(Y, X1, X2, pattern); },
656 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
657 [&] (int X, int Y, uint16_t pattern) { m_viewPort[Y][X] = pattern; },
658 [&] (int X, int Y) { m_viewPort[Y][X] = ~m_viewPort[Y][X]; }
659 );
660}
661
662
663// parameters not checked
664void TFTController::rawFillRow(int y, int x1, int x2, uint16_t pattern)
665{
666 auto px = m_viewPort[y] + x1;
667 for (int x = x1; x <= x2; ++x, ++px)
668 *px = pattern;
669}
670
671
672// parameters not checked
673void TFTController::rawFillRow(int y, int x1, int x2, RGB888 color)
674{
675 rawFillRow(y, x1, x2, preparePixel(color));
676}
677
678
679// swaps all pixels inside the range x1...x2 of yA and yB
680// parameters not checked
681void TFTController::swapRows(int yA, int yB, int x1, int x2)
682{
683 auto pxA = m_viewPort[yA] + x1;
684 auto pxB = m_viewPort[yB] + x1;
685 for (int x = x1; x <= x2; ++x, ++pxA, ++pxB)
686 tswap(*pxA, *pxB);
687}
688
689
690void TFTController::rawInvertRow(int y, int x1, int x2)
691{
692 auto px = m_viewPort[y] + x1;
693 for (int x = x1; x <= x2; ++x, ++px)
694 *px = ~*px;
695}
696
697
698void TFTController::drawEllipse(Size const & size, Rect & updateRect)
699{
700 genericDrawEllipse(size, updateRect,
701 [&] (RGB888 const & color) { return preparePixel(color); },
702 [&] (int X, int Y, uint16_t pattern) { m_viewPort[Y][X] = pattern; }
703 );
704}
705
706
707void TFTController::clear(Rect & updateRect)
708{
709 hideSprites(updateRect);
710 auto pattern = preparePixel(getActualBrushColor());
711 for (int y = 0; y < m_viewPortHeight; ++y)
712 rawFillRow(y, 0, m_viewPortWidth - 1, pattern);
713}
714
715
716void TFTController::VScroll(int scroll, Rect & updateRect)
717{
718 genericVScroll(scroll, updateRect,
719 [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
720 [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
721 [&] (int y, int x1, int x2, RGB888 pattern) { rawFillRow(y, x1, x2, pattern); } // rawFillRow
722 );
723}
724
725
726void TFTController::HScroll(int scroll, Rect & updateRect)
727{
728 genericHScroll(scroll, updateRect,
729 [&] (RGB888 const & color) { return preparePixel(color); }, // preparePixel
730 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
731 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
732 [&] (uint16_t * row, int x, int pattern) { row[x] = pattern; } // rawSetPixelInRow
733 );
734}
735
736
737void TFTController::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
738{
739 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
740 [&] (RGB888 const & color) { return preparePixel(color); },
741 [&] (int y) { return m_viewPort[y]; },
742 [&] (uint16_t * row, int x, uint16_t pattern) { row[x] = pattern; }
743 );
744}
745
746
747void TFTController::invertRect(Rect const & rect, Rect & updateRect)
748{
749 genericInvertRect(rect, updateRect,
750 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
751 );
752}
753
754
755void TFTController::swapFGBG(Rect const & rect, Rect & updateRect)
756{
757 genericSwapFGBG(rect, updateRect,
758 [&] (RGB888 const & color) { return preparePixel(color); },
759 [&] (int y) { return m_viewPort[y]; },
760 [&] (uint16_t * row, int x) { return row[x]; },
761 [&] (uint16_t * row, int x, uint16_t pattern) { row[x] = pattern; }
762 );
763}
764
765
766// supports overlapping of source and dest rectangles
767void TFTController::copyRect(Rect const & source, Rect & updateRect)
768{
769 genericCopyRect(source, updateRect,
770 [&] (int y) { return m_viewPort[y]; },
771 [&] (uint16_t * row, int x) { return row[x]; },
772 [&] (uint16_t * row, int x, uint16_t pattern) { row[x] = pattern; }
773 );
774}
775
776
777// no bounds check is done!
778void TFTController::readScreen(Rect const & rect, RGB888 * destBuf)
779{
780 for (int y = rect.Y1; y <= rect.Y2; ++y) {
781 auto row = m_viewPort[y] + rect.X1;
782 for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf, ++row)
783 *destBuf = nativeToRGB888(*row);
784 }
785}
786
787
788void TFTController::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
789{
790 genericRawDrawBitmap_Native(destX, destY, (uint16_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
791 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
792 [&] (uint16_t * row, int x, uint16_t src) { row[x] = src; } // rawSetPixelInRow
793 );
794}
795
796
797void TFTController::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
798{
799 auto foregroundPattern = preparePixel(bitmap->foregroundColor);
800 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint16_t*)saveBackground, X1, Y1, XCount, YCount,
801 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
802 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
803 [&] (uint16_t * row, int x) { row[x] = foregroundPattern; } // rawSetPixelInRow
804 );
805}
806
807
808void TFTController::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
809{
810 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint16_t*)saveBackground, X1, Y1, XCount, YCount,
811 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
812 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
813 [&] (uint16_t * row, int x, uint8_t src) { row[x] = RGBA2222toNative(src); } // rawSetPixelInRow
814 );
815}
816
817
818void TFTController::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
819{
820 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint16_t*)saveBackground, X1, Y1, XCount, YCount,
821 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
822 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
823 [&] (uint16_t * row, int x, RGBA8888 const & src) { row[x] = RGBA8888toNative(src); } // rawSetPixelInRow
824 );
825}
826
827
828void TFTController::swapBuffers()
829{
830 // nothing to do, we just send current view port to the device
831 sendScreenBuffer(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
832}
833
834
835
836
837} // end of namespace
This file contains fabgl::TFTController definition.
uint8_t width
uint8_t const * data
int16_t X
uint8_t swapFGBG
int16_t Y
int16_t X1
Definition: fabutils.h:0
int16_t Y2
Definition: fabutils.h:3
int16_t X2
Definition: fabutils.h:2
int16_t Y1
Definition: fabutils.h:1
This file contains some utility classes and functions.
TFTOrientation
This enum defines TFT orientation.
Represents a 24 bit RGB color.
Represents a rectangle.
Definition: fabutils.h:244