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