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