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