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 CurrentVideoMode::set(VideoMode::SPI);
154
155 #ifdef ARDUINO
156 m_spi = spi;
157 #endif
158
159 m_DC = DC;
160 m_RESX = RESX;
161 m_CS = CS;
162
163 setupGPIO();
164}
165#endif
166
167
168// use SPIClass
169// without CS it is not possible to share SPI with other devices
170#ifdef ARDUINO
171void TFTController::begin(SPIClass * spi, int DC, int RESX, int CS)
172{
173 begin(spi, int2gpio(DC), int2gpio(RESX), int2gpio(CS));
174}
175#endif
176
177
178// use SDK driver
179// without CS it is not possible to share SPI with other devices
180void TFTController::begin(int SCK, int MOSI, int DC, int RESX, int CS, int host)
181{
182 m_SPIHost = (spi_host_device_t)host;
183 m_SCK = int2gpio(SCK);
184 m_MOSI = int2gpio(MOSI);
185 m_DC = int2gpio(DC);
186 m_RESX = int2gpio(RESX);
187 m_CS = int2gpio(CS);
188
189 setupGPIO();
190 SPIBegin();
191}
192
193
194void TFTController::begin()
195{
196 begin(18, 23, 22, 21, 5, VSPI_HOST);
197}
198
199
200void TFTController::end()
201{
202 if (m_updateTaskHandle)
203 vTaskDelete(m_updateTaskHandle);
204 m_updateTaskHandle = nullptr;
205
206 freeViewPort();
207
208 SPIEnd();
209}
210
211
212void TFTController::setResolution(char const * modeline, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
213{
214 char label[32];
215 int pos = 0, swidth, sheight;
216 int count = sscanf(modeline, "\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
217 if (count != 3 || pos == 0)
218 return; // invalid modeline
219
220 m_screenWidth = swidth;
221 m_screenHeight = sheight;
222 m_screenCol = 0;
223 m_screenRow = 0;
224
225 // inform base class about screen size
226 setScreenSize(m_screenWidth, m_screenHeight);
227
228 setDoubleBuffered(doubleBuffered);
229
230 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
231 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
232
233 m_rot0ViewPortWidth = m_viewPortWidth;
234 m_rot0ViewPortHeight = m_viewPortHeight;
235
236 resetPaintState();
237
238 hardReset();
239 softReset();
240
241 // setup update task
242 xTaskCreate(&updateTaskFunc, "", TFT_UPDATETASK_STACK, this, TFT_UPDATETASK_PRIORITY, &m_updateTaskHandle);
243
244 // allows updateTaskFunc() to run
245 m_updateTaskFuncSuspended = 0;
246}
247
248
249void TFTController::setScreenCol(int value)
250{
251 if (value != m_screenCol) {
252 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
253 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
254 addPrimitive(p);
255 }
256}
257
258
259void TFTController::setScreenRow(int value)
260{
261 if (value != m_screenRow) {
262 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
263 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
264 addPrimitive(p);
265 }
266}
267
268
269void TFTController::hardReset()
270{
271 if (m_RESX != GPIO_UNUSED) {
272 SPIBeginWrite();
273
274 configureGPIO(m_RESX, GPIO_MODE_OUTPUT);
275 gpio_set_level(m_RESX, 1);
276 vTaskDelay(5 / portTICK_PERIOD_MS);
277 gpio_set_level(m_RESX, 0);
278 vTaskDelay(20 / portTICK_PERIOD_MS);
279 gpio_set_level(m_RESX, 1);
280
281 SPIEndWrite();
282
283 vTaskDelay(150 / portTICK_PERIOD_MS);
284 }
285}
286
287
288void TFTController::setupOrientation()
289{
290 freeViewPort();
291 m_viewPortWidth = m_rot0ViewPortWidth;
292 m_viewPortHeight = m_rot0ViewPortHeight;
293 m_rotOffsetX = 0;
294 m_rotOffsetY = 0;
295 uint8_t MX = m_reverseHorizontal ? 0x40 : 0;
296 uint8_t madclt = 0x08 | MX; // BGR
297 switch (m_orientation) {
298 case TFTOrientation::Rotate90:
299 tswap(m_viewPortWidth, m_viewPortHeight);
300 madclt |= 0x20; // MV = 1
301 madclt ^= 0x40; // inv MX
302 break;
303 case TFTOrientation::Rotate180:
304 madclt |= 0x80; // MY = 1
305 madclt ^= 0x40; // inv MX
306 m_rotOffsetY = m_controllerHeight - m_viewPortHeight;
307 m_rotOffsetX = m_controllerWidth - m_viewPortWidth;
308 break;
309 case TFTOrientation::Rotate270:
310 tswap(m_viewPortWidth, m_viewPortHeight);
311 madclt |= 0x20 | 0x80; // MV = 1, MY = 1
312 m_rotOffsetX = m_controllerHeight - m_viewPortWidth;
313 break;
314 default:
315 break;
316 }
317 // Memory Access Control
318 writeCommand(TFT_MADCTL);
319 writeByte(madclt);
320
321 // alloc viewport
322 allocViewPort();
323
324 // resets scrolling region, clipping rect, etc...
325 Primitive p;
326 p.cmd = PrimitiveCmd::Reset;
327 addPrimitive(p);
328}
329
330
331void TFTController::setOrientation(TFTOrientation value, bool force)
332{
333 if (m_orientation != value || force) {
334 suspendBackgroundPrimitiveExecution();
335 m_orientation = value;
336 SPIBeginWrite();
337 setupOrientation();
338 SPIEndWrite();
339 resumeBackgroundPrimitiveExecution();
340 sendRefresh();
341 }
342}
343
344
345void TFTController::setReverseHorizontal(bool value)
346{
347 m_reverseHorizontal = value;
348 setOrientation(m_orientation, true);
349}
350
351
352void TFTController::SPIBegin()
353{
354 #ifdef ARDUINO
355 if (m_spi)
356 return;
357 #endif
358
359 spi_bus_config_t busconf;
360 memset(&busconf, 0, sizeof(busconf));
361 busconf.mosi_io_num = m_MOSI;
362 busconf.miso_io_num = -1;
363 busconf.sclk_io_num = m_SCK;
364 busconf.quadwp_io_num = -1;
365 busconf.quadhd_io_num = -1;
366 busconf.flags = SPICOMMON_BUSFLAG_MASTER;
367 auto r = spi_bus_initialize(m_SPIHost, &busconf, TFT_DMACHANNEL);
368 if (r == ESP_OK || r == ESP_ERR_INVALID_STATE) { // ESP_ERR_INVALID_STATE, maybe spi_bus_initialize already called
369 spi_device_interface_config_t devconf;
370 memset(&devconf, 0, sizeof(devconf));
371 devconf.mode = TFT_SPI_MODE;
372 devconf.clock_speed_hz = TFT_SPI_WRITE_FREQUENCY;
373 devconf.spics_io_num = -1;
374 devconf.flags = 0;
375 devconf.queue_size = 1;
376 spi_bus_add_device(m_SPIHost, &devconf, &m_SPIDevHandle);
377 }
378
379 if (m_updateTaskFuncSuspended)
380 resumeBackgroundPrimitiveExecution();
381}
382
383
384void TFTController::SPIEnd()
385{
386 #ifdef ARDUINO
387 if (m_spi)
388 return;
389 #endif
390
391 suspendBackgroundPrimitiveExecution();
392
393 if (m_SPIDevHandle) {
394 spi_bus_remove_device(m_SPIDevHandle);
395 m_SPIDevHandle = nullptr;
396 spi_bus_free(m_SPIHost); // this will not free bus if there is a device still connected (ie sdcard)
397 }
398}
399
400
401void TFTController::SPIBeginWrite()
402{
403 #ifdef ARDUINO
404 if (m_spi) {
405 m_spi->beginTransaction(SPISettings(TFT_SPI_WRITE_FREQUENCY, SPI_MSBFIRST, TFT_SPI_MODE));
406 }
407 #endif
408
409 if (m_SPIDevHandle) {
410 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
411 }
412
413 if (m_CS != GPIO_UNUSED) {
414 gpio_set_level(m_CS, 0);
415 }
416}
417
418
419void TFTController::SPIEndWrite()
420{
421 if (m_CS != GPIO_UNUSED) {
422 gpio_set_level(m_CS, 1);
423 }
424
425 // leave in data mode
426 gpio_set_level(m_DC, 1); // 1 = DATA
427
428 #ifdef ARDUINO
429 if (m_spi) {
430 m_spi->endTransaction();
431 }
432 #endif
433
434 if (m_SPIDevHandle) {
435 spi_device_release_bus(m_SPIDevHandle);
436 }
437}
438
439
440void TFTController::SPIWriteByte(uint8_t data)
441{
442 #ifdef ARDUINO
443 if (m_spi) {
444 m_spi->write(data);
445 }
446 #endif
447
448 if (m_SPIDevHandle) {
449 spi_transaction_t ta;
450 ta.flags = SPI_TRANS_USE_TXDATA;
451 ta.length = 8;
452 ta.rxlength = 0;
453 ta.tx_data[0] = data;
454 ta.rx_buffer = nullptr;
455 spi_device_polling_transmit(m_SPIDevHandle, &ta);
456 }
457}
458
459
460void TFTController::SPIWriteWord(uint16_t data)
461{
462 #ifdef ARDUINO
463 if (m_spi) {
464 m_spi->write(data >> 8);
465 m_spi->write(data & 0xff);
466 }
467 #endif
468
469 if (m_SPIDevHandle) {
470 spi_transaction_t ta;
471 ta.flags = SPI_TRANS_USE_TXDATA;
472 ta.length = 16;
473 ta.rxlength = 0;
474 ta.tx_data[0] = data >> 8;
475 ta.tx_data[1] = data & 0xff;
476 ta.rx_buffer = nullptr;
477 spi_device_polling_transmit(m_SPIDevHandle, &ta);
478 }
479}
480
481
482void TFTController::SPIWriteBuffer(void * data, size_t size)
483{
484 #ifdef ARDUINO
485 if (m_spi) {
486 m_spi->writeBytes((uint8_t*)data, size);
487 }
488 #endif
489
490 if (m_SPIDevHandle) {
491 spi_transaction_t ta;
492 ta.flags = 0;
493 ta.length = 8 * size;
494 ta.rxlength = 0;
495 ta.tx_buffer = data;
496 ta.rx_buffer = nullptr;
497 spi_device_polling_transmit(m_SPIDevHandle, &ta);
498 }
499}
500
501
502void TFTController::writeCommand(uint8_t cmd)
503{
504 gpio_set_level(m_DC, 0); // 0 = CMD
505 SPIWriteByte(cmd);
506}
507
508
509void TFTController::writeByte(uint8_t data)
510{
511 gpio_set_level(m_DC, 1); // 1 = DATA
512 SPIWriteByte(data);
513}
514
515
516void TFTController::writeData(void * data, size_t size)
517{
518 gpio_set_level(m_DC, 1); // 1 = DATA
519 SPIWriteBuffer(data, size);
520}
521
522
523// high byte first
524void TFTController::writeWord(uint16_t data)
525{
526 gpio_set_level(m_DC, 1); // 1 = DATA
527 SPIWriteWord(data);
528}
529
530
531void TFTController::sendRefresh()
532{
533 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
534 addPrimitive(p);
535}
536
537
538void TFTController::sendScreenBuffer(Rect updateRect)
539{
540 SPIBeginWrite();
541
542 updateRect = updateRect.intersection(Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
543
544 // Column Address Set
545 writeCommand(TFT_CASET);
546 writeWord(m_rotOffsetX + updateRect.X1); // XS (X Start)
547 writeWord(m_rotOffsetX + updateRect.X2); // XE (X End)
548
549 // Row Address Set
550 writeCommand(TFT_RASET);
551 writeWord(m_rotOffsetY + updateRect.Y1); // YS (Y Start)
552 writeWord(m_rotOffsetY + updateRect.Y2); // YE (Y End)
553
554 writeCommand(TFT_RAMWR);
555 const int width = updateRect.width();
556 for (int row = updateRect.Y1; row <= updateRect.Y2; ++row) {
557 writeData(m_viewPort[row] + updateRect.X1, sizeof(uint16_t) * width);
558 }
559
560 SPIEndWrite();
561}
562
563
564void TFTController::allocViewPort()
565{
566 m_viewPort = (uint16_t**) heap_caps_malloc(m_viewPortHeight * sizeof(uint16_t*), MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
567 for (int i = 0; i < m_viewPortHeight; ++i) {
568 m_viewPort[i] = (uint16_t*) heap_caps_malloc(m_viewPortWidth * sizeof(uint16_t), MALLOC_CAP_DMA);
569 memset(m_viewPort[i], 0, m_viewPortWidth * sizeof(uint16_t));
570 }
571}
572
573
574void TFTController::freeViewPort()
575{
576 if (m_viewPort) {
577 for (int i = 0; i < m_viewPortHeight; ++i)
578 heap_caps_free(m_viewPort[i]);
579 heap_caps_free(m_viewPort);
580 m_viewPort = nullptr;
581 }
582}
583
584
585void TFTController::updateTaskFunc(void * pvParameters)
586{
587 TFTController * ctrl = (TFTController*) pvParameters;
588
589 while (true) {
590
591 ctrl->waitForPrimitives();
592
593 // primitive processing blocked?
594 if (ctrl->m_updateTaskFuncSuspended > 0)
595 ulTaskNotifyTake(true, portMAX_DELAY); // yes, wait for a notify
596
597 ctrl->m_updateTaskRunning = true;
598
599 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
600
601 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
602 do {
603
604 Primitive prim;
605 if (ctrl->getPrimitive(&prim, TFT_BACKGROUND_PRIMITIVE_TIMEOUT / 1000) == false)
606 break;
607
608 ctrl->execPrimitive(prim, updateRect, false);
609
610 if (ctrl->m_updateTaskFuncSuspended > 0)
611 break;
612
613 } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + TFT_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
614
615 ctrl->showSprites(updateRect);
616
617 ctrl->m_updateTaskRunning = false;
618
619 if (!ctrl->isDoubleBuffered())
620 ctrl->sendScreenBuffer(updateRect);
621 }
622}
623
624
625
626void TFTController::suspendBackgroundPrimitiveExecution()
627{
628 ++m_updateTaskFuncSuspended;
629 while (m_updateTaskRunning)
630 taskYIELD();
631}
632
633
634void TFTController::resumeBackgroundPrimitiveExecution()
635{
636 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
637 if (m_updateTaskFuncSuspended == 0)
638 xTaskNotifyGive(m_updateTaskHandle); // resume updateTaskFunc()
639}
640
641
642void TFTController::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
643{
644 genericSetPixelAt(pixelDesc, updateRect,
645 [&] (RGB888 const & color) { return preparePixel(color); },
646 [&] (int X, int Y, uint16_t pattern) { m_viewPort[Y][X] = pattern; }
647 );
648}
649
650
651// coordinates are absolute values (not relative to origin)
652// line clipped on current absolute clipping rectangle
653void TFTController::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
654{
655 genericAbsDrawLine(X1, Y1, X2, Y2, color,
656 [&] (RGB888 const & color) { return preparePixel(color); },
657 [&] (int Y, int X1, int X2, uint16_t pattern) { rawFillRow(Y, X1, X2, pattern); },
658 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
659 [&] (int X, int Y, uint16_t pattern) { m_viewPort[Y][X] = pattern; },
660 [&] (int X, int Y) { m_viewPort[Y][X] = ~m_viewPort[Y][X]; }
661 );
662}
663
664
665// parameters not checked
666void TFTController::rawFillRow(int y, int x1, int x2, uint16_t pattern)
667{
668 auto px = m_viewPort[y] + x1;
669 for (int x = x1; x <= x2; ++x, ++px)
670 *px = pattern;
671}
672
673
674// parameters not checked
675void TFTController::rawFillRow(int y, int x1, int x2, RGB888 color)
676{
677 rawFillRow(y, x1, x2, preparePixel(color));
678}
679
680
681// swaps all pixels inside the range x1...x2 of yA and yB
682// parameters not checked
683void TFTController::swapRows(int yA, int yB, int x1, int x2)
684{
685 auto pxA = m_viewPort[yA] + x1;
686 auto pxB = m_viewPort[yB] + x1;
687 for (int x = x1; x <= x2; ++x, ++pxA, ++pxB)
688 tswap(*pxA, *pxB);
689}
690
691
692void TFTController::rawInvertRow(int y, int x1, int x2)
693{
694 auto px = m_viewPort[y] + x1;
695 for (int x = x1; x <= x2; ++x, ++px)
696 *px = ~*px;
697}
698
699
700void TFTController::drawEllipse(Size const & size, Rect & updateRect)
701{
702 genericDrawEllipse(size, updateRect,
703 [&] (RGB888 const & color) { return preparePixel(color); },
704 [&] (int X, int Y, uint16_t pattern) { m_viewPort[Y][X] = pattern; }
705 );
706}
707
708
709void TFTController::clear(Rect & updateRect)
710{
711 hideSprites(updateRect);
712 auto pattern = preparePixel(getActualBrushColor());
713 for (int y = 0; y < m_viewPortHeight; ++y)
714 rawFillRow(y, 0, m_viewPortWidth - 1, pattern);
715}
716
717
718void TFTController::VScroll(int scroll, Rect & updateRect)
719{
720 genericVScroll(scroll, updateRect,
721 [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
722 [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
723 [&] (int y, int x1, int x2, RGB888 pattern) { rawFillRow(y, x1, x2, pattern); } // rawFillRow
724 );
725}
726
727
728void TFTController::HScroll(int scroll, Rect & updateRect)
729{
730 genericHScroll(scroll, updateRect,
731 [&] (RGB888 const & color) { return preparePixel(color); }, // preparePixel
732 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
733 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
734 [&] (uint16_t * row, int x, int pattern) { row[x] = pattern; } // rawSetPixelInRow
735 );
736}
737
738
739void TFTController::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
740{
741 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
742 [&] (RGB888 const & color) { return preparePixel(color); },
743 [&] (int y) { return m_viewPort[y]; },
744 [&] (uint16_t * row, int x, uint16_t pattern) { row[x] = pattern; }
745 );
746}
747
748
749void TFTController::invertRect(Rect const & rect, Rect & updateRect)
750{
751 genericInvertRect(rect, updateRect,
752 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
753 );
754}
755
756
757void TFTController::swapFGBG(Rect const & rect, Rect & updateRect)
758{
759 genericSwapFGBG(rect, updateRect,
760 [&] (RGB888 const & color) { return preparePixel(color); },
761 [&] (int y) { return m_viewPort[y]; },
762 [&] (uint16_t * row, int x) { return row[x]; },
763 [&] (uint16_t * row, int x, uint16_t pattern) { row[x] = pattern; }
764 );
765}
766
767
768// supports overlapping of source and dest rectangles
769void TFTController::copyRect(Rect const & source, Rect & updateRect)
770{
771 genericCopyRect(source, updateRect,
772 [&] (int y) { return m_viewPort[y]; },
773 [&] (uint16_t * row, int x) { return row[x]; },
774 [&] (uint16_t * row, int x, uint16_t pattern) { row[x] = pattern; }
775 );
776}
777
778
779// no bounds check is done!
780void TFTController::readScreen(Rect const & rect, RGB888 * destBuf)
781{
782 for (int y = rect.Y1; y <= rect.Y2; ++y) {
783 auto row = m_viewPort[y] + rect.X1;
784 for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf, ++row)
785 *destBuf = nativeToRGB888(*row);
786 }
787}
788
789
790void TFTController::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
791{
792 genericRawDrawBitmap_Native(destX, destY, (uint16_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
793 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
794 [&] (uint16_t * row, int x, uint16_t src) { row[x] = src; } // rawSetPixelInRow
795 );
796}
797
798
799void TFTController::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
800{
801 auto foregroundPattern = preparePixel(bitmap->foregroundColor);
802 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint16_t*)saveBackground, X1, Y1, XCount, YCount,
803 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
804 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
805 [&] (uint16_t * row, int x) { row[x] = foregroundPattern; } // rawSetPixelInRow
806 );
807}
808
809
810void TFTController::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
811{
812 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint16_t*)saveBackground, X1, Y1, XCount, YCount,
813 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
814 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
815 [&] (uint16_t * row, int x, uint8_t src) { row[x] = RGBA2222toNative(src); } // rawSetPixelInRow
816 );
817}
818
819
820void TFTController::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
821{
822 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint16_t*)saveBackground, X1, Y1, XCount, YCount,
823 [&] (int y) { return m_viewPort[y]; }, // rawGetRow
824 [&] (uint16_t * row, int x) { return row[x]; }, // rawGetPixelInRow
825 [&] (uint16_t * row, int x, RGBA8888 const & src) { row[x] = RGBA8888toNative(src); } // rawSetPixelInRow
826 );
827}
828
829
830void TFTController::swapBuffers()
831{
832 // nothing to do, we just send current view port to the device
833 sendScreenBuffer(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
834}
835
836
837
838
839} // 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:248