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