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-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#include "graphicsadapter.h"
28
29
30
31
32#pragma GCC optimize ("O2")
33
34
35namespace fabgl {
36
37
38
39static 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
60static 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
92void 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_720x350_70Hz, -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
140GraphicsAdapter::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
162GraphicsAdapter::~GraphicsAdapter()
163{
164 enableVideo(false);
165 cleanupFont();
166 freeLUT();
167 if (m_cursorGlyph)
168 heap_caps_free(m_cursorGlyph);
169}
170
171
172bool 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
190void 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
207void GraphicsAdapter::freeLUT()
208{
209 if (m_rawLUT)
210 heap_caps_free(m_rawLUT);
211 m_rawLUT = nullptr;
212}
213
214
215void 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
274void GraphicsAdapter::setPCGraphicsBackgroundColorIndex(int colorIndex)
275{
276 m_PCGraphicsBackgroundColorIndex = colorIndex;
277 setupLUT();
278}
279
280
281void GraphicsAdapter::setPCGraphicsForegroundColorIndex(int colorIndex)
282{
283 m_PCGraphicsForegroundColorIndex = colorIndex;
284 setupLUT();
285}
286
287
288void GraphicsAdapter::setPCGraphicsPaletteInUse(int paletteIndex)
289{
290 m_PCGraphicsPaletteInUse = paletteIndex;
291 setupLUT();
292}
293
294
295void GraphicsAdapter::setVideoBuffer(void const * videoBuffer)
296{
297 m_videoBuffer = (uint8_t*) videoBuffer;
298}
299
300
301void 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
310void 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
323void 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
346void GraphicsAdapter::setCursorPos(int row, int column)
347{
348 m_cursorRow = row;
349 m_cursorCol = column;
350}
351
352
353void 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
452void 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...
553void 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...
575void 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...
599void 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:246
#define VGA_720x350_70Hz
Definition: fabglconf.h:276
#define VGA_640x240_60Hz
Definition: fabglconf.h:219
#define QVGA_320x240_60Hz
Definition: fabglconf.h:189
This file contains fabgl::GraphicsAdapter definition.
Represents a 6 bit RGB color.
int16_t height
Definition: fabutils.h:233
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.