FabGL
ESP32 Display Controller and Graphics Library
vga2controller.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 <alloca.h>
25 #include <stdarg.h>
26 #include <math.h>
27 #include <string.h>
28 
29 #include "freertos/FreeRTOS.h"
30 #include "freertos/task.h"
31 
32 #include "soc/i2s_struct.h"
33 #include "soc/i2s_reg.h"
34 #include "driver/periph_ctrl.h"
35 #include "rom/lldesc.h"
36 #include "soc/rtc.h"
37 #include "esp_spi_flash.h"
38 #include "esp_heap_caps.h"
39 
40 #include "fabutils.h"
41 #include "vga2controller.h"
42 #include "devdrivers/swgenerator.h"
43 
44 
45 
46 
47 
48 
49 
50 
51 namespace fabgl {
52 
53 
54 
55 static inline __attribute__((always_inline)) void VGA2_SETPIXELINROW(uint8_t * row, int x, int value) {
56  int brow = x >> 3;
57  row[brow] ^= (-value ^ row[brow]) & (0x80 >> (x & 7));
58 }
59 
60 static inline __attribute__((always_inline)) int VGA2_GETPIXELINROW(uint8_t * row, int x) {
61  int brow = x >> 3;
62  return (row[brow] & (0x80 >> (x & 7))) != 0;
63 }
64 
65 #define VGA2_INVERTPIXELINROW(row, x) (row)[(x) >> 3] ^= (0x80 >> ((x) & 7))
66 
67 static inline __attribute__((always_inline)) void VGA2_SETPIXEL(int x, int y, int value) {
68  auto row = (uint8_t*) VGA2Controller::sgetScanline(y);
69  int brow = x >> 3;
70  row[brow] ^= (-value ^ row[brow]) & (0x80 >> (x & 7));
71 }
72 
73 #define VGA2_GETPIXEL(x, y) VGA2_GETPIXELINROW((uint8_t*)VGA2Controller::s_viewPort[(y)], (x))
74 
75 #define VGA2_INVERT_PIXEL(x, y) VGA2_INVERTPIXELINROW((uint8_t*)VGA2Controller::s_viewPort[(y)], (x))
76 
77 
78 
79 
80 /*************************************************************************************/
81 /* VGA2Controller definitions */
82 
83 
84 VGA2Controller * VGA2Controller::s_instance = nullptr;
85 
86 
87 
88 VGA2Controller::VGA2Controller()
89  : VGAPalettedController(VGA2_LinesCount, NativePixelFormat::PALETTE2, 8, 1, ISRHandler)
90 {
91  s_instance = this;
92  m_packedPaletteIndexOctet_to_signals = (uint64_t *) heap_caps_malloc(256 * sizeof(uint64_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
93 }
94 
95 
96 VGA2Controller::~VGA2Controller()
97 {
98  heap_caps_free((void *)m_packedPaletteIndexOctet_to_signals);
99 }
100 
101 
102 void VGA2Controller::setupDefaultPalette()
103 {
104  setPaletteItem(0, RGB888(0, 0, 0)); // 0: black
105  setPaletteItem(1, RGB888(255, 255, 255)); // 1: white
106 }
107 
108 
109 void VGA2Controller::setPaletteItem(int index, RGB888 const & color)
110 {
111  index %= 2;
112  m_palette[index] = color;
113  auto packed222 = RGB888toPackedRGB222(color);
114  for (int i = 0; i < 256; ++i) {
115  auto b = (uint8_t *) (m_packedPaletteIndexOctet_to_signals + i);
116  for (int j = 0; j < 8; ++j) {
117  auto aj = 7 - j;
118  if ((index == 0 && ((1 << aj) & i) == 0) || (index == 1 && ((1 << aj) & i) != 0)) {
119  b[j ^ 2] = m_HVSync | packed222;
120  }
121  }
122  }
123 }
124 
125 
126 void VGA2Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
127 {
128  genericSetPixelAt(pixelDesc, updateRect,
129  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
130  VGA2_SETPIXEL
131  );
132 }
133 
134 
135 // coordinates are absolute values (not relative to origin)
136 // line clipped on current absolute clipping rectangle
137 void VGA2Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
138 {
139  genericAbsDrawLine(X1, Y1, X2, Y2, color,
140  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
141  [&] (int Y, int X1, int X2, uint8_t colorIndex) { rawFillRow(Y, X1, X2, colorIndex); },
142  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
143  VGA2_SETPIXEL,
144  [&] (int X, int Y) { VGA2_INVERT_PIXEL(X, Y); }
145  );
146 }
147 
148 
149 // parameters not checked
150 void VGA2Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
151 {
152  rawFillRow(y, x1, x2, RGB888toPaletteIndex(color));
153 }
154 
155 
156 // parameters not checked
157 void VGA2Controller::rawFillRow(int y, int x1, int x2, uint8_t colorIndex)
158 {
159  uint8_t * row = (uint8_t*) m_viewPort[y];
160  // fill first pixels before full 8 bits word
161  int x = x1;
162  for (; x <= x2 && (x & 7) != 0; ++x) {
163  VGA2_SETPIXELINROW(row, x, colorIndex);
164  }
165  // fill whole 8 bits words (8 pixels)
166  if (x <= x2) {
167  int sz = (x2 & ~7) - x;
168  memset((void*)(row + x / 8), colorIndex ? 0xFF : 0x00, sz / 8);
169  x += sz;
170  }
171  // fill last unaligned pixels
172  for (; x <= x2; ++x) {
173  VGA2_SETPIXELINROW(row, x, colorIndex);
174  }
175 }
176 
177 
178 // parameters not checked
179 void VGA2Controller::rawInvertRow(int y, int x1, int x2)
180 {
181  auto row = m_viewPort[y];
182  for (int x = x1; x <= x2; ++x)
183  VGA2_INVERTPIXELINROW(row, x);
184 }
185 
186 
187 void VGA2Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
188 {
189  auto srcRow = (uint8_t*) m_viewPort[srcY];
190  auto dstRow = (uint8_t*) m_viewPort[dstY];
191  // copy first pixels before full 8 bits word
192  int x = x1;
193  for (; x <= x2 && (x & 7) != 0; ++x) {
194  VGA2_SETPIXELINROW(dstRow, x, VGA2_GETPIXELINROW(srcRow, x));
195  }
196  // copy whole 8 bits words (8 pixels)
197  auto src = (uint8_t*)(srcRow + x / 8);
198  auto dst = (uint8_t*)(dstRow + x / 8);
199  for (int right = (x2 & ~7); x < right; x += 8)
200  *dst++ = *src++;
201  // copy last unaligned pixels
202  for (x = (x2 & ~7); x <= x2; ++x) {
203  VGA2_SETPIXELINROW(dstRow, x, VGA2_GETPIXELINROW(srcRow, x));
204  }
205 }
206 
207 
208 void VGA2Controller::swapRows(int yA, int yB, int x1, int x2)
209 {
210  auto rowA = (uint8_t*) m_viewPort[yA];
211  auto rowB = (uint8_t*) m_viewPort[yB];
212  // swap first pixels before full 8 bits word
213  int x = x1;
214  for (; x <= x2 && (x & 7) != 0; ++x) {
215  uint8_t a = VGA2_GETPIXELINROW(rowA, x);
216  uint8_t b = VGA2_GETPIXELINROW(rowB, x);
217  VGA2_SETPIXELINROW(rowA, x, b);
218  VGA2_SETPIXELINROW(rowB, x, a);
219  }
220  // swap whole 8 bits words (8 pixels)
221  auto a = (uint8_t*)(rowA + x / 8);
222  auto b = (uint8_t*)(rowB + x / 8);
223  for (int right = (x2 & ~7); x < right; x += 8)
224  tswap(*a++, *b++);
225  // swap last unaligned pixels
226  for (x = (x2 & ~7); x <= x2; ++x) {
227  uint8_t a = VGA2_GETPIXELINROW(rowA, x);
228  uint8_t b = VGA2_GETPIXELINROW(rowB, x);
229  VGA2_SETPIXELINROW(rowA, x, b);
230  VGA2_SETPIXELINROW(rowB, x, a);
231  }
232 }
233 
234 
235 void VGA2Controller::drawEllipse(Size const & size, Rect & updateRect)
236 {
237  genericDrawEllipse(size, updateRect,
238  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
239  VGA2_SETPIXEL
240  );
241 }
242 
243 
244 void VGA2Controller::clear(Rect & updateRect)
245 {
246  hideSprites(updateRect);
247  uint8_t paletteIndex = RGB888toPaletteIndex(getActualBrushColor());
248  uint8_t pattern8 = paletteIndex ? 0xFF : 0x00;
249  for (int y = 0; y < m_viewPortHeight; ++y)
250  memset((uint8_t*) m_viewPort[y], pattern8, m_viewPortWidth / 8);
251 }
252 
253 
254 // scroll < 0 -> scroll UP
255 // scroll > 0 -> scroll DOWN
256 void VGA2Controller::VScroll(int scroll, Rect & updateRect)
257 {
258  genericVScroll(scroll, updateRect,
259  [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
260  [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
261  [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
262  );
263 }
264 
265 
266 void VGA2Controller::HScroll(int scroll, Rect & updateRect)
267 {
268  hideSprites(updateRect);
269  uint8_t back = RGB888toPaletteIndex(getActualBrushColor());
270  uint8_t back8 = back ? 0xFF : 0x00;
271 
272  int Y1 = paintState().scrollingRegion.Y1;
273  int Y2 = paintState().scrollingRegion.Y2;
274  int X1 = paintState().scrollingRegion.X1;
275  int X2 = paintState().scrollingRegion.X2;
276 
277  int width = X2 - X1 + 1;
278  bool HScrolllingRegionAligned = ((X1 & 7) == 0 && (width & 7) == 0); // 8 pixels aligned
279 
280  if (scroll < 0) {
281  // scroll left
282  for (int y = Y1; y <= Y2; ++y) {
283  if (HScrolllingRegionAligned) {
284  // aligned horizontal scrolling region, fast version
285  uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 8;
286  for (int s = -scroll; s > 0;) {
287  if (s < 8) {
288  // scroll left by 1..7
289  int sz = width / 8;
290  uint8_t prev = back8;
291  for (int i = sz - 1; i >= 0; --i) {
292  uint8_t lowbits = prev >> (8 - s);
293  prev = row[i];
294  row[i] = (row[i] << s) | lowbits;
295  }
296  s = 0;
297  } else {
298  // scroll left by multiplies of 8
299  auto sc = s & ~7;
300  auto sz = width & ~7;
301  memmove(row, row + sc / 8, (sz - sc) / 8);
302  rawFillRow(y, X2 - sc + 1, X2, back);
303  s -= sc;
304  }
305  }
306  } else {
307  // unaligned horizontal scrolling region, fallback to slow version
308  auto row = (uint8_t*) m_viewPort[y];
309  for (int x = X1; x <= X2 + scroll; ++x)
310  VGA2_SETPIXELINROW(row, x, VGA2_GETPIXELINROW(row, x - scroll));
311  // fill right area with brush color
312  rawFillRow(y, X2 + 1 + scroll, X2, back);
313  }
314  }
315  } else if (scroll > 0) {
316  // scroll right
317  for (int y = Y1; y <= Y2; ++y) {
318  if (HScrolllingRegionAligned) {
319  // aligned horizontal scrolling region, fast version
320  uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 8;
321  for (int s = scroll; s > 0;) {
322  if (s < 8) {
323  // scroll right by 1..7
324  int sz = width / 8;
325  uint8_t prev = back8;
326  for (int i = 0; i < sz; ++i) {
327  uint8_t highbits = prev << (8 - s);
328  prev = row[i];
329  row[i] = (row[i] >> s) | highbits;
330  }
331  s = 0;
332  } else {
333  // scroll right by multiplies of 8
334  auto sc = s & ~7;
335  auto sz = width & ~7;
336  memmove(row + sc / 8, row, (sz - sc) / 8);
337  rawFillRow(y, X1, X1 + sc - 1, back);
338  s -= sc;
339  }
340  }
341  } else {
342  // unaligned horizontal scrolling region, fallback to slow version
343  auto row = (uint8_t*) m_viewPort[y];
344  for (int x = X2 - scroll; x >= X1; --x)
345  VGA2_SETPIXELINROW(row, x + scroll, VGA2_GETPIXELINROW(row, x));
346  // fill left area with brush color
347  rawFillRow(y, X1, X1 + scroll - 1, back);
348  }
349  }
350 
351  }
352 }
353 
354 
355 void VGA2Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
356 {
357  genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
358  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
359  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
360  VGA2_SETPIXELINROW
361  );
362 }
363 
364 
365 void VGA2Controller::invertRect(Rect const & rect, Rect & updateRect)
366 {
367  genericInvertRect(rect, updateRect,
368  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
369  );
370 }
371 
372 
373 void VGA2Controller::swapFGBG(Rect const & rect, Rect & updateRect)
374 {
375  genericSwapFGBG(rect, updateRect,
376  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
377  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
378  VGA2_GETPIXELINROW,
379  VGA2_SETPIXELINROW
380  );
381 }
382 
383 
384 // Slow operation!
385 // supports overlapping of source and dest rectangles
386 void VGA2Controller::copyRect(Rect const & source, Rect & updateRect)
387 {
388  genericCopyRect(source, updateRect,
389  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
390  VGA2_GETPIXELINROW,
391  VGA2_SETPIXELINROW
392  );
393 }
394 
395 
396 // no bounds check is done!
397 void VGA2Controller::readScreen(Rect const & rect, RGB888 * destBuf)
398 {
399  for (int y = rect.Y1; y <= rect.Y2; ++y) {
400  auto row = (uint8_t*) m_viewPort[y];
401  for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf) {
402  const RGB222 v = m_palette[VGA2_GETPIXELINROW(row, x)];
403  *destBuf = RGB888(v.R * 85, v.G * 85, v.B * 85); // 85 x 3 = 255
404  }
405  }
406 }
407 
408 
409 void VGA2Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
410 {
411  genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
412  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
413  VGA2_SETPIXELINROW
414  );
415 }
416 
417 
418 void VGA2Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
419 {
420  auto foregroundColorIndex = RGB888toPaletteIndex(bitmap->foregroundColor);
421  genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
422  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
423  VGA2_GETPIXELINROW,
424  [&] (uint8_t * row, int x) { VGA2_SETPIXELINROW(row, x, foregroundColorIndex); } // rawSetPixelInRow
425  );
426 }
427 
428 
429 void VGA2Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
430 {
431  genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
432  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
433  VGA2_GETPIXELINROW,
434  [&] (uint8_t * row, int x, uint8_t src) { VGA2_SETPIXELINROW(row, x, RGB2222toPaletteIndex(src)); } // rawSetPixelInRow
435  );
436 }
437 
438 
439 void VGA2Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
440 {
441  genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
442  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
443  [&] (uint8_t * row, int x) { return VGA2_GETPIXELINROW(row, x); }, // rawGetPixelInRow
444  [&] (uint8_t * row, int x, RGBA8888 const & src) { VGA2_SETPIXELINROW(row, x, RGB8888toPaletteIndex(src)); } // rawSetPixelInRow
445  );
446 }
447 
448 
449 #pragma GCC optimize ("O2")
450 
451 
452 void IRAM_ATTR VGA2Controller::ISRHandler(void * arg)
453 {
454  #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
455  auto s1 = getCycleCount();
456  #endif
457 
458  auto ctrl = (VGA2Controller *) arg;
459 
460  if (I2S1.int_st.out_eof) {
461 
462  auto const desc = (lldesc_t*) I2S1.out_eof_des_addr;
463 
464  if (desc == s_frameResetDesc)
465  s_scanLine = 0;
466 
467  auto const width = ctrl->m_viewPortWidth;
468  auto const height = ctrl->m_viewPortHeight;
469  auto const packedPaletteIndexOctet_to_signals = (uint64_t const *) ctrl->m_packedPaletteIndexOctet_to_signals;
470  auto const lines = ctrl->m_lines;
471 
472  int scanLine = (s_scanLine + VGA2_LinesCount / 2) % height;
473 
474  auto lineIndex = scanLine & (VGA2_LinesCount - 1);
475 
476  for (int i = 0; i < VGA2_LinesCount / 2; ++i) {
477 
478  auto src = (uint8_t const *) s_viewPortVisible[scanLine];
479  auto dest = (uint64_t*) lines[lineIndex];
480 
481  // optimizazion warn: horizontal resolution must be a multiple of 16!
482  for (int col = 0; col < width; col += 16) {
483 
484  auto src1 = *(src + 0);
485  auto src2 = *(src + 1);
486 
487  PSRAM_HACK;
488 
489  auto v1 = packedPaletteIndexOctet_to_signals[src1];
490  auto v2 = packedPaletteIndexOctet_to_signals[src2];
491 
492  *(dest + 0) = v1;
493  *(dest + 1) = v2;
494 
495  dest += 2;
496  src += 2;
497 
498  }
499 
500  ++lineIndex;
501  ++scanLine;
502  }
503 
504  s_scanLine += VGA2_LinesCount / 2;
505 
506  if (scanLine >= height && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled()) {
507  // vertical sync, unlock primitive execution task
508  // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
509  vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
510  }
511 
512  }
513 
514  #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
515  s_vgapalctrlcycles += getCycleCount() - s1;
516  #endif
517 
518  I2S1.int_clr.val = I2S1.int_st.val;
519 }
520 
521 
522 
523 
524 } // 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
This file contains fabgl::GPIOStream definition.
void setPaletteItem(int index, RGB888 const &color)
Determines color of specified palette item.
int16_t X1
Definition: fabutils.h:148
This file contains fabgl::VGA2Controller definition.
This file contains some utility classes and functions.
Definition: canvas.cpp:31
NativePixelFormat
This enum defines the display controller native pixel format.
Represents a rectangle.
Definition: fabutils.h:191
int16_t X
uint8_t height
uint8_t width