FabGL
ESP32 Display Controller and Graphics Library
vga2controller.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 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 "vga2controller.h"
46
47
48
49#pragma GCC optimize ("O2")
50
51
52
53
54namespace fabgl {
55
56
57
58static inline __attribute__((always_inline)) void VGA2_SETPIXELINROW(uint8_t * row, int x, int value) {
59 int brow = x >> 3;
60 row[brow] ^= (-value ^ row[brow]) & (0x80 >> (x & 7));
61}
62
63static inline __attribute__((always_inline)) int VGA2_GETPIXELINROW(uint8_t * row, int x) {
64 int brow = x >> 3;
65 return (row[brow] & (0x80 >> (x & 7))) != 0;
66}
67
68#define VGA2_INVERTPIXELINROW(row, x) (row)[(x) >> 3] ^= (0x80 >> ((x) & 7))
69
70static inline __attribute__((always_inline)) void VGA2_SETPIXEL(int x, int y, int value) {
71 auto row = (uint8_t*) VGA2Controller::sgetScanline(y);
72 int brow = x >> 3;
73 row[brow] ^= (-value ^ row[brow]) & (0x80 >> (x & 7));
74}
75
76#define VGA2_GETPIXEL(x, y) VGA2_GETPIXELINROW((uint8_t*)VGA2Controller::s_viewPort[(y)], (x))
77
78#define VGA2_INVERT_PIXEL(x, y) VGA2_INVERTPIXELINROW((uint8_t*)VGA2Controller::s_viewPort[(y)], (x))
79
80
81#define VGA2_COLUMNSQUANTUM 16
82
83
84
85/*************************************************************************************/
86/* VGA2Controller definitions */
87
88
89VGA2Controller * VGA2Controller::s_instance = nullptr;
90
91
92
93VGA2Controller::VGA2Controller()
94 : VGAPalettedController(VGA2_LinesCount, VGA2_COLUMNSQUANTUM, NativePixelFormat::PALETTE2, 8, 1, ISRHandler)
95{
96 s_instance = this;
97 m_packedPaletteIndexOctet_to_signals = (uint64_t *) heap_caps_malloc(256 * sizeof(uint64_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
98}
99
100
101VGA2Controller::~VGA2Controller()
102{
103 heap_caps_free((void *)m_packedPaletteIndexOctet_to_signals);
104}
105
106
107void VGA2Controller::setupDefaultPalette()
108{
109 setPaletteItem(0, RGB888(0, 0, 0)); // 0: black
110 setPaletteItem(1, RGB888(255, 255, 255)); // 1: white
111}
112
113
114void VGA2Controller::setPaletteItem(int index, RGB888 const & color)
115{
116 index %= 2;
117 m_palette[index] = color;
118 auto packed222 = RGB888toPackedRGB222(color);
119 for (int i = 0; i < 256; ++i) {
120 auto b = (uint8_t *) (m_packedPaletteIndexOctet_to_signals + i);
121 for (int j = 0; j < 8; ++j) {
122 auto aj = 7 - j;
123 if ((index == 0 && ((1 << aj) & i) == 0) || (index == 1 && ((1 << aj) & i) != 0)) {
124 b[j ^ 2] = m_HVSync | packed222;
125 }
126 }
127 }
128}
129
130
131void VGA2Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
132{
133 genericSetPixelAt(pixelDesc, updateRect,
134 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
135 VGA2_SETPIXEL
136 );
137}
138
139
140// coordinates are absolute values (not relative to origin)
141// line clipped on current absolute clipping rectangle
142void VGA2Controller::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 VGA2_SETPIXEL,
149 [&] (int X, int Y) { VGA2_INVERT_PIXEL(X, Y); }
150 );
151}
152
153
154// parameters not checked
155void VGA2Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
156{
157 rawFillRow(y, x1, x2, RGB888toPaletteIndex(color));
158}
159
160
161// parameters not checked
162void VGA2Controller::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 & 7) != 0; ++x) {
168 VGA2_SETPIXELINROW(row, x, colorIndex);
169 }
170 // fill whole 8 bits words (8 pixels)
171 if (x <= x2) {
172 int sz = (x2 & ~7) - x;
173 memset((void*)(row + x / 8), colorIndex ? 0xFF : 0x00, sz / 8);
174 x += sz;
175 }
176 // fill last unaligned pixels
177 for (; x <= x2; ++x) {
178 VGA2_SETPIXELINROW(row, x, colorIndex);
179 }
180}
181
182
183// parameters not checked
184void VGA2Controller::rawInvertRow(int y, int x1, int x2)
185{
186 auto row = m_viewPort[y];
187 for (int x = x1; x <= x2; ++x)
188 VGA2_INVERTPIXELINROW(row, x);
189}
190
191
192void VGA2Controller::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 & 7) != 0; ++x) {
199 VGA2_SETPIXELINROW(dstRow, x, VGA2_GETPIXELINROW(srcRow, x));
200 }
201 // copy whole 8 bits words (8 pixels)
202 auto src = (uint8_t*)(srcRow + x / 8);
203 auto dst = (uint8_t*)(dstRow + x / 8);
204 for (int right = (x2 & ~7); x < right; x += 8)
205 *dst++ = *src++;
206 // copy last unaligned pixels
207 for (x = (x2 & ~7); x <= x2; ++x) {
208 VGA2_SETPIXELINROW(dstRow, x, VGA2_GETPIXELINROW(srcRow, x));
209 }
210}
211
212
213void VGA2Controller::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 & 7) != 0; ++x) {
220 uint8_t a = VGA2_GETPIXELINROW(rowA, x);
221 uint8_t b = VGA2_GETPIXELINROW(rowB, x);
222 VGA2_SETPIXELINROW(rowA, x, b);
223 VGA2_SETPIXELINROW(rowB, x, a);
224 }
225 // swap whole 8 bits words (8 pixels)
226 auto a = (uint8_t*)(rowA + x / 8);
227 auto b = (uint8_t*)(rowB + x / 8);
228 for (int right = (x2 & ~7); x < right; x += 8)
229 tswap(*a++, *b++);
230 // swap last unaligned pixels
231 for (x = (x2 & ~7); x <= x2; ++x) {
232 uint8_t a = VGA2_GETPIXELINROW(rowA, x);
233 uint8_t b = VGA2_GETPIXELINROW(rowB, x);
234 VGA2_SETPIXELINROW(rowA, x, b);
235 VGA2_SETPIXELINROW(rowB, x, a);
236 }
237}
238
239
240void VGA2Controller::drawEllipse(Size const & size, Rect & updateRect)
241{
242 genericDrawEllipse(size, updateRect,
243 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
244 VGA2_SETPIXEL
245 );
246}
247
248
249void VGA2Controller::clear(Rect & updateRect)
250{
251 hideSprites(updateRect);
252 uint8_t paletteIndex = RGB888toPaletteIndex(getActualBrushColor());
253 uint8_t pattern8 = paletteIndex ? 0xFF : 0x00;
254 for (int y = 0; y < m_viewPortHeight; ++y)
255 memset((uint8_t*) m_viewPort[y], pattern8, m_viewPortWidth / 8);
256}
257
258
259// scroll < 0 -> scroll UP
260// scroll > 0 -> scroll DOWN
261void VGA2Controller::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
271void VGA2Controller::HScroll(int scroll, Rect & updateRect)
272{
273 hideSprites(updateRect);
274 uint8_t back = RGB888toPaletteIndex(getActualBrushColor());
275 uint8_t back8 = back ? 0xFF : 0x00;
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 & 7) == 0 && (width & 7) == 0); // 8 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 / 8;
291 for (int s = -scroll; s > 0;) {
292 if (s < 8) {
293 // scroll left by 1..7
294 int sz = width / 8;
295 uint8_t prev = back8;
296 for (int i = sz - 1; i >= 0; --i) {
297 uint8_t lowbits = prev >> (8 - s);
298 prev = row[i];
299 row[i] = (row[i] << s) | lowbits;
300 }
301 s = 0;
302 } else {
303 // scroll left by multiplies of 8
304 auto sc = s & ~7;
305 auto sz = width & ~7;
306 memmove(row, row + sc / 8, (sz - sc) / 8);
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 VGA2_SETPIXELINROW(row, x, VGA2_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 / 8;
326 for (int s = scroll; s > 0;) {
327 if (s < 8) {
328 // scroll right by 1..7
329 int sz = width / 8;
330 uint8_t prev = back8;
331 for (int i = 0; i < sz; ++i) {
332 uint8_t highbits = prev << (8 - s);
333 prev = row[i];
334 row[i] = (row[i] >> s) | highbits;
335 }
336 s = 0;
337 } else {
338 // scroll right by multiplies of 8
339 auto sc = s & ~7;
340 auto sz = width & ~7;
341 memmove(row + sc / 8, row, (sz - sc) / 8);
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 VGA2_SETPIXELINROW(row, x + scroll, VGA2_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
360void VGA2Controller::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 VGA2_SETPIXELINROW
366 );
367}
368
369
370void VGA2Controller::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
378void VGA2Controller::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 VGA2_GETPIXELINROW,
384 VGA2_SETPIXELINROW
385 );
386}
387
388
389// Slow operation!
390// supports overlapping of source and dest rectangles
391void VGA2Controller::copyRect(Rect const & source, Rect & updateRect)
392{
393 genericCopyRect(source, updateRect,
394 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
395 VGA2_GETPIXELINROW,
396 VGA2_SETPIXELINROW
397 );
398}
399
400
401// no bounds check is done!
402void VGA2Controller::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[VGA2_GETPIXELINROW(row, x)];
408 *destBuf = RGB888(v.R * 85, v.G * 85, v.B * 85); // 85 x 3 = 255
409 }
410 }
411}
412
413
414void VGA2Controller::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 VGA2_SETPIXELINROW
419 );
420}
421
422
423void VGA2Controller::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 VGA2_GETPIXELINROW,
429 [&] (uint8_t * row, int x) { VGA2_SETPIXELINROW(row, x, foregroundColorIndex); } // rawSetPixelInRow
430 );
431}
432
433
434void VGA2Controller::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 VGA2_GETPIXELINROW,
439 [&] (uint8_t * row, int x, uint8_t src) { VGA2_SETPIXELINROW(row, x, RGB2222toPaletteIndex(src)); } // rawSetPixelInRow
440 );
441}
442
443
444void VGA2Controller::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 VGA2_GETPIXELINROW(row, x); }, // rawGetPixelInRow
449 [&] (uint8_t * row, int x, RGBA8888 const & src) { VGA2_SETPIXELINROW(row, x, RGB8888toPaletteIndex(src)); } // rawSetPixelInRow
450 );
451}
452
453
454void IRAM_ATTR VGA2Controller::ISRHandler(void * arg)
455{
456 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
457 auto s1 = getCycleCount();
458 #endif
459
460 auto ctrl = (VGA2Controller *) arg;
461
462 if (I2S1.int_st.out_eof) {
463
464 auto const desc = (lldesc_t*) I2S1.out_eof_des_addr;
465
466 if (desc == s_frameResetDesc)
467 s_scanLine = 0;
468
469 auto const width = ctrl->m_viewPortWidth;
470 auto const height = ctrl->m_viewPortHeight;
471 auto const packedPaletteIndexOctet_to_signals = (uint64_t const *) ctrl->m_packedPaletteIndexOctet_to_signals;
472 auto const lines = ctrl->m_lines;
473
474 int scanLine = (s_scanLine + VGA2_LinesCount / 2) % height;
475
476 auto lineIndex = scanLine & (VGA2_LinesCount - 1);
477
478 for (int i = 0; i < VGA2_LinesCount / 2; ++i) {
479
480 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
481 auto dest = (uint64_t*) lines[lineIndex];
482
483 // optimization warn: horizontal resolution must be a multiple of 16!
484 for (int col = 0; col < width; col += 16) {
485
486 auto src1 = *(src + 0);
487 auto src2 = *(src + 1);
488
489 PSRAM_HACK;
490
491 auto v1 = packedPaletteIndexOctet_to_signals[src1];
492 auto v2 = packedPaletteIndexOctet_to_signals[src2];
493
494 *(dest + 0) = v1;
495 *(dest + 1) = v2;
496
497 dest += 2;
498 src += 2;
499
500 }
501
502 ++lineIndex;
503 ++scanLine;
504 }
505
506 s_scanLine += VGA2_LinesCount / 2;
507
508 if (scanLine >= height && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
509 // vertical sync, unlock primitive execution task
510 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
511 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
512 }
513
514 }
515
516 #if FABGLIB_VGAXCONTROLLER_PERFORMANCE_CHECK
517 s_vgapalctrlcycles += getCycleCount() - s1;
518 #endif
519
520 I2S1.int_clr.val = I2S1.int_st.val;
521}
522
523
524
525
526} // end of namespace
uint8_t width
int16_t X
uint8_t swapFGBG
int16_t Y
uint8_t height
int16_t X1
Definition: fabutils.h:0
int16_t Y2
Definition: fabutils.h:3
int16_t X2
Definition: fabutils.h:2
int16_t Y1
Definition: fabutils.h:1
This file contains some utility classes and functions.
NativePixelFormat
This enum defines the display controller native pixel format.
Represents a 24 bit RGB color.
Represents a rectangle.
Definition: fabutils.h:248
This file contains fabgl::GPIOStream definition.
This file contains fabgl::VGA2Controller definition.