FabGL
ESP32 Display Controller and Graphics Library
vgacontroller.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 
42 #include "fabutils.h"
43 #include "vgacontroller.h"
44 #include "devdrivers/swgenerator.h"
45 
46 
47 
48 
49 #pragma GCC optimize ("O2")
50 
51 
52 namespace fabgl {
53 
54 
55 
56 
57 
58 /*************************************************************************************/
59 /* VGAController definitions */
60 
61 
62 VGAController * VGAController::s_instance = nullptr;
63 
64 
65 VGAController::VGAController()
66 {
67  s_instance = this;
68 }
69 
70 
71 void VGAController::init()
72 {
73  VGABaseController::init();
74 
75  m_doubleBufferOverDMA = true;
76 }
77 
78 
80 {
81  VGABaseController::suspendBackgroundPrimitiveExecution();
82  if (m_primitiveProcessingSuspended == 1) {
83  I2S1.int_clr.val = 0xFFFFFFFF;
84  I2S1.int_ena.out_eof = 0;
85  }
86 }
87 
88 
90 {
91  VGABaseController::resumeBackgroundPrimitiveExecution();
92  if (m_primitiveProcessingSuspended == 0) {
93  if (m_isr_handle == nullptr)
94  esp_intr_alloc(ETS_I2S1_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, VSyncInterrupt, this, &m_isr_handle);
95  I2S1.int_clr.val = 0xFFFFFFFF;
96  I2S1.int_ena.out_eof = 1;
97  }
98 }
99 
100 
101 void VGAController::allocateViewPort()
102 {
103  VGABaseController::allocateViewPort(MALLOC_CAP_DMA, m_viewPortWidth);
104 }
105 
106 
107 void VGAController::setResolution(VGATimings const& timings, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
108 {
109  VGABaseController::setResolution(timings, viewPortWidth, viewPortHeight, doubleBuffered);
110 
111  // fill view port
112  for (int i = 0; i < m_viewPortHeight; ++i)
113  fill(m_viewPort[i], 0, m_viewPortWidth, 0, 0, 0, false, false);
114 
115  // number of microseconds usable in VSynch ISR
116  m_maxVSyncISRTime = ceil(1000000.0 / m_timings.frequency * m_timings.scanCount * m_HLineSize * (m_timings.VSyncPulse + m_timings.VBackPorch + m_timings.VFrontPorch + m_viewPortRow));
117 
118  startGPIOStream();
120 }
121 
122 
123 void VGAController::onSetupDMABuffer(lldesc_t volatile * buffer, bool isStartOfVertFrontPorch, int scan, bool isVisible, int visibleRow)
124 {
125  // generate interrupt at the beginning of vertical front porch
126  if (isStartOfVertFrontPorch)
127  buffer->eof = 1;
128 }
129 
130 
131 void IRAM_ATTR VGAController::VSyncInterrupt(void * arg)
132 {
133  if (I2S1.int_st.out_eof) {
134  auto VGACtrl = (VGAController*)arg;
135  int64_t startTime = VGACtrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
136  Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
137  do {
138  Primitive prim;
139  if (VGACtrl->getPrimitiveISR(&prim) == false)
140  break;
141 
142  VGACtrl->execPrimitive(prim, updateRect, true);
143 
144  if (VGACtrl->m_primitiveProcessingSuspended)
145  break;
146 
147  } while (!VGACtrl->backgroundPrimitiveTimeoutEnabled() || (startTime + VGACtrl->m_maxVSyncISRTime > esp_timer_get_time()));
148  VGACtrl->showSprites(updateRect);
149  }
150  I2S1.int_clr.val = I2S1.int_st.val;
151 }
152 
153 
154 void IRAM_ATTR VGAController::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
155 {
156  genericSetPixelAt(pixelDesc, updateRect,
157  [&] (RGB888 const & color) { return preparePixel(color); },
158  [&] (int X, int Y, uint8_t pattern) { VGA_PIXEL(X, Y) = pattern; }
159  );
160 }
161 
162 
163 // coordinates are absolute values (not relative to origin)
164 // line clipped on current absolute clipping rectangle
165 void IRAM_ATTR VGAController::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
166 {
167  genericAbsDrawLine(X1, Y1, X2, Y2, color,
168  [&] (RGB888 const & color) { return preparePixel(color); },
169  [&] (int Y, int X1, int X2, uint8_t pattern) { rawFillRow(Y, X1, X2, pattern); },
170  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
171  [&] (int X, int Y, uint8_t pattern) { VGA_PIXEL(X, Y) = pattern; },
172  [&] (int X, int Y) { VGA_INVERT_PIXEL(X, Y); }
173  );
174 }
175 
176 
177 // parameters not checked
178 void IRAM_ATTR VGAController::rawFillRow(int y, int x1, int x2, RGB888 color)
179 {
180  rawFillRow(y, x1, x2, preparePixel(color));
181 }
182 
183 
184 // parameters not checked
185 void IRAM_ATTR VGAController::rawFillRow(int y, int x1, int x2, uint8_t pattern)
186 {
187  auto row = m_viewPort[y];
188  // fill first bytes before full 32 bits word
189  int x = x1;
190  for (; x <= x2 && (x & 3) != 0; ++x) {
191  VGA_PIXELINROW(row, x) = pattern;
192  }
193  // fill whole 32 bits words (don't care about VGA_PIXELINROW adjusted alignment)
194  if (x <= x2) {
195  int sz = (x2 & ~3) - x;
196  memset((void*)(row + x), pattern, sz);
197  x += sz;
198  }
199  // fill last unaligned bytes
200  for (; x <= x2; ++x) {
201  VGA_PIXELINROW(row, x) = pattern;
202  }
203 }
204 
205 
206 // parameters not checked
207 void IRAM_ATTR VGAController::rawInvertRow(int y, int x1, int x2)
208 {
209  auto row = m_viewPort[y];
210  for (int x = x1; x <= x2; ++x) {
211  uint8_t * px = (uint8_t*) &VGA_PIXELINROW(row, x);
212  *px = m_HVSync | ~(*px);
213  }
214 }
215 
216 
217 // swaps all pixels inside the range x1...x2 of yA and yB
218 // parameters not checked
219 void IRAM_ATTR VGAController::swapRows(int yA, int yB, int x1, int x2)
220 {
221  uint8_t * rowA = (uint8_t*) m_viewPort[yA];
222  uint8_t * rowB = (uint8_t*) m_viewPort[yB];
223  // swap first bytes before full 32 bits word
224  int x = x1;
225  for (; x <= x2 && (x & 3) != 0; ++x)
226  tswap(VGA_PIXELINROW(rowA, x), VGA_PIXELINROW(rowB, x));
227  // swap whole 32 bits words (don't care about VGA_PIXELINROW adjusted alignment)
228  uint32_t * a = (uint32_t*)(rowA + x);
229  uint32_t * b = (uint32_t*)(rowB + x);
230  for (int right = (x2 & ~3); x < right; x += 4)
231  tswap(*a++, *b++);
232  // swap last unaligned bytes
233  for (x = (x2 & ~3); x <= x2; ++x)
234  tswap(VGA_PIXELINROW(rowA, x), VGA_PIXELINROW(rowB, x));
235 }
236 
237 
238 void IRAM_ATTR VGAController::drawEllipse(Size const & size, Rect & updateRect)
239 {
240  genericDrawEllipse(size, updateRect,
241  [&] (RGB888 const & color) { return preparePixel(color); },
242  [&] (int X, int Y, uint8_t pattern) { VGA_PIXEL(X, Y) = pattern; }
243  );
244 }
245 
246 
247 void IRAM_ATTR VGAController::clear(Rect & updateRect)
248 {
249  hideSprites(updateRect);
250  uint8_t pattern = preparePixel(getActualBrushColor());
251  for (int y = 0; y < m_viewPortHeight; ++y)
252  memset((uint8_t*) m_viewPort[y], pattern, m_viewPortWidth);
253 }
254 
255 
256 // scroll < 0 -> scroll UP
257 // scroll > 0 -> scroll DOWN
258 // Speciying horizontal scrolling region slow-down scrolling!
259 void IRAM_ATTR VGAController::VScroll(int scroll, Rect & updateRect)
260 {
261  genericVScroll(scroll, updateRect,
262  [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
263  [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
264  [&] (int y, int x1, int x2, RGB888 pattern) { rawFillRow(y, x1, x2, pattern); } // rawFillRow
265  );
266 
267  if (scroll != 0) {
268  // reassign DMA pointers
269  int viewPortBuffersPerLine = 0;
270  int linePos = 1;
271  switch (m_timings.HStartingBlock) {
273  // FRONTPORCH -> SYNC -> BACKPORCH -> VISIBLEAREA
274  viewPortBuffersPerLine = (m_viewPortCol + m_viewPortWidth) < m_timings.HVisibleArea ? 3 : 2;
275  break;
276  case VGAScanStart::Sync:
277  // SYNC -> BACKPORCH -> VISIBLEAREA -> FRONTPORCH
278  viewPortBuffersPerLine = 3;
279  break;
281  // BACKPORCH -> VISIBLEAREA -> FRONTPORCH -> SYNC
282  viewPortBuffersPerLine = 3;
283  break;
285  // VISIBLEAREA -> FRONTPORCH -> SYNC -> BACKPORCH
286  viewPortBuffersPerLine = m_viewPortCol > 0 ? 3 : 2;
287  linePos = m_viewPortCol > 0 ? 1 : 0;
288  break;
289  }
290  const int Y1 = paintState().scrollingRegion.Y1;
291  const int Y2 = paintState().scrollingRegion.Y2;
292  for (int i = Y1, idx = Y1 * m_timings.scanCount; i <= Y2; ++i)
293  for (int scan = 0; scan < m_timings.scanCount; ++scan, ++idx)
294  setDMABufferView(m_viewPortRow * m_timings.scanCount + idx * viewPortBuffersPerLine + linePos, i, scan, m_viewPort, false);
295  }
296 }
297 
298 
299 // Scrolling by 1, 2, 3 and 4 pixels is optimized. Also scrolling multiples of 4 (8, 16, 24...) is optimized.
300 // Scrolling by other values requires up to three steps (scopose scrolling by 1, 2, 3 or 4): for example scrolling by 5 is scomposed to 4 and 1, scrolling
301 // by 6 is 4 + 2, etc.
302 // Horizontal scrolling region start and size (X2-X1+1) must be aligned to 32 bits, otherwise the unoptimized (very slow) version is used.
303 void IRAM_ATTR VGAController::HScroll(int scroll, Rect & updateRect)
304 {
305  hideSprites(updateRect);
306  uint8_t pattern8 = preparePixel(getActualBrushColor());
307  uint16_t pattern16 = pattern8 << 8 | pattern8;
308  uint32_t pattern32 = pattern16 << 16 | pattern16;
309 
310  int Y1 = paintState().scrollingRegion.Y1;
311  int Y2 = paintState().scrollingRegion.Y2;
312  int X1 = paintState().scrollingRegion.X1;
313  int X2 = paintState().scrollingRegion.X2;
314 
315  int width = X2 - X1 + 1;
316  int width32 = width >> 2;
317  bool HScrolllingRegionAligned = ((X1 & 3) == 0 && (width & 3) == 0);
318 
319  if (scroll < 0) {
320  // scroll left
321  for (int y = Y1; y <= Y2; ++y) {
322  if (HScrolllingRegionAligned) {
323  // aligned horizontal scrolling region, fast version
324  uint8_t * row = (uint8_t*) (m_viewPort[y] + X1);
325  for (int s = -scroll; s > 0;) {
326  if (s >= 4) {
327  // scroll left 4, 8 12, etc.. pixels moving 32 bit words
328  uint8_t * w = row;
329  int sz = (s & ~3) >> 2;
330  for (int i = 0; i < width32 - sz; ++i, w += 4)
331  *((uint32_t*)w) = *((uint32_t*)w + sz);
332  for (int i = tmax(0, width32 - sz); i < width32; ++i, w += 4)
333  *((uint32_t*)w) = pattern32;
334  s -= (s & ~3);
335  } else if ((s & 3) == 3) {
336  // scroll left 3 pixels swapping 8 bit words
337  uint8_t * b = row;
338  for (int i = 1; i < width32; ++i, b += 4) {
339  b[2] = b[1];
340  b[1] = b[4];
341  b[0] = b[7];
342  b[3] = b[6];
343  }
344  b[2] = b[1];
345  b[1] = b[0] = b[3] = pattern8;
346  s -= 3;
347  } else if (s & 2) {
348  // scroll left 2 pixels swapping 16 bit words
349  uint16_t * w = (uint16_t*) row;
350  for (int i = 1; i < width32; ++i, w += 2) {
351  w[1] = w[0];
352  w[0] = w[3];
353  }
354  w[1] = w[0];
355  w[0] = pattern16;
356  s -= 2;
357  } else if (s & 1) {
358  // scroll left 1 pixel by rotating 32 bit words
359  uint8_t * w = row;
360  for (int i = 1; i < width32; ++i, w += 4) {
361  *((uint32_t*)w) = *((uint32_t*)w) >> 8 | *((uint32_t*)w) << 24;
362  w[1] = w[6];
363  }
364  *((uint32_t*)w) = *((uint32_t*)w) >> 8 | *((uint32_t*)w) << 24;
365  w[1] = pattern8;
366  --s;
367  }
368  }
369  } else {
370  // unaligned horizontal scrolling region, fallback to slow version
371  uint8_t * row = (uint8_t*) m_viewPort[y];
372  for (int x = X1; x <= X2 + scroll; ++x)
373  VGA_PIXELINROW(row, x) = VGA_PIXELINROW(row, x - scroll);
374  // fill right area with brush color
375  for (int x = X2 + 1 + scroll; x <= X2; ++x)
376  VGA_PIXELINROW(row, x) = pattern8;
377  }
378  }
379  } else if (scroll > 0) {
380  // scroll right
381  for (int y = Y1; y <= Y2; ++y) {
382  if (HScrolllingRegionAligned) {
383  // aligned horizontal scrolling region, fast version
384  uint8_t * row = (uint8_t*) (m_viewPort[y] + X1);
385  for (int s = scroll; s > 0;) {
386  if (s >= 4) {
387  // scroll right 4, 8 12, etc.. pixels moving 32 bit words
388  int sz = (s & ~3) >> 2;
389  uint8_t * w = row + width - 4;
390  for (int i = 0; i < width32 - sz; ++i, w -= 4)
391  *((uint32_t*)w) = *((uint32_t*)w - sz);
392  for (int i = tmax(0, width32 - sz); i < width32; ++i, w -= 4)
393  *((uint32_t*)w) = pattern32;
394  s -= (s & ~3);
395  } else if ((s & 3) == 3) {
396  // scroll right 3 pixels swapping 8 bit words
397  uint8_t * b = row + width - 4;
398  for (int i = 1; i < width32; ++i, b -= 4) {
399  b[0] = b[-3];
400  b[1] = b[2];
401  b[2] = b[-1];
402  b[3] = b[-4];
403  }
404  b[1] = b[2];
405  b[0] = b[2] = b[3] = pattern8;
406  s -= 3;
407  } else if (s & 2) {
408  // scroll right 2 pixels swapping 16 bit words
409  uint16_t * w = (uint16_t*) (row + width - 4);
410  for (int i = 1; i < width32; ++i, w -= 2) {
411  w[0] = w[1];
412  w[1] = w[-2];
413  }
414  w[0] = w[1];
415  w[1] = pattern16;
416  s -= 2;
417  } else if (s & 1) {
418  // scroll right 1 pixel by rotating 32 bit words
419  uint8_t * w = row + width - 4;
420  for (int i = 1; i < width32; ++i, w -= 4) {
421  *((uint32_t*)w) = *((uint32_t*)w) << 8 | *((uint32_t*)w) >> 24;
422  w[2] = w[-3];
423  }
424  *((uint32_t*)w) = *((uint32_t*)w) << 8 | *((uint32_t*)w) >> 24;
425  w[2] = pattern8;
426  --s;
427  }
428  }
429  } else {
430  // unaligned horizontal scrolling region, fallback to slow version
431  uint8_t * row = (uint8_t*) m_viewPort[y];
432  for (int x = X2 - scroll; x >= X1; --x)
433  VGA_PIXELINROW(row, x + scroll) = VGA_PIXELINROW(row, x);
434  // fill left area with brush color
435  for (int x = X1; x < X1 + scroll; ++x)
436  VGA_PIXELINROW(row, x) = pattern8;
437  }
438  }
439 
440  }
441 }
442 
443 
444 void IRAM_ATTR VGAController::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
445 {
446  genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
447  [&] (RGB888 const & color) { return preparePixel(color); },
448  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
449  [&] (uint8_t * row, int x, uint8_t pattern) { VGA_PIXELINROW(row, x) = pattern; }
450  );
451 }
452 
453 
454 void IRAM_ATTR VGAController::invertRect(Rect const & rect, Rect & updateRect)
455 {
456  genericInvertRect(rect, updateRect,
457  [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
458  );
459 }
460 
461 
462 void IRAM_ATTR VGAController::swapFGBG(Rect const & rect, Rect & updateRect)
463 {
464  genericSwapFGBG(rect, updateRect,
465  [&] (RGB888 const & color) { return preparePixel(color); },
466  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
467  [&] (uint8_t * row, int x) { return VGA_PIXELINROW(row, x); },
468  [&] (uint8_t * row, int x, uint8_t pattern) { VGA_PIXELINROW(row, x) = pattern; }
469  );
470 }
471 
472 
473 // Slow operation!
474 // supports overlapping of source and dest rectangles
475 void IRAM_ATTR VGAController::copyRect(Rect const & source, Rect & updateRect)
476 {
477  genericCopyRect(source, updateRect,
478  [&] (int y) { return (uint8_t*) m_viewPort[y]; },
479  [&] (uint8_t * row, int x) { return VGA_PIXELINROW(row, x); },
480  [&] (uint8_t * row, int x, uint8_t pattern) { VGA_PIXELINROW(row, x) = pattern; }
481  );
482 }
483 
484 
485 // no bounds check is done!
486 void VGAController::readScreen(Rect const & rect, RGB888 * destBuf)
487 {
488  for (int y = rect.Y1; y <= rect.Y2; ++y) {
489  uint8_t * row = (uint8_t*) m_viewPort[y];
490  for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf) {
491  uint8_t rawpix = VGA_PIXELINROW(row, x);
492  *destBuf = RGB888((rawpix & 3) * 85, ((rawpix >> 2) & 3) * 85, ((rawpix >> 4) & 3) * 85);
493  }
494  }
495 }
496 
497 
498 // no bounds check is done!
499 void VGAController::readScreen(Rect const & rect, RGB222 * destBuf)
500 {
501  uint8_t * dbuf = (uint8_t*) destBuf;
502  for (int y = rect.Y1; y <= rect.Y2; ++y) {
503  uint8_t * row = (uint8_t*) m_viewPort[y];
504  for (int x = rect.X1; x <= rect.X2; ++x, ++dbuf)
505  *dbuf = VGA_PIXELINROW(row, x) & ~VGA_SYNC_MASK;
506  }
507 }
508 
509 
510 // no bounds check is done!
511 void VGAController::writeScreen(Rect const & rect, RGB222 * srcBuf)
512 {
513  uint8_t * sbuf = (uint8_t*) srcBuf;
514  for (int y = rect.Y1; y <= rect.Y2; ++y) {
515  uint8_t * row = (uint8_t*) m_viewPort[y];
516  for (int x = rect.X1; x <= rect.X2; ++x, ++sbuf)
517  VGA_PIXELINROW(row, x) = *sbuf | m_HVSync;
518  }
519 }
520 
521 
522 void IRAM_ATTR VGAController::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
523 {
524  genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
525  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
526  [&] (uint8_t * row, int x, uint8_t src) { VGA_PIXELINROW(row, x) = m_HVSync | src; } // rawSetPixelInRow
527  );
528 }
529 
530 
531 void IRAM_ATTR VGAController::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
532 {
533  auto foregroundPattern = preparePixel(bitmap->foregroundColor);
534  genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
535  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
536  [&] (uint8_t * row, int x) { return VGA_PIXELINROW(row, x); }, // rawGetPixelInRow
537  [&] (uint8_t * row, int x) { VGA_PIXELINROW(row, x) = foregroundPattern; } // rawSetPixelInRow
538  );
539 }
540 
541 
542 void IRAM_ATTR VGAController::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
543 {
544  genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
545  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
546  [&] (uint8_t * row, int x) { return VGA_PIXELINROW(row, x); }, // rawGetPixelInRow
547  [&] (uint8_t * row, int x, uint8_t src) { VGA_PIXELINROW(row, x) = m_HVSync | (src & 0x3f); } // rawSetPixelInRow
548  );
549 }
550 
551 
552 void IRAM_ATTR VGAController::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
553 {
554  genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
555  [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
556  [&] (uint8_t * row, int x) { return VGA_PIXELINROW(row, x); }, // rawGetPixelInRow
557  [&] (uint8_t * row, int x, RGBA8888 const & src) { VGA_PIXELINROW(row, x) = m_HVSync | (src.R >> 6) | (src.G >> 6 << 2) | (src.B >> 6 << 4); } // rawSetPixelInRow
558  );
559 }
560 
561 
562 
563 } // end of namespace
int16_t X2
Definition: fabutils.h:180
int16_t Y2
Definition: fabutils.h:181
int16_t Y1
Definition: fabutils.h:179
int16_t Y
This file contains fabgl::GPIOStream definition.
This file contains fabgl::VGAController definition.
int16_t X1
Definition: fabutils.h:178
Represents an image.
This file contains some utility classes and functions.
Definition: canvas.cpp:36
void suspendBackgroundPrimitiveExecution()
Suspends drawings.
Represents a rectangle.
Definition: fabutils.h:226
void resumeBackgroundPrimitiveExecution()
Resumes drawings after suspendBackgroundPrimitiveExecution().
int16_t X
void readScreen(Rect const &rect, RGB222 *destBuf)
Reads pixels inside the specified rectangle.
void writeScreen(Rect const &rect, RGB222 *srcBuf)
Writes pixels inside the specified rectangle.
Represents a 6 bit RGB color.
uint8_t width