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-2021 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. Feel free to use FabGL in free software and hardware:
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"
34 #include "TFTControllerGeneric.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 
52 namespace 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
60 inline 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 
69 inline 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 
79 inline 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 
90 inline 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 
96 inline uint16_t RGBA8888toNative(RGBA8888 const & rgba8888)
97 {
98  return preparePixel(RGB888(rgba8888.R, rgba8888.G, rgba8888.B));
99 }
100 
101 
102 TFTController::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 
121 TFTController::~TFTController()
122 {
123  end();
124 }
125 
126 
128 void 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
151 void 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
169 void 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
178 void 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 
192 void TFTController::begin()
193 {
194  begin(18, 23, 22, 21, 5, VSPI_HOST);
195 }
196 
197 
198 void TFTController::end()
199 {
200  if (m_updateTaskHandle)
201  vTaskDelete(m_updateTaskHandle);
202  m_updateTaskHandle = nullptr;
203 
204  freeViewPort();
205 
206  SPIEnd();
207 }
208 
209 
210 void 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 
247 void 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 
257 void 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 
267 void 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 
286 void 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 
329 void 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 
343 void TFTController::setReverseHorizontal(bool value)
344 {
345  m_reverseHorizontal = value;
346  setOrientation(m_orientation, true);
347 }
348 
349 
350 void 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 
382 void 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 
399 void 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 
417 void 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 
438 void 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 
458 void 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 
480 void 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 
500 void TFTController::writeCommand(uint8_t cmd)
501 {
502  gpio_set_level(m_DC, 0); // 0 = CMD
503  SPIWriteByte(cmd);
504 }
505 
506 
507 void TFTController::writeByte(uint8_t data)
508 {
509  gpio_set_level(m_DC, 1); // 1 = DATA
510  SPIWriteByte(data);
511 }
512 
513 
514 void 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
522 void TFTController::writeWord(uint16_t data)
523 {
524  gpio_set_level(m_DC, 1); // 1 = DATA
525  SPIWriteWord(data);
526 }
527 
528 
529 void TFTController::sendRefresh()
530 {
531  Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
532  addPrimitive(p);
533 }
534 
535 
536 void 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 
562 void 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 
572 void 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 
583 void 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 
624 void TFTController::suspendBackgroundPrimitiveExecution()
625 {
626  ++m_updateTaskFuncSuspended;
627  while (m_updateTaskRunning)
628  taskYIELD();
629 }
630 
631 
632 void 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 
640 void 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
651 void 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
664 void 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
673 void 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
681 void 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 
690 void 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 
698 void 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 
707 void 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 
716 void 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 
726 void 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 
737 void 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 
747 void 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 
755 void 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
767 void 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!
778 void 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 
788 void 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 
797 void 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 
808 void 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 
818 void 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 
828 void 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
int16_t X2
Definition: fabutils.h:179
Represents a 24 bit RGB color.
int16_t Y2
Definition: fabutils.h:180
int16_t Y1
Definition: fabutils.h:178
int16_t Y
uint8_t const * data
int16_t X1
Definition: fabutils.h:177
This file contains some utility classes and functions.
Definition: canvas.cpp:36
TFTOrientation
This enum defines TFT orientation.
Represents a rectangle.
Definition: fabutils.h:225
This file contains fabgl::TFTController definition.
int16_t X
uint8_t swapFGBG
uint8_t width