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. 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 <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 
82 
83 /*************************************************************************************/
84 /* VGA2Controller definitions */
85 
86 
87 VGA2Controller * VGA2Controller::s_instance = nullptr;
88 
89 
90 
91 VGA2Controller::VGA2Controller()
92  : VGAPalettedController(VGA2_LinesCount, NativePixelFormat::PALETTE2, 8, 1, ISRHandler)
93 {
94  s_instance = this;
95  m_packedPaletteIndexOctet_to_signals = (uint64_t *) heap_caps_malloc(256 * sizeof(uint64_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
96 }
97 
98 
99 VGA2Controller::~VGA2Controller()
100 {
101  heap_caps_free((void *)m_packedPaletteIndexOctet_to_signals);
102 }
103 
104 
105 void VGA2Controller::setupDefaultPalette()
106 {
107  setPaletteItem(0, RGB888(0, 0, 0)); // 0: black
108  setPaletteItem(1, RGB888(255, 255, 255)); // 1: white
109 }
110 
111 
112 void VGA2Controller::setPaletteItem(int index, RGB888 const & color)
113 {
114  index %= 2;
115  m_palette[index] = color;
116  auto packed222 = RGB888toPackedRGB222(color);
117  for (int i = 0; i < 256; ++i) {
118  auto b = (uint8_t *) (m_packedPaletteIndexOctet_to_signals + i);
119  for (int j = 0; j < 8; ++j) {
120  auto aj = 7 - j;
121  if ((index == 0 && ((1 << aj) & i) == 0) || (index == 1 && ((1 << aj) & i) != 0)) {
122  b[j ^ 2] = m_HVSync | packed222;
123  }
124  }
125  }
126 }
127 
128 
129 void VGA2Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
130 {
131  genericSetPixelAt(pixelDesc, updateRect,
132  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
133  VGA2_SETPIXEL
134  );
135 }
136 
137 
138 // coordinates are absolute values (not relative to origin)
139 // line clipped on current absolute clipping rectangle
140 void VGA2Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
141 {
142  genericAbsDrawLine(X1, Y1, X2, Y2, color,
143  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
144  [&] (int Y, int X1, int X2, uint8_t colorIndex) { rawFillRow(Y, X1, X2, colorIndex); },
145  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
146  VGA2_SETPIXEL,
147  [&] (int X, int Y) { VGA2_INVERT_PIXEL(X, Y); }
148  );
149 }
150 
151 
152 // parameters not checked
153 void VGA2Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
154 {
155  rawFillRow(y, x1, x2, RGB888toPaletteIndex(color));
156 }
157 
158 
159 // parameters not checked
160 void VGA2Controller::rawFillRow(int y, int x1, int x2, uint8_t colorIndex)
161 {
162  uint8_t * row = (uint8_t*) m_viewPort[y];
163  // fill first pixels before full 8 bits word
164  int x = x1;
165  for (; x <= x2 && (x & 7) != 0; ++x) {
166  VGA2_SETPIXELINROW(row, x, colorIndex);
167  }
168  // fill whole 8 bits words (8 pixels)
169  if (x <= x2) {
170  int sz = (x2 & ~7) - x;
171  memset((void*)(row + x / 8), colorIndex ? 0xFF : 0x00, sz / 8);
172  x += sz;
173  }
174  // fill last unaligned pixels
175  for (; x <= x2; ++x) {
176  VGA2_SETPIXELINROW(row, x, colorIndex);
177  }
178 }
179 
180 
181 // parameters not checked
182 void VGA2Controller::rawInvertRow(int y, int x1, int x2)
183 {
184  auto row = m_viewPort[y];
185  for (int x = x1; x <= x2; ++x)
186  VGA2_INVERTPIXELINROW(row, x);
187 }
188 
189 
190 void VGA2Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
191 {
192  auto srcRow = (uint8_t*) m_viewPort[srcY];
193  auto dstRow = (uint8_t*) m_viewPort[dstY];
194  // copy first pixels before full 8 bits word
195  int x = x1;
196  for (; x <= x2 && (x & 7) != 0; ++x) {
197  VGA2_SETPIXELINROW(dstRow, x, VGA2_GETPIXELINROW(srcRow, x));
198  }
199  // copy whole 8 bits words (8 pixels)
200  auto src = (uint8_t*)(srcRow + x / 8);
201  auto dst = (uint8_t*)(dstRow + x / 8);
202  for (int right = (x2 & ~7); x < right; x += 8)
203  *dst++ = *src++;
204  // copy last unaligned pixels
205  for (x = (x2 & ~7); x <= x2; ++x) {
206  VGA2_SETPIXELINROW(dstRow, x, VGA2_GETPIXELINROW(srcRow, x));
207  }
208 }
209 
210 
211 void VGA2Controller::swapRows(int yA, int yB, int x1, int x2)
212 {
213  auto rowA = (uint8_t*) m_viewPort[yA];
214  auto rowB = (uint8_t*) m_viewPort[yB];
215  // swap first pixels before full 8 bits word
216  int x = x1;
217  for (; x <= x2 && (x & 7) != 0; ++x) {
218  uint8_t a = VGA2_GETPIXELINROW(rowA, x);
219  uint8_t b = VGA2_GETPIXELINROW(rowB, x);
220  VGA2_SETPIXELINROW(rowA, x, b);
221  VGA2_SETPIXELINROW(rowB, x, a);
222  }
223  // swap whole 8 bits words (8 pixels)
224  auto a = (uint8_t*)(rowA + x / 8);
225  auto b = (uint8_t*)(rowB + x / 8);
226  for (int right = (x2 & ~7); x < right; x += 8)
227  tswap(*a++, *b++);
228  // swap last unaligned pixels
229  for (x = (x2 & ~7); x <= x2; ++x) {
230  uint8_t a = VGA2_GETPIXELINROW(rowA, x);
231  uint8_t b = VGA2_GETPIXELINROW(rowB, x);
232  VGA2_SETPIXELINROW(rowA, x, b);
233  VGA2_SETPIXELINROW(rowB, x, a);
234  }
235 }
236 
237 
238 void VGA2Controller::drawEllipse(Size const & size, Rect & updateRect)
239 {
240  genericDrawEllipse(size, updateRect,
241  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
242  VGA2_SETPIXEL
243  );
244 }
245 
246 
247 void VGA2Controller::clear(Rect & updateRect)
248 {
249  hideSprites(updateRect);
250  uint8_t paletteIndex = RGB888toPaletteIndex(getActualBrushColor());
251  uint8_t pattern8 = paletteIndex ? 0xFF : 0x00;
252  for (int y = 0; y < m_viewPortHeight; ++y)
253  memset((uint8_t*) m_viewPort[y], pattern8, m_viewPortWidth / 8);
254 }
255 
256 
257 // scroll < 0 -> scroll UP
258 // scroll > 0 -> scroll DOWN
259 void VGA2Controller::VScroll(int scroll, Rect & updateRect)
260 {
261  genericVScroll(scroll, updateRect,
262  [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
263  [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
264  [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
265  );
266 }
267 
268 
269 void VGA2Controller::HScroll(int scroll, Rect & updateRect)
270 {
271  hideSprites(updateRect);
272  uint8_t back = RGB888toPaletteIndex(getActualBrushColor());
273  uint8_t back8 = back ? 0xFF : 0x00;
274 
275  int Y1 = paintState().scrollingRegion.Y1;
276  int Y2 = paintState().scrollingRegion.Y2;
277  int X1 = paintState().scrollingRegion.X1;
278  int X2 = paintState().scrollingRegion.X2;
279 
280  int width = X2 - X1 + 1;
281  bool HScrolllingRegionAligned = ((X1 & 7) == 0 && (width & 7) == 0); // 8 pixels aligned
282 
283  if (scroll < 0) {
284  // scroll left
285  for (int y = Y1; y <= Y2; ++y) {
286  if (HScrolllingRegionAligned) {
287  // aligned horizontal scrolling region, fast version
288  uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 8;
289  for (int s = -scroll; s > 0;) {
290  if (s < 8) {
291  // scroll left by 1..7
292  int sz = width / 8;
293  uint8_t prev = back8;
294  for (int i = sz - 1; i >= 0; --i) {
295  uint8_t lowbits = prev >> (8 - s);
296  prev = row[i];
297  row[i] = (row[i] << s) | lowbits;
298  }
299  s = 0;
300  } else {
301  // scroll left by multiplies of 8
302  auto sc = s & ~7;
303  auto sz = width & ~7;
304  memmove(row, row + sc / 8, (sz - sc) / 8);
305  rawFillRow(y, X2 - sc + 1, X2, back);
306  s -= sc;
307  }
308  }
309  } else {
310  // unaligned horizontal scrolling region, fallback to slow version
311  auto row = (uint8_t*) m_viewPort[y];
312  for (int x = X1; x <= X2 + scroll; ++x)
313  VGA2_SETPIXELINROW(row, x, VGA2_GETPIXELINROW(row, x - scroll));
314  // fill right area with brush color
315  rawFillRow(y, X2 + 1 + scroll, X2, back);
316  }
317  }
318  } else if (scroll > 0) {
319  // scroll right
320  for (int y = Y1; y <= Y2; ++y) {
321  if (HScrolllingRegionAligned) {
322  // aligned horizontal scrolling region, fast version
323  uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 8;
324  for (int s = scroll; s > 0;) {
325  if (s < 8) {
326  // scroll right by 1..7
327  int sz = width / 8;
328  uint8_t prev = back8;
329  for (int i = 0; i < sz; ++i) {
330  uint8_t highbits = prev << (8 - s);
331  prev = row[i];
332  row[i] = (row[i] >> s) | highbits;
333  }
334  s = 0;
335  } else {
336  // scroll right by multiplies of 8
337  auto sc = s & ~7;
338  auto sz = width & ~7;
339  memmove(row + sc / 8, row, (sz - sc) / 8);
340  rawFillRow(y, X1, X1 + sc - 1, back);
341  s -= sc;
342  }
343  }
344  } else {
345  // unaligned horizontal scrolling region, fallback to slow version
346  auto row = (uint8_t*) m_viewPort[y];
347  for (int x = X2 - scroll; x >= X1; --x)
348  VGA2_SETPIXELINROW(row, x + scroll, VGA2_GETPIXELINROW(row, x));
349  // fill left area with brush color
350  rawFillRow(y, X1, X1 + scroll - 1, back);
351  }
352  }
353 
354  }
355 }
356 
357 
358 void VGA2Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
359 {
360  genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
361  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
362  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
363  VGA2_SETPIXELINROW
364  );
365 }
366 
367 
368 void VGA2Controller::invertRect(Rect const & rect, Rect & updateRect)
369 {
370  genericInvertRect(rect, updateRect,
371  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
372  );
373 }
374 
375 
376 void VGA2Controller::swapFGBG(Rect const & rect, Rect & updateRect)
377 {
378  genericSwapFGBG(rect, updateRect,
379  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
380  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
381  VGA2_GETPIXELINROW,
382  VGA2_SETPIXELINROW
383  );
384 }
385 
386 
387 // Slow operation!
388 // supports overlapping of source and dest rectangles
389 void VGA2Controller::copyRect(Rect const & source, Rect & updateRect)
390 {
391  genericCopyRect(source, updateRect,
392  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
393  VGA2_GETPIXELINROW,
394  VGA2_SETPIXELINROW
395  );
396 }
397 
398 
399 // no bounds check is done!
400 void VGA2Controller::readScreen(Rect const & rect, RGB888 * destBuf)
401 {
402  for (int y = rect.Y1; y <= rect.Y2; ++y) {
403  auto row = (uint8_t*) m_viewPort[y];
404  for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf) {
405  const RGB222 v = m_palette[VGA2_GETPIXELINROW(row, x)];
406  *destBuf = RGB888(v.R * 85, v.G * 85, v.B * 85); // 85 x 3 = 255
407  }
408  }
409 }
410 
411 
412 void VGA2Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
413 {
414  genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
415  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
416  VGA2_SETPIXELINROW
417  );
418 }
419 
420 
421 void VGA2Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
422 {
423  auto foregroundColorIndex = RGB888toPaletteIndex(bitmap->foregroundColor);
424  genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
425  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
426  VGA2_GETPIXELINROW,
427  [&] (uint8_t * row, int x) { VGA2_SETPIXELINROW(row, x, foregroundColorIndex); } // rawSetPixelInRow
428  );
429 }
430 
431 
432 void VGA2Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
433 {
434  genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
435  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
436  VGA2_GETPIXELINROW,
437  [&] (uint8_t * row, int x, uint8_t src) { VGA2_SETPIXELINROW(row, x, RGB2222toPaletteIndex(src)); } // rawSetPixelInRow
438  );
439 }
440 
441 
442 void VGA2Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
443 {
444  genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
445  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
446  [&] (uint8_t * row, int x) { return VGA2_GETPIXELINROW(row, x); }, // rawGetPixelInRow
447  [&] (uint8_t * row, int x, RGBA8888 const & src) { VGA2_SETPIXELINROW(row, x, RGB8888toPaletteIndex(src)); } // rawSetPixelInRow
448  );
449 }
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() && ctrl->m_primitiveExecTask) {
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:179
Represents a 24 bit RGB color.
int16_t Y2
Definition: fabutils.h:180
int16_t Y1
Definition: fabutils.h:178
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:177
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:225
int16_t X
uint8_t height
uint8_t width