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-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 "vga16controller.h"
42 #include "devdrivers/swgenerator.h"
43 
44 
45 
46 
47 
48 
49 
50 
51 namespace fabgl {
52 
53 
54 
55 // high nibble is pixel 0, low nibble is pixel 1
56 
57 static inline __attribute__((always_inline)) void VGA16_SETPIXELINROW(uint8_t * row, int x, int value) {
58  int brow = x >> 1;
59  row[brow] = (x & 1) ? ((row[brow] & 0xf0) | value) : ((row[brow] & 0x0f) | (value << 4));
60 }
61 
62 static inline __attribute__((always_inline)) int VGA16_GETPIXELINROW(uint8_t * row, int x) {
63  int brow = x >> 1;
64  return ((x & 1) ? (row[brow] & 0x0f) : ((row[brow] & 0xf0) >> 4));
65 }
66 
67 #define VGA16_INVERTPIXELINROW(row, x) (row)[(x) >> 1] ^= (0xf0 >> (((x) & 1) << 2))
68 
69 static inline __attribute__((always_inline)) void VGA16_SETPIXEL(int x, int y, int value) {
70  auto row = (uint8_t*) VGA16Controller::sgetScanline(y);
71  int brow = x >> 1;
72  row[brow] = (x & 1) ? ((row[brow] & 0xf0) | value) : ((row[brow] & 0x0f) | (value << 4));
73 }
74 
75 #define VGA16_GETPIXEL(x, y) VGA16_GETPIXELINROW((uint8_t*)VGA16Controller::s_viewPort[(y)], (x))
76 
77 #define VGA16_INVERT_PIXEL(x, y) VGA16_INVERTPIXELINROW((uint8_t*)VGA16Controller::s_viewPort[(y)], (x))
78 
79 
80 
81 
82 /*************************************************************************************/
83 /* VGA16Controller definitions */
84 
85 
86 VGA16Controller * VGA16Controller::s_instance = nullptr;
87 
88 
89 
90 VGA16Controller::VGA16Controller()
91  : VGAPalettedController(VGA16_LinesCount, NativePixelFormat::PALETTE16, 2, 1, ISRHandler)
92 {
93  s_instance = this;
94 }
95 
96 
97 void VGA16Controller::setupDefaultPalette()
98 {
99  for (int colorIndex = 0; colorIndex < 16; ++colorIndex) {
100  RGB888 rgb888((Color)colorIndex);
101  setPaletteItem(colorIndex, rgb888);
102  }
103 }
104 
105 
106 void VGA16Controller::setPaletteItem(int index, RGB888 const & color)
107 {
108  index %= 16;
109  m_palette[index] = color;
110  auto packed222 = RGB888toPackedRGB222(color);
111  for (int i = 0; i < 16; ++i) {
112  m_packedPaletteIndexPair_to_signals[(index << 4) | i] &= 0xFF00;
113  m_packedPaletteIndexPair_to_signals[(index << 4) | i] |= (m_HVSync | packed222);
114  m_packedPaletteIndexPair_to_signals[(i << 4) | index] &= 0x00FF;
115  m_packedPaletteIndexPair_to_signals[(i << 4) | index] |= (m_HVSync | packed222) << 8;
116  }
117 }
118 
119 
120 void VGA16Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
121 {
122  genericSetPixelAt(pixelDesc, updateRect,
123  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
124  VGA16_SETPIXEL
125  );
126 }
127 
128 
129 // coordinates are absolute values (not relative to origin)
130 // line clipped on current absolute clipping rectangle
131 void VGA16Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
132 {
133  genericAbsDrawLine(X1, Y1, X2, Y2, color,
134  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
135  [&] (int Y, int X1, int X2, uint8_t colorIndex) { rawFillRow(Y, X1, X2, colorIndex); },
136  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
137  VGA16_SETPIXEL,
138  [&] (int X, int Y) { VGA16_INVERT_PIXEL(X, Y); }
139  );
140 }
141 
142 
143 // parameters not checked
144 void VGA16Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
145 {
146  rawFillRow(y, x1, x2, RGB888toPaletteIndex(color));
147 }
148 
149 
150 // parameters not checked
151 void VGA16Controller::rawFillRow(int y, int x1, int x2, uint8_t colorIndex)
152 {
153  uint8_t * row = (uint8_t*) m_viewPort[y];
154  // fill first pixels before full 16 bits word
155  int x = x1;
156  for (; x <= x2 && (x & 3) != 0; ++x) {
157  VGA16_SETPIXELINROW(row, x, colorIndex);
158  }
159  // fill whole 16 bits words (4 pixels)
160  if (x <= x2) {
161  int sz = (x2 & ~3) - x;
162  memset((void*)(row + x / 2), colorIndex | (colorIndex << 4), sz / 2);
163  x += sz;
164  }
165  // fill last unaligned pixels
166  for (; x <= x2; ++x) {
167  VGA16_SETPIXELINROW(row, x, colorIndex);
168  }
169 }
170 
171 
172 // parameters not checked
173 void VGA16Controller::rawInvertRow(int y, int x1, int x2)
174 {
175  auto row = m_viewPort[y];
176  for (int x = x1; x <= x2; ++x)
177  VGA16_INVERTPIXELINROW(row, x);
178 }
179 
180 
181 void VGA16Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
182 {
183  auto srcRow = (uint8_t*) m_viewPort[srcY];
184  auto dstRow = (uint8_t*) m_viewPort[dstY];
185  // copy first pixels before full 16 bits word
186  int x = x1;
187  for (; x <= x2 && (x & 3) != 0; ++x) {
188  VGA16_SETPIXELINROW(dstRow, x, VGA16_GETPIXELINROW(srcRow, x));
189  }
190  // copy whole 16 bits words (4 pixels)
191  auto src = (uint16_t*)(srcRow + x / 2);
192  auto dst = (uint16_t*)(dstRow + x / 2);
193  for (int right = (x2 & ~3); x < right; x += 4)
194  *dst++ = *src++;
195  // copy last unaligned pixels
196  for (x = (x2 & ~3); x <= x2; ++x) {
197  VGA16_SETPIXELINROW(dstRow, x, VGA16_GETPIXELINROW(srcRow, x));
198  }
199 }
200 
201 
202 void VGA16Controller::swapRows(int yA, int yB, int x1, int x2)
203 {
204  auto rowA = (uint8_t*) m_viewPort[yA];
205  auto rowB = (uint8_t*) m_viewPort[yB];
206  // swap first pixels before full 16 bits word
207  int x = x1;
208  for (; x <= x2 && (x & 3) != 0; ++x) {
209  uint8_t a = VGA16_GETPIXELINROW(rowA, x);
210  uint8_t b = VGA16_GETPIXELINROW(rowB, x);
211  VGA16_SETPIXELINROW(rowA, x, b);
212  VGA16_SETPIXELINROW(rowB, x, a);
213  }
214  // swap whole 16 bits words (4 pixels)
215  auto a = (uint16_t*)(rowA + x / 2);
216  auto b = (uint16_t*)(rowB + x / 2);
217  for (int right = (x2 & ~3); x < right; x += 4)
218  tswap(*a++, *b++);
219  // swap last unaligned pixels
220  for (x = (x2 & ~3); x <= x2; ++x) {
221  uint8_t a = VGA16_GETPIXELINROW(rowA, x);
222  uint8_t b = VGA16_GETPIXELINROW(rowB, x);
223  VGA16_SETPIXELINROW(rowA, x, b);
224  VGA16_SETPIXELINROW(rowB, x, a);
225  }
226 }
227 
228 
229 void VGA16Controller::drawEllipse(Size const & size, Rect & updateRect)
230 {
231  genericDrawEllipse(size, updateRect,
232  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
233  VGA16_SETPIXEL
234  );
235 }
236 
237 
238 void VGA16Controller::clear(Rect & updateRect)
239 {
240  hideSprites(updateRect);
241  uint8_t paletteIndex = RGB888toPaletteIndex(getActualBrushColor());
242  uint8_t pattern = paletteIndex | (paletteIndex << 4);
243  for (int y = 0; y < m_viewPortHeight; ++y)
244  memset((uint8_t*) m_viewPort[y], pattern, m_viewPortWidth / 2);
245 }
246 
247 
248 // scroll < 0 -> scroll UP
249 // scroll > 0 -> scroll DOWN
250 void VGA16Controller::VScroll(int scroll, Rect & updateRect)
251 {
252  genericVScroll(scroll, updateRect,
253  [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
254  [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
255  [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
256  );
257 }
258 
259 
260 void VGA16Controller::HScroll(int scroll, Rect & updateRect)
261 {
262  hideSprites(updateRect);
263  uint8_t back4 = RGB888toPaletteIndex(getActualBrushColor());
264 
265  int Y1 = paintState().scrollingRegion.Y1;
266  int Y2 = paintState().scrollingRegion.Y2;
267  int X1 = paintState().scrollingRegion.X1;
268  int X2 = paintState().scrollingRegion.X2;
269 
270  int width = X2 - X1 + 1;
271  bool HScrolllingRegionAligned = ((X1 & 3) == 0 && (width & 3) == 0); // 4 pixels aligned
272 
273  if (scroll < 0) {
274  // scroll left
275  for (int y = Y1; y <= Y2; ++y) {
276  if (HScrolllingRegionAligned) {
277  // aligned horizontal scrolling region, fast version
278  uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 2;
279  for (int s = -scroll; s > 0;) {
280  if (s > 1) {
281  // scroll left by 2, 4, 6, ... moving bytes
282  auto sc = s & ~1;
283  auto sz = width & ~1;
284  memmove(row, row + sc / 2, (sz - sc) / 2);
285  rawFillRow(y, X2 - sc + 1, X2, back4);
286  s -= sc;
287  } else if (s & 1) {
288  // scroll left 1 pixel (uint16_t at the time = 4 pixels)
289  // nibbles 0,1,2... P is prev or background
290  // byte : 01 23 45 67 -> 12 34 56 7P
291  // word (little endian CPU) : 2301 6745 -> 3412 7P56
292  auto prev = back4;
293  auto w = (uint16_t *) (row + width / 2) - 1;
294  for (int i = 0; i < width; i += 4) {
295  const uint16_t p4 = *w; // four pixels
296  *w-- = (p4 << 4 & 0xf000) | (prev << 8 & 0x0f00) | (p4 << 4 & 0x00f0) | (p4 >> 12 & 0x000f);
297  prev = p4 >> 4 & 0x000f;
298  }
299  --s;
300  }
301  }
302  } else {
303  // unaligned horizontal scrolling region, fallback to slow version
304  auto row = (uint8_t*) m_viewPort[y];
305  for (int x = X1; x <= X2 + scroll; ++x)
306  VGA16_SETPIXELINROW(row, x, VGA16_GETPIXELINROW(row, x - scroll));
307  // fill right area with brush color
308  rawFillRow(y, X2 + 1 + scroll, X2, back4);
309  }
310  }
311  } else if (scroll > 0) {
312  // scroll right
313  for (int y = Y1; y <= Y2; ++y) {
314  if (HScrolllingRegionAligned) {
315  // aligned horizontal scrolling region, fast version
316  uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 2;
317  for (int s = scroll; s > 0;) {
318  if (s > 1) {
319  // scroll right by 2, 4, 6, ... moving bytes
320  auto sc = s & ~1;
321  auto sz = width & ~1;
322  memmove(row + sc / 2, row, (sz - sc) / 2);
323  rawFillRow(y, X1, X1 + sc - 1, back4);
324  s -= sc;
325  } else if (s & 1) {
326  // scroll right 1 pixel (uint16_t at the time = 4 pixels)
327  // nibbles 0,1,2... P is prev or background
328  // byte : 01 23 45 67 -> P0 12 34 56 7...
329  // word (little endian CPU) : 2301 6745 -> 12P0 5634 ...
330  auto prev = back4;
331  auto w = (uint16_t *) row;
332  for (int i = 0; i < width; i += 4) {
333  const uint16_t p4 = *w; // four pixels
334  *w++ = (p4 << 12 & 0xf000) | (p4 >> 4 & 0x0f00) | (prev << 4) | (p4 >> 4 & 0x000f);
335  prev = p4 >> 8 & 0x000f;
336  }
337  --s;
338  }
339  }
340  } else {
341  // unaligned horizontal scrolling region, fallback to slow version
342  auto row = (uint8_t*) m_viewPort[y];
343  for (int x = X2 - scroll; x >= X1; --x)
344  VGA16_SETPIXELINROW(row, x + scroll, VGA16_GETPIXELINROW(row, x));
345  // fill left area with brush color
346  rawFillRow(y, X1, X1 + scroll - 1, back4);
347  }
348  }
349 
350  }
351 }
352 
353 
354 void VGA16Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
355 {
356  genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
357  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
358  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
359  VGA16_SETPIXELINROW
360  );
361 }
362 
363 
364 void VGA16Controller::invertRect(Rect const & rect, Rect & updateRect)
365 {
366  genericInvertRect(rect, updateRect,
367  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
368  );
369 }
370 
371 
372 void VGA16Controller::swapFGBG(Rect const & rect, Rect & updateRect)
373 {
374  genericSwapFGBG(rect, updateRect,
375  [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
376  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
377  VGA16_GETPIXELINROW,
378  VGA16_SETPIXELINROW
379  );
380 }
381 
382 
383 // Slow operation!
384 // supports overlapping of source and dest rectangles
385 void VGA16Controller::copyRect(Rect const & source, Rect & updateRect)
386 {
387  genericCopyRect(source, updateRect,
388  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
389  VGA16_GETPIXELINROW,
390  VGA16_SETPIXELINROW
391  );
392 }
393 
394 
395 // no bounds check is done!
396 void VGA16Controller::readScreen(Rect const & rect, RGB888 * destBuf)
397 {
398  for (int y = rect.Y1; y <= rect.Y2; ++y) {
399  auto row = (uint8_t*) m_viewPort[y];
400  for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf) {
401  const RGB222 v = m_palette[VGA16_GETPIXELINROW(row, x)];
402  *destBuf = RGB888(v.R * 85, v.G * 85, v.B * 85); // 85 x 3 = 255
403  }
404  }
405 }
406 
407 
408 void VGA16Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
409 {
410  genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
411  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
412  VGA16_SETPIXELINROW
413  );
414 }
415 
416 
417 void VGA16Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
418 {
419  auto foregroundColorIndex = RGB888toPaletteIndex(bitmap->foregroundColor);
420  genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
421  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
422  VGA16_GETPIXELINROW,
423  [&] (uint8_t * row, int x) { VGA16_SETPIXELINROW(row, x, foregroundColorIndex); } // rawSetPixelInRow
424  );
425 }
426 
427 
428 void VGA16Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
429 {
430  genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
431  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
432  VGA16_GETPIXELINROW,
433  [&] (uint8_t * row, int x, uint8_t src) { VGA16_SETPIXELINROW(row, x, RGB2222toPaletteIndex(src)); } // rawSetPixelInRow
434  );
435 }
436 
437 
438 void VGA16Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
439 {
440  genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
441  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
442  [&] (uint8_t * row, int x) { return VGA16_GETPIXELINROW(row, x); }, // rawGetPixelInRow
443  [&] (uint8_t * row, int x, RGBA8888 const & src) { VGA16_SETPIXELINROW(row, x, RGB8888toPaletteIndex(src)); } // rawSetPixelInRow
444  );
445 }
446 
447 
448 #pragma GCC optimize ("O2")
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()) {
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:150
Represents a 24 bit RGB color.
This file contains fabgl::VGA16Controller definition.
int16_t Y2
Definition: fabutils.h:151
int16_t Y1
Definition: fabutils.h:149
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:148
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