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