FabGL
ESP32 Display Controller and Graphics Library
graphicsadapter.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. Feel free to use FabGL in free software and hardware:
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 #include "graphicsadapter.h"
28 
29 
30 
31 
32 #pragma GCC optimize ("O2")
33 
34 
35 namespace fabgl {
36 
37 
38 
39 static const RGB222 CGAPalette[16] = {
40  RGB222(0, 0, 0), // black
41  RGB222(0, 0, 2), // blue
42  RGB222(0, 2, 0), // green
43  RGB222(0, 2, 2), // cyan
44  RGB222(2, 0, 0), // red
45  RGB222(2, 0, 2), // magenta
46  RGB222(2, 1, 0), // brown
47  RGB222(2, 2, 2), // light gray
48  RGB222(1, 1, 1), // dark gray
49  RGB222(1, 1, 3), // light blue
50  RGB222(1, 3, 1), // light green
51  RGB222(1, 3, 3), // light cyan
52  RGB222(3, 1, 1), // light red
53  RGB222(3, 1, 3), // light magenta
54  RGB222(3, 3, 1), // yellow
55  RGB222(3, 3, 3), // white
56 };
57 
58 
59 
60 static const RGB222 CGAGraphics4ColorsPalette[4][4] = {
61  // low intensity PC graphics 4 colors palette
62  {
63  RGB222(0, 0, 0), // background (not used)
64  RGB222(0, 2, 0), // green
65  RGB222(2, 0, 0), // red
66  RGB222(2, 1, 0), // brown
67  },
68  // high intensity PC graphics 4 colors palette
69  {
70  RGB222(0, 0, 0), // background (not used)
71  RGB222(1, 3, 1), // light green
72  RGB222(3, 1, 1), // light red
73  RGB222(3, 3, 1), // yellow
74  },
75  // low intensity PC graphics 4 colors alternate palette
76  {
77  RGB222(0, 0, 0), // background (not used)
78  RGB222(0, 2, 2), // cyan
79  RGB222(2, 0, 2), // magenta
80  RGB222(2, 2, 2), // light gray
81  },
82  // low intensity PC graphics 4 colors alternate palette
83  {
84  RGB222(0, 0, 0), // background (not used)
85  RGB222(1, 3, 3), // light cyan
86  RGB222(3, 1, 3), // light magenta
87  RGB222(3, 3, 3), // white
88  },
89 };
90 
91 
92 
93 
94 GraphicsAdapter::GraphicsAdapter()
95  : m_VGADCtrl(false),
96  m_emulation(Emulation::None),
97  m_videoBuffer(nullptr),
98  m_rawLUT(nullptr),
99  m_cursorRow(0),
100  m_cursorCol(0),
101  m_cursorStart(0),
102  m_cursorEnd(0),
103  m_cursorVisible(false),
104  m_cursorGlyph(nullptr),
105  m_bit7blink(true),
106  m_PCGraphicsBackgroundColorIndex(0),
107  m_PCGraphicsForegroundColorIndex(15),
108  m_PCGraphicsPaletteInUse(0)
109 {
110  m_font.data = nullptr;
111  m_VGADCtrl.begin();
112 }
113 
114 
115 GraphicsAdapter::~GraphicsAdapter()
116 {
117  cleanupFont();
118  freeLUT();
119  if (m_cursorGlyph)
120  heap_caps_free(m_cursorGlyph);
121 }
122 
123 
124 void GraphicsAdapter::setEmulation(Emulation emulation)
125 {
126  if (m_emulation != emulation) {
127  m_emulation = emulation;
128 
129  m_VGADCtrl.end();
130  freeLUT();
131 
132  switch (m_emulation) {
133 
134  case Emulation::None:
135  //printf("Emulation::None\n");
136  break;
137 
138  case Emulation::PC_Text_40x25_16Colors:
139  //printf("Emulation::PC_Text_40x25_16Colors\n");
140  setFont(&FONT_8x8);
141  setCursorShape(5, 7);
142  m_VGADCtrl.setDrawScanlineCallback(drawScanline_PC_Text_40x25_16Colors, this);
143  m_VGADCtrl.setScanlinesPerCallBack(4);
144  m_VGADCtrl.setResolution(VGA_320x200_70Hz); // VGA_320x200_60HzD as alternative?
145  m_columns = m_VGADCtrl.getViewPortWidth() / m_font.width;
146  m_rows = m_VGADCtrl.getViewPortHeight() / m_font.height;
147  break;
148 
149  case Emulation::PC_Text_80x25_16Colors:
150  //printf("Emulation::PC_Text_80x25_16Colors\n");
151  setFont(&FONT_8x16);
152  setCursorShape(13, 15);
153  m_VGADCtrl.setDrawScanlineCallback(drawScanline_PC_Text_80x25_16Colors, this);
154  m_VGADCtrl.setScanlinesPerCallBack(8);
155  m_VGADCtrl.setResolution(VGA_640x400_70Hz); // VGA_640x400_60Hz as alternative?
156  m_columns = m_VGADCtrl.getViewPortWidth() / m_font.width;
157  m_rows = m_VGADCtrl.getViewPortHeight() / m_font.height;
158  break;
159 
160  case Emulation::PC_Graphics_320x200_4Colors:
161  //printf("Emulation::PC_Graphics_320x200_4Colors\n");
162  m_VGADCtrl.setDrawScanlineCallback(drawScanline_PC_Graphics_320x200_4Colors, this);
163  m_VGADCtrl.setScanlinesPerCallBack(1);
164  m_VGADCtrl.setResolution(VGA_320x200_70Hz); // VGA_320x200_60HzD as alternative?
165  break;
166 
167  case Emulation::PC_Graphics_640x200_2Colors:
168  //printf("Emulation::PC_Graphics_640x200_2Colors\n");
169  m_VGADCtrl.setDrawScanlineCallback(drawScanline_PC_Graphics_640x200_2Colors, this);
170  m_VGADCtrl.setScanlinesPerCallBack(1);
171  m_VGADCtrl.setResolution(VGA_640x200_70Hz); // VGA_640x200_60HzD as alternative?
172  break;
173 
174  case Emulation::PC_Graphics_HGC_720x348:
175  //printf("Emulation::PC_Graphics_HGC_720x348\n");
176  m_VGADCtrl.setDrawScanlineCallback(drawScanline_PC_Graphics_HGC_720x348, this);
177  m_VGADCtrl.setScanlinesPerCallBack(2);
178  m_VGADCtrl.setResolution(VGA_720x348_73Hz); // VGA_720x348_50HzD as alternative?
179  break;
180 
181  }
182 
183  if (m_emulation != Emulation::None) {
184  setupLUT();
185  m_VGADCtrl.run();
186  }
187  }
188 }
189 
190 
191 void GraphicsAdapter::freeLUT()
192 {
193  if (m_rawLUT)
194  heap_caps_free(m_rawLUT);
195  m_rawLUT = nullptr;
196 }
197 
198 
199 void GraphicsAdapter::setupLUT()
200 {
201  switch (m_emulation) {
202 
203  case Emulation::None:
204  break;
205 
206  case Emulation::PC_Text_80x25_16Colors:
207  case Emulation::PC_Text_40x25_16Colors:
208  // each LUT item contains half pixel (an index to 16 colors palette)
209  if (!m_rawLUT)
210  m_rawLUT = (uint8_t*) heap_caps_malloc(16, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
211  for (int i = 0; i < 16; ++i)
212  m_rawLUT[i] = m_VGADCtrl.createRawPixel(CGAPalette[i]);
213  break;
214 
215  case Emulation::PC_Graphics_320x200_4Colors:
216  // each LUT item contains four pixels (decodes as four raw bytes)
217  if (!m_rawLUT)
218  m_rawLUT = (uint8_t*) heap_caps_malloc(256 * 4, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
219  for (int i = 0; i < 256; ++i) {
220  for (int j = 0; j < 4; ++j) {
221  int pixel = (i >> (6 - j * 2)) & 0b11;
222  uint8_t rawPixel = m_VGADCtrl.createRawPixel(pixel == 0 ? CGAPalette[m_PCGraphicsBackgroundColorIndex] : CGAGraphics4ColorsPalette[m_PCGraphicsPaletteInUse][pixel]);
223  m_rawLUT[(i * 4) + (j ^ 2)] = rawPixel;
224  }
225  }
226  break;
227 
228  case Emulation::PC_Graphics_640x200_2Colors:
229  // each LUT item contains eight pixels (decodes as eight raw bytes)
230  if (!m_rawLUT)
231  m_rawLUT = (uint8_t*) heap_caps_malloc(256 * 8, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
232  for (int i = 0; i < 256; ++i) {
233  for (int j = 0; j < 8; ++j) {
234  bool pixel = (i >> (7 - j)) & 1;
235  uint8_t rawPixel = m_VGADCtrl.createRawPixel(pixel ? CGAPalette[m_PCGraphicsForegroundColorIndex] : RGB222(0, 0, 0));
236  m_rawLUT[(i * 8) + (j ^ 2)] = rawPixel;
237  }
238  }
239  break;
240 
241  case Emulation::PC_Graphics_HGC_720x348:
242  // each LUT item contains eight pixels (decodes as eight raw bytes)
243  if (!m_rawLUT)
244  m_rawLUT = (uint8_t*) heap_caps_malloc(256 * 8, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
245  for (int i = 0; i < 256; ++i) {
246  for (int j = 0; j < 8; ++j) {
247  bool pixel = (i >> (7 - j)) & 1;
248  uint8_t rawPixel = m_VGADCtrl.createRawPixel(RGB222(pixel * 3, pixel * 3, pixel * 3));
249  m_rawLUT[(i * 8) + (j ^ 2)] = rawPixel;
250  }
251  }
252  break;
253 
254  }
255 }
256 
257 
258 void GraphicsAdapter::setPCGraphicsBackgroundColorIndex(int colorIndex)
259 {
260  m_PCGraphicsBackgroundColorIndex = colorIndex;
261  setupLUT();
262 }
263 
264 
265 void GraphicsAdapter::setPCGraphicsForegroundColorIndex(int colorIndex)
266 {
267  m_PCGraphicsForegroundColorIndex = colorIndex;
268  setupLUT();
269 }
270 
271 
272 void GraphicsAdapter::setPCGraphicsPaletteInUse(int paletteIndex)
273 {
274  m_PCGraphicsPaletteInUse = paletteIndex;
275  setupLUT();
276 }
277 
278 
279 void GraphicsAdapter::setVideoBuffer(void const * videoBuffer)
280 {
281  m_videoBuffer = (uint8_t*) videoBuffer;
282 }
283 
284 
285 void GraphicsAdapter::cleanupFont()
286 {
287  if (m_font.data) {
288  heap_caps_free((void*)m_font.data);
289  m_font.data = nullptr;
290  }
291 }
292 
293 
294 void GraphicsAdapter::setFont(FontInfo const * font)
295 {
296  cleanupFont();
297  if (font) {
298  m_font = *font;
299  // copy font data into internal RAM
300  auto size = 256 * ((m_font.width + 7) / 8) * m_font.height;
301  m_font.data = (uint8_t const *) heap_caps_malloc(size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
302  memcpy((void*)m_font.data, font->data, size);
303  }
304 }
305 
306 
307 void GraphicsAdapter::setCursorShape(int start, int end)
308 {
309  if (start != m_cursorStart || end != m_cursorEnd) {
310  m_cursorStart = start;
311  m_cursorEnd = end;
312 
313  // readapt start->end to the actual font height to make sure the cursor is always visible
314  if (start <= end && end >= m_font.height) {
315  int h = end - start;
316  end = m_font.height - 1;
317  start = end - h;
318  }
319 
320  int charWidthInBytes = (m_font.width + 7) / 8;
321  if (!m_cursorGlyph)
322  m_cursorGlyph = (uint8_t*) heap_caps_malloc(charWidthInBytes * m_font.height, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
323  memset(m_cursorGlyph, 0, charWidthInBytes * m_font.height);
324  if (end >= start && start >= 0 && start < m_font.height && end < m_font.height)
325  memset(m_cursorGlyph + (start * charWidthInBytes), 0xff, (end - start + 1) * charWidthInBytes);
326  }
327 }
328 
329 
330 void GraphicsAdapter::setCursorPos(int row, int column)
331 {
332  m_cursorRow = row;
333  m_cursorCol = column;
334 }
335 
336 
337 void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Text_40x25_16Colors(void * arg, uint8_t * dest, int scanLine)
338 {
339  auto ga = (GraphicsAdapter*) arg;
340 
341  constexpr int CHARWIDTH = 8;
342  constexpr int CHARHEIGHT = 8;
343  constexpr int CHARWIDTHINBYTES = (CHARWIDTH + 7) / 8;
344  constexpr int CHARSIZEINBYTES = CHARWIDTHINBYTES * CHARHEIGHT;
345  constexpr int COLUMNS = 40;
346  constexpr int SCREENWIDTH = 320;
347  constexpr int LINES = 8;
348 
349  if (scanLine == 0)
350  ++ga->m_frameCounter;
351 
352  int charScanline = scanLine & (CHARHEIGHT - 1);
353  int textRow = scanLine / CHARHEIGHT;
354 
355  uint8_t const * fontData = ga->m_font.data + (charScanline * CHARWIDTHINBYTES);
356 
357  uint8_t * rawLUT = ga->m_rawLUT;
358 
359  uint8_t const * curItem = ga->m_videoBuffer + (textRow * COLUMNS * 2);
360 
361  bool showCursor = ga->m_cursorVisible && ga->m_cursorRow == textRow && ((ga->m_frameCounter & 0x1f) < 0xf);
362  int cursorCol = ga->m_cursorCol;
363 
364  bool bit7blink = ga->m_bit7blink;
365  bool blinktime = bit7blink && !((ga->m_frameCounter & 0x3f) < 0x1f);
366 
367  for (int textCol = 0; textCol < COLUMNS; ++textCol) {
368 
369  int charIdx = *curItem++;
370  int charAttr = *curItem++;
371 
372  bool blink = false;
373  if (bit7blink) {
374  blink = blinktime && (charAttr & 0x80);
375  charAttr &= 0x7f;
376  }
377 
378  uint8_t bg = rawLUT[charAttr >> 4];
379  uint8_t fg = blink ? bg : rawLUT[charAttr & 0xf];
380 
381  const uint8_t colors[2] = { bg, fg };
382 
383  uint8_t const * charBitmapPtr = fontData + charIdx * CHARSIZEINBYTES;
384 
385  auto destptr = dest;
386 
387  if (showCursor && textCol == cursorCol) {
388 
389  uint8_t const * cursorBitmapPtr = ga->m_cursorGlyph + (charScanline * CHARWIDTHINBYTES);
390 
391  for (int charRow = 0; charRow < LINES / 2; ++charRow) {
392 
393  uint32_t charBitmap = *charBitmapPtr | *cursorBitmapPtr;
394 
395  *(destptr + 0) = colors[(bool)(charBitmap & 0x20)];
396  *(destptr + 1) = colors[(bool)(charBitmap & 0x10)];
397  *(destptr + 2) = colors[(bool)(charBitmap & 0x80)];
398  *(destptr + 3) = colors[(bool)(charBitmap & 0x40)];
399  *(destptr + 4) = colors[(bool)(charBitmap & 0x02)];
400  *(destptr + 5) = colors[(bool)(charBitmap & 0x01)];
401  *(destptr + 6) = colors[(bool)(charBitmap & 0x08)];
402  *(destptr + 7) = colors[(bool)(charBitmap & 0x04)];
403 
404  destptr += SCREENWIDTH;
405  charBitmapPtr += CHARWIDTHINBYTES;
406  cursorBitmapPtr += CHARWIDTHINBYTES;
407  }
408 
409  } else {
410 
411  for (int charRow = 0; charRow < LINES / 2; ++charRow) {
412 
413  uint32_t charBitmap = *charBitmapPtr;
414 
415  *(destptr + 0) = colors[(bool)(charBitmap & 0x20)];
416  *(destptr + 1) = colors[(bool)(charBitmap & 0x10)];
417  *(destptr + 2) = colors[(bool)(charBitmap & 0x80)];
418  *(destptr + 3) = colors[(bool)(charBitmap & 0x40)];
419  *(destptr + 4) = colors[(bool)(charBitmap & 0x02)];
420  *(destptr + 5) = colors[(bool)(charBitmap & 0x01)];
421  *(destptr + 6) = colors[(bool)(charBitmap & 0x08)];
422  *(destptr + 7) = colors[(bool)(charBitmap & 0x04)];
423 
424  destptr += SCREENWIDTH;
425  charBitmapPtr += CHARWIDTHINBYTES;
426  }
427 
428  }
429 
430  dest += 8;
431 
432  }
433 }
434 
435 
436 void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Text_80x25_16Colors(void * arg, uint8_t * dest, int scanLine)
437 {
438  auto ga = (GraphicsAdapter*) arg;
439 
440  constexpr int CHARWIDTH = 8;
441  constexpr int CHARHEIGHT = 16;
442  constexpr int CHARWIDTHINBYTES = (CHARWIDTH + 7) / 8;
443  constexpr int CHARSIZEINBYTES = CHARWIDTHINBYTES * CHARHEIGHT;
444  constexpr int COLUMNS = 80;
445  constexpr int SCREENWIDTH = 640;
446  constexpr int LINES = 16;
447 
448  if (scanLine == 0)
449  ++ga->m_frameCounter;
450 
451  int charScanline = scanLine & (CHARHEIGHT - 1);
452  int textRow = scanLine / CHARHEIGHT;
453 
454  uint8_t const * fontData = ga->m_font.data + (charScanline * CHARWIDTHINBYTES);
455 
456  uint8_t * rawLUT = ga->m_rawLUT;
457 
458  uint8_t const * curItem = ga->m_videoBuffer + (textRow * COLUMNS * 2);
459 
460  bool showCursor = ga->m_cursorVisible && ga->m_cursorRow == textRow && ((ga->m_frameCounter & 0x1f) < 0xf);
461  int cursorCol = ga->m_cursorCol;
462 
463  bool bit7blink = ga->m_bit7blink;
464  bool blinktime = bit7blink && !((ga->m_frameCounter & 0x3f) < 0x1f);
465 
466  for (int textCol = 0; textCol < COLUMNS; ++textCol) {
467 
468  int charIdx = *curItem++;
469  int charAttr = *curItem++;
470 
471  bool blink = false;
472  if (bit7blink) {
473  blink = blinktime && (charAttr & 0x80);
474  charAttr &= 0x7f;
475  }
476 
477  uint8_t bg = rawLUT[charAttr >> 4];
478  uint8_t fg = blink ? bg : rawLUT[charAttr & 0xf];
479 
480  const uint8_t colors[2] = { bg, fg };
481 
482  uint8_t const * charBitmapPtr = fontData + charIdx * CHARSIZEINBYTES;
483 
484  auto destptr = dest;
485 
486  if (showCursor && textCol == cursorCol) {
487 
488  uint8_t const * cursorBitmapPtr = ga->m_cursorGlyph + (charScanline * CHARWIDTHINBYTES);
489 
490  for (int charRow = 0; charRow < LINES / 2; ++charRow) {
491 
492  uint32_t charBitmap = *charBitmapPtr | *cursorBitmapPtr;
493 
494  *(destptr + 0) = colors[(bool)(charBitmap & 0x20)];
495  *(destptr + 1) = colors[(bool)(charBitmap & 0x10)];
496  *(destptr + 2) = colors[(bool)(charBitmap & 0x80)];
497  *(destptr + 3) = colors[(bool)(charBitmap & 0x40)];
498  *(destptr + 4) = colors[(bool)(charBitmap & 0x02)];
499  *(destptr + 5) = colors[(bool)(charBitmap & 0x01)];
500  *(destptr + 6) = colors[(bool)(charBitmap & 0x08)];
501  *(destptr + 7) = colors[(bool)(charBitmap & 0x04)];
502 
503  destptr += SCREENWIDTH;
504  charBitmapPtr += CHARWIDTHINBYTES;
505  cursorBitmapPtr += CHARWIDTHINBYTES;
506  }
507 
508  } else {
509 
510  for (int charRow = 0; charRow < LINES / 2; ++charRow) {
511 
512  uint32_t charBitmap = *charBitmapPtr;
513 
514  *(destptr + 0) = colors[(bool)(charBitmap & 0x20)];
515  *(destptr + 1) = colors[(bool)(charBitmap & 0x10)];
516  *(destptr + 2) = colors[(bool)(charBitmap & 0x80)];
517  *(destptr + 3) = colors[(bool)(charBitmap & 0x40)];
518  *(destptr + 4) = colors[(bool)(charBitmap & 0x02)];
519  *(destptr + 5) = colors[(bool)(charBitmap & 0x01)];
520  *(destptr + 6) = colors[(bool)(charBitmap & 0x08)];
521  *(destptr + 7) = colors[(bool)(charBitmap & 0x04)];
522 
523  destptr += SCREENWIDTH;
524  charBitmapPtr += CHARWIDTHINBYTES;
525  }
526 
527  }
528 
529  dest += 8;
530 
531  }
532 }
533 
534 
535 // offset 0x0000 for even scan lines (bit 0 = 0), lines 0, 2, 4...
536 // offset 0x2000 for odd scan lines (bit 0 = 1), lines 1, 3, 5...
537 void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Graphics_320x200_4Colors(void * arg, uint8_t * dest, int scanLine)
538 {
539  constexpr int WIDTH = 320;
540  constexpr int PIXELSPERBYTE = 4;
541  constexpr int WIDTHINBYTES = WIDTH / PIXELSPERBYTE;
542 
543  auto ga = (GraphicsAdapter*) arg;
544 
545  auto src = ga->m_videoBuffer + ((scanLine & 1) << 13) + WIDTHINBYTES * (scanLine >> 1);
546 
547  auto dest32 = (uint32_t*) dest;
548 
549  auto LUT32 = (uint32_t*) ga->m_rawLUT;
550 
551  for (int col = 0; col < WIDTH; col += PIXELSPERBYTE) {
552  *dest32++ = LUT32[*src++];
553  }
554 }
555 
556 
557 // offset 0x0000 for even scan lines (bit 0 = 0), lines 0, 2, 4...
558 // offset 0x2000 for odd scan lines (bit 0 = 1), lines 1, 3, 5...
559 void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Graphics_640x200_2Colors(void * arg, uint8_t * dest, int scanLine)
560 {
561  constexpr int WIDTH = 640;
562  constexpr int PIXELSPERBYTE = 8;
563  constexpr int WIDTHINBYTES = WIDTH / PIXELSPERBYTE;
564 
565  auto ga = (GraphicsAdapter*) arg;
566 
567  auto src = ga->m_videoBuffer + ((scanLine & 1) << 13) + WIDTHINBYTES * (scanLine >> 1);
568 
569  auto dest64 = (uint64_t*) dest;
570 
571  auto LUT64 = (uint64_t*) ga->m_rawLUT;
572 
573  for (int col = 0; col < WIDTH; col += PIXELSPERBYTE) {
574  *dest64++ = LUT64[*src++];
575  }
576 }
577 
578 
579 // offset 0x0000 for lines 0, 4, 8, 12...
580 // offset 0x2000 for lines 1, 5, 9, 13...
581 // offset 0x4000 for lines 2, 6, 10, 14...
582 // offset 0x6000 for lines 3, 7, 11, 15...
583 void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Graphics_HGC_720x348(void * arg, uint8_t * dest, int scanLine)
584 {
585  constexpr int WIDTH = 720;
586  constexpr int PIXELSPERBYTE = 8;
587  constexpr int WIDTHINBYTES = WIDTH / PIXELSPERBYTE;
588 
589  auto ga = (GraphicsAdapter*) arg;
590 
591  auto src = ga->m_videoBuffer + ((scanLine & 0b11) << 13) + WIDTHINBYTES * (scanLine >> 2);
592 
593  auto dest64 = (uint64_t*) dest;
594 
595  auto LUT64 = (uint64_t*) ga->m_rawLUT;
596 
597  for (int col = 0; col < WIDTH; col += PIXELSPERBYTE)
598  *dest64++ = LUT64[*src++];
599 
600  ++scanLine;
601  src = ga->m_videoBuffer + ((scanLine & 0b11) << 13) + WIDTHINBYTES * (scanLine >> 2);
602 
603  for (int col = 0; col < WIDTH; col += PIXELSPERBYTE)
604  *dest64++ = LUT64[*src++];
605 }
606 
607 
608 
609 
610 
611 }; // fabgl namespace
#define VGA_720x348_73Hz
Definition: fabglconf.h:264
#define VGA_640x200_70Hz
Definition: fabglconf.h:207
#define VGA_640x400_70Hz
Definition: fabglconf.h:234
Definition: canvas.cpp:36
This file contains fabgl::GraphicsAdapter definition.
int16_t height
Definition: fabutils.h:210
Represents a 6 bit RGB color.
#define VGA_320x200_70Hz
Definition: fabglconf.h:174