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