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