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