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