egra: optimised SSE memfill and colorblend
[iv.d.git] / glkgi.d
blob4015165f3b53e45c191c8d38bf06cec947552fd7
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 /// DO NOT USE YET!
17 module iv.glkgi /*is aliced*/;
19 import iv.alice;
20 import core.atomic;
21 import std.concurrency;
23 public import arsd.simpledisplay;
24 //public import arsd.color;
25 //public import arsd.image;
27 import iv.bclamp;
28 public import iv.cmdcongl;
29 import iv.glbinds;
31 //version=glkgi_rgba; // default, has priority
32 //version=glkgi_bgra;
34 version(BigEndian) static assert(0, "sorry, big-endian platforms aren't supported yet");
36 version(glkgi_rgba) {
37 public enum KGIRGBA = true;
38 } else version(glkgi_bgra) {
39 public enum KGIRGBA = false;
40 } else {
41 public enum KGIRGBA = true;
45 // ////////////////////////////////////////////////////////////////////////// //
46 public enum kgiMaxCurW = 64;
47 public enum kgiMaxCurH = 64;
50 // ////////////////////////////////////////////////////////////////////////// //
51 // 0:b; 1:g; 2:r; 3: nothing
52 private __gshared int vbufW = 800, vbufH = 600;
53 __gshared uint* vbuf; // see KGIRGBA
54 __gshared uint* vbufsaved; // saved before updating texture
55 private __gshared uint* vcurbuf;
56 private __gshared bool blit2x = false;
57 //private enum BlitType { normal, bw, green, red }
58 //private __gshared BlitType blitType = BlitType.normal;
59 private __gshared bool scanlines = false;
60 private __gshared SimpleWindow vbwin;
61 private __gshared uint vbTexId = 0;
62 private __gshared uint vbCurTexId = 0;
63 private __gshared uint sdrScanlineId = 0;
64 private __gshared int shaderVersionOk = -1; // <0: not checked; 0: fail; >0: ok
65 private __gshared string kgiTitle = "KGI Graphics";
66 private __gshared uint vupcounter = 0;
67 private __gshared uint vupcounterlast = 0;
68 private shared bool updateTexture = true;
69 private shared bool updateCurTexture = true;
70 private __gshared uint fps = 35; // average FPS
71 private __gshared bool showShaderWarnings = false;
73 private __gshared bool oldogl = false;
75 private __gshared int mcurX = 0, mcurY = 0;
76 private __gshared int mhotX = 0, mhotY = 0;
77 private __gshared int mcurHidden = 1;
79 private shared bool vWantMotionEvents = false;
80 private shared bool vWantCharEvents = false;
83 public @property bool kgiMotionEvents () nothrow @trusted @nogc { pragma(inline, true); return atomicLoad(vWantMotionEvents); } ///
84 public @property bool kgiCharEvents () nothrow @trusted @nogc { pragma(inline, true); return atomicLoad(vWantCharEvents); } ///
86 public @property void kgiMotionEvents (bool v) nothrow @trusted @nogc { pragma(inline, true); atomicStore(vWantMotionEvents, v); } ///
87 public @property void kgiCharEvents (bool v) nothrow @trusted @nogc { pragma(inline, true); atomicStore(vWantCharEvents, v); } ///
89 public @property int kgiWidth () nothrow @trusted @nogc { pragma(inline, true); return vbufW; }
90 public @property int kgiHeight () nothrow @trusted @nogc { pragma(inline, true); return vbufH; }
93 //FIXME! turn this to proper setter
94 public __gshared bool delegate () onKGICloseRequest;
97 shared static this () {
98 conRegVar!blit2x("v_scale2x", "video window scale");
99 conRegVar!scanlines("v_scanlines", "emulate old CRT scanline effect");
100 conRegVar!vbufW(64, 4096, "v_width", "video window width");
101 conRegVar!vbufH(64, 4096, "v_height", "video window height");
102 conRegVar!fps(1, 60, "v_fps", "video update rate");
103 conRegVar!kgiTitle("v_title", "video window title");
104 conRegVar!showShaderWarnings("v_shader_warnings", "show warnings from shader compilation");
105 conRegVar!oldogl("v_legacygl", "set to true to use legacy OpenGL");
109 private void setUpdateTextureFlag () {
110 pragma(inline, true);
111 if (vbuf !is null) {
112 atomicFence();
113 auto c = vupcounter;
114 if (vupcounterlast != c) {
115 vupcounterlast = c;
116 vbufsaved[0..vbufW*vbufH] = vbuf[0..vbufW*vbufH];
118 atomicStore(updateTexture, true);
124 public void kgiHideCursor () {
125 bool csc;
127 consoleLock();
128 scope(exit) consoleUnlock();
129 ++mcurHidden;
130 csc = (vbwin !is null && (mcurHidden == -1 || mcurHidden == 1));
132 if (csc) vbwin.showCursor();
137 public void kgiShowCursor () {
138 bool csc;
140 consoleLock();
141 scope(exit) consoleUnlock();
142 --mcurHidden;
143 csc = (vbwin !is null && mcurHidden == 0);
145 if (csc) vbwin.hideCursor();
150 public void kgiSetCursor (int wdt, int hgt, const(VColor)[] img, int hotx=0, int hoty=0) nothrow @trusted @nogc {
151 consoleLock();
152 scope(exit) consoleUnlock();
153 mhotX = hotx;
154 mhotY = hoty;
155 if (vcurbuf is null) return;
156 vcurbuf[0..kgiMaxCurW*kgiMaxCurH] = 0;
157 if (wdt < 1 || hgt < 1) return;
158 foreach (immutable dy; 0..64) {
159 if (dy >= hgt) break;
160 foreach (immutable dx; 0..64) {
161 if (dx >= wdt) break;
162 uint sp = dy*wdt+dx;
163 if (sp >= img.length) break;
164 vcurbuf[dy*kgiMaxCurW+dx] = img.ptr[sp];
167 atomicStore(updateCurTexture, true);
171 private void unpackDefaultCursor () nothrow @trusted @nogc {
172 mhotX = 2;
173 mhotY = 0;
174 if (vcurbuf is null) return;
175 vcurbuf[0..kgiMaxCurW*kgiMaxCurH] = 0;
176 foreach (immutable dy; 0..defaultCurHeight) {
177 foreach (immutable dx; 0..defaultCurWidth) {
178 uint sp = dy*defaultCurWidth+dx;
179 vcurbuf[dy*kgiMaxCurW+dx] = defaultCurPal.ptr[defaultCurImg.ptr[sp]];
186 public void kgiSetDefaultCursor () nothrow @trusted @nogc {
187 consoleLock();
188 scope(exit) consoleUnlock();
189 unpackDefaultCursor();
190 atomicStore(updateCurTexture, true);
194 public void kgiSetBlankCursor () nothrow @trusted @nogc {
195 consoleLock();
196 scope(exit) consoleUnlock();
197 mhotX = mhotY = 0;
198 if (vcurbuf is null) return;
199 vcurbuf[0..kgiMaxCurW*kgiMaxCurH] = 0;
200 atomicStore(updateCurTexture, true);
204 // ////////////////////////////////////////////////////////////////////////// //
206 public struct KGIEvent {
207 enum Type { None, Key, Mouse, Char, Close }
208 Type type = Type.None;
209 union {
210 KeyEvent k;
211 MouseEvent m;
212 dchar ch;
214 static createClose () pure nothrow @trusted @nogc {
215 KGIEvent ev = void;
216 ev.type = KGIEvent.Type.Close;
217 return ev;
219 @property const pure nothrow @trusted @nogc:
220 bool isMouse () { pragma(inline, true); return (type == Type.Mouse); }
221 bool isKey () { pragma(inline, true); return (type == Type.Key); }
222 bool isChar () { pragma(inline, true); return (type == Type.Char); }
223 bool isNone () { pragma(inline, true); return (type == Type.None); }
224 bool isClose () { pragma(inline, true); return (type == Type.Close); }
228 private __gshared KGIEvent[] evbuf;
229 private __gshared uint evbufused;
233 public bool kgiHasEvent () {
234 consoleLock();
235 scope(exit) consoleUnlock();
236 version(LDC) {} else atomicFence();
237 if (vupcounter) setUpdateTextureFlag(); // just in case
238 return (evbufused > 0 || vbwin is null); // no vbwin --> always has Quit
243 public KGIEvent kgiPeekEvent () {
244 consoleLock();
245 scope(exit) consoleUnlock();
246 version(LDC) {} else atomicFence();
247 if (!atomicLoad(kgiThreadStarted)) return KGIEvent.createClose();
248 if (vupcounter) setUpdateTextureFlag(); // just in case
249 if (evbufused > 0) return evbuf[0];
250 if (vbwin is null) return KGIEvent(KGIEvent.Type.Close);
251 return KGIEvent();
256 public KGIEvent kgiGetEvent () {
257 import core.thread;
258 import core.time;
259 version(LDC) {} else atomicFence();
260 if (vupcounter) setUpdateTextureFlag(); // just in case
261 for (;;) {
263 consoleLock();
264 scope(exit) consoleUnlock();
265 if (!atomicLoad(kgiThreadStarted)) return KGIEvent.createClose();
266 if (evbufused > 0) {
267 auto ev = evbuf[0];
268 if (evbufused > 1) {
269 import core.stdc.string : memmove;
270 memmove(evbuf.ptr, evbuf.ptr+1, KGIEvent.sizeof*(evbufused-1));
272 --evbufused;
273 evbuf[evbufused].type = KGIEvent.Type.None;
274 return ev;
275 } else if (vbwin is null) {
276 return KGIEvent(KGIEvent.Type.Close);
279 Thread.sleep(10.msecs); //FIXME
284 private void pushEventIntr (ref KGIEvent ev) {
285 if (evbufused >= evbuf.length) evbuf.length += 256;
286 evbuf[evbufused++] = ev;
291 public void kgiPushEvent (KeyEvent e) {
292 KGIEvent ev = void;
293 ev.type = KGIEvent.Type.Key;
294 ev.k = e;
295 consoleLock();
296 scope(exit) consoleUnlock();
297 pushEventIntr(ev);
301 public void kgiPushEvent (MouseEvent e) {
302 KGIEvent ev = void;
303 ev.type = KGIEvent.Type.Mouse;
304 ev.m = e;
305 consoleLock();
306 scope(exit) consoleUnlock();
307 pushEventIntr(ev);
311 public void kgiPushEvent (dchar ch) {
312 KGIEvent ev = void;
313 ev.type = KGIEvent.Type.Char;
314 ev.ch = ch;
315 consoleLock();
316 scope(exit) consoleUnlock();
317 pushEventIntr(ev);
321 public void kgiPushCloseEvent(bool clearQueue=true) () {
322 KGIEvent ev = void;
323 ev.type = KGIEvent.Type.Close;
324 consoleLock();
325 scope(exit) consoleUnlock();
326 static if (clearQueue) evbufused = 0;
327 pushEventIntr(ev);
331 /// remove all keypresses from input queue
332 public void kgiKeyFlush () {
333 uint sidx = 0, didx = 0;
334 consoleLock();
335 scope(exit) consoleUnlock();
336 if (!atomicLoad(kgiThreadStarted)) return;
337 while (sidx < evbufused) {
338 if (!evbuf[sidx].isKey) {
339 if (sidx != didx) evbuf[didx] = evbuf[sidx];
340 ++didx;
342 ++sidx;
344 evbufused = didx;
348 /// wait for keypress (and eat it)
349 public KGIEvent kgiWaitKey () {
350 for (;;) {
351 auto ev = kgiGetEvent();
352 if (ev.isClose) {
353 consoleLock();
354 scope(exit) consoleUnlock();
355 evbufused = 1;
356 evbuf[0] = ev;
357 return ev;
359 if (!ev.isKey) continue;
360 if (!ev.k.pressed) continue;
361 return ev;
366 /// flush drawing buffer (copy it to actual screen)
367 public void kgiFlush () {
368 setUpdateTextureFlag();
369 while (atomicLoad(updateTexture)) {
370 import core.thread;
371 import core.time;
372 if (!atomicLoad(kgiThreadStarted)) return;
373 Thread.sleep(1.msecs);
378 // ////////////////////////////////////////////////////////////////////////// //
380 public void kgiDeinit () {
381 concmd("quit");
385 private void kgiThread (Tid starterTid) {
386 try {
387 vbwin = new SimpleWindow(vbufW*(blit2x ? 2 : 1), vbufH*(blit2x ? 2 : 1), kgiTitle, OpenGlOptions.yes, Resizability.fixedSize);
388 if (mcurHidden == 0) vbwin.hideCursor();
390 static if (is(typeof(openGLContextFallbackActivated))) {
391 if (openGLContextFallbackActivated) oldogl = true;
394 vbwin.redrawOpenGlScene = delegate () {
395 glgfxBlit();
396 glconDraw();
399 version(Windows) {
400 // eat shit
401 } else {
402 vbwin.closeQuery = delegate () {
403 if (onKGICloseRequest !is null) try { if (!onKGICloseRequest()) return; } catch (Throwable e) {}
404 concmd("quit");
408 vbwin.visibleForTheFirstTime = delegate () {
409 vbwin.setAsCurrentOpenGlContext();
412 import core.stdc.stdio;
413 printf("GL version: %s\n", glGetString(GL_VERSION));
414 GLint l, h;
415 glGetIntegerv(GL_MAJOR_VERSION, &h);
416 glGetIntegerv(GL_MINOR_VERSION, &l);
417 printf("version: %d.%d\n", h, l);
418 printf("shader version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
419 GLint svcount;
420 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
421 if (svcount > 0) {
422 printf("%d shader versions supported:\n", svcount);
423 foreach (GLuint n; 0..svcount) printf(" %d: %s\n", n, glGetStringi(GL_SHADING_LANGUAGE_VERSION, n));
426 GLint ecount;
427 glGetIntegerv(GL_NUM_EXTENSIONS, &ecount);
428 if (ecount > 0) {
429 printf("%d extensions supported:\n", ecount);
430 foreach (GLuint n; 0..ecount) printf(" %d: %s\n", n, glGetStringi(GL_EXTENSIONS, n));
435 glgfxInitTexture();
436 glconInit(vbufW, vbufH, (blit2x ? 2 : 1));
437 glgfxUpdateTexture();
438 vbwin.redrawOpenGlSceneNow();
440 send(starterTid, 42);
443 bool receiveMessages () {
444 bool res = false;
445 for (;;) {
446 import core.time : Duration;
447 auto got = receiveTimeout(
448 Duration.zero, // don't wait
449 (OwnerTerminated e) {
450 //conwriteln("OWNER IS TERMINATED!");
451 res = true;
453 (Variant v) {
454 conwriteln("WARNING: unknown thread message received and ignored: ", v.toString);
457 if (!got) break; // no more messages
459 return res;
462 bool processConsoleCommands () {
463 consoleLock();
464 scope(exit) consoleUnlock();
465 auto ccwasempty = conQueueEmpty();
466 conProcessQueue();
467 return (!ccwasempty && conQueueEmpty());
470 bool lastConVisible = isConsoleVisible;
472 //FIXME: FPS time calculations are totally broken
473 import core.time;
474 uint rfps = (fps < 0 ? 1 : fps > 120 ? 120 : fps);
475 uint msfull = 1000/fps;
476 auto lastFrameTime = MonoTime.currTime;
477 auto lastCursorTime = MonoTime.currTime;
479 version(Windows) {
480 // windoze eats shit! if we won't awake event loop here, it will fuck itself.
481 import core.sys.windows.winuser;
482 PostMessage(null, WM_USER, 0, 0);
485 vbwin.eventLoop(8,
486 delegate () {
487 //version(Windows) { {import core.stdc.stdio : printf; printf("xxx\n"); } }
488 if (vbwin.closed) return;
489 if (isQuitRequested) { kgiPushCloseEvent(); vbwin.close(); return; }
490 if (receiveMessages()) { kgiPushCloseEvent(); vbwin.close(); return; }
491 auto conexeced = processConsoleCommands();
492 bool convis = isConsoleVisible;
493 if (lastConVisible != convis || conexeced) {
494 lastConVisible = isConsoleVisible;
495 setUpdateTextureFlag();
496 consoleLock();
497 scope(exit) consoleUnlock();
498 if (evbufused == 0) {
499 KGIEvent kev;
500 pushEventIntr(kev);
503 bool newFrame = false;
504 auto ctt = MonoTime.currTime;
505 if (lastConVisible != convis || conexeced) {
506 newFrame = true;
507 } else if (convis) {
508 // visible console should be updated at least 35 times per second
509 newFrame = ((ctt-lastFrameTime).total!"msecs" >= 28);
510 } else {
511 // fps
512 if ((ctt-lastFrameTime).total!"msecs" >= msfull) newFrame = true;
514 //if (newFrame) atomicStore(updateTexture, true);
515 if (newFrame || atomicLoad(updateTexture)) {
516 lastFrameTime = ctt;
517 lastCursorTime = ctt;
518 glgfxUpdateTexture();
519 //version(Windows) { {import core.stdc.stdio : printf; printf("000\n"); } }
520 vbwin.redrawOpenGlSceneNow();
521 } else if (mcurHidden == 0) {
522 // ~60 FPS for mouse cursor
523 if ((ctt-lastCursorTime).total!"msecs" >= 16) {
524 lastCursorTime = ctt;
525 glgfxUpdateCurTexture();
526 //version(Windows) { {import core.stdc.stdio : printf; printf("001\n"); } }
527 vbwin.redrawOpenGlSceneNow();
531 delegate (KeyEvent event) {
532 if (vbwin.closed) return;
533 if (isQuitRequested) { kgiPushCloseEvent(); vbwin.close(); return; }
534 if (glconKeyEvent(event)) return;
535 //if (event.pressed && event.key == Key.Escape) { concmd("quit"); return; }
536 kgiPushEvent(event);
538 delegate (MouseEvent event) {
539 if (vbwin.closed) return;
540 mcurX = event.x/(blit2x ? 2 : 1);
541 mcurY = event.y/(blit2x ? 2 : 1);
542 if (event.type == MouseEventType.motion && !kgiMotionEvents) return;
543 if (blit2x) { event.x /= 2; event.y /= 2; }
544 kgiPushEvent(event);
546 delegate (dchar ch) {
547 if (vbwin.closed) return;
548 if (glconCharEvent(ch)) return;
549 if (ch == '`') concmd("r_console tan");
550 if (!kgiCharEvents) return;
551 kgiPushEvent(ch);
554 } catch (Throwable e) {
555 // here, we are dead and fucked (the exact order doesn't matter)
556 import core.stdc.stdlib : abort;
557 import core.stdc.stdio : fprintf, stderr;
558 import core.memory : GC;
559 import core.thread;
560 GC.disable(); // yeah
561 thread_suspendAll(); // stop right here, you criminal scum!
562 auto s = e.toString();
563 fprintf(stderr, "\n=== FATAL ===\n%.*s\n", cast(uint)s.length, s.ptr);
564 abort(); // die, you bitch!
566 flushGui();
567 vbwin = null;
570 import core.stdc.stdlib : exit;
571 exit(0);
574 kgiPushCloseEvent();
575 atomicStore(kgiThreadStarted, false);
579 // ////////////////////////////////////////////////////////////////////////// //
580 private __gshared Tid kgiTid;
581 private shared bool kgiThreadStarted = false;
584 private void startKGIThread () {
585 //if (!cas(&kgiThreadStarted, false, true)) assert(0, "render thread already started!");
586 kgiTid = spawn(&kgiThread, thisTid);
587 setMaxMailboxSize(kgiTid, 1024, OnCrowding.throwException); //FIXME
588 // wait for "i'm ready" signal
589 receive(
590 (int ok) {
591 if (ok != 42) assert(0, "wtf?!");
594 //conwriteln("rendering thread started");
599 public bool kgiInitEx (int awdt, int ahgt, string title, bool a2x, uint afps) {
600 import core.stdc.stdlib : malloc;
601 import arsd.simpledisplay;
603 if (awdt < 1 || ahgt < 1 || awdt > 4096 || ahgt > 4096) {
604 return false;
605 //assert(0, "invalid dimensions");
608 if (!cas(&kgiThreadStarted, false, true)) return false;
610 if (vbwin !is null) assert(0, "double initialization");
612 if (afps < 1) afps = 1; else if (afps > 60) afps = 60;
614 conSealVar("v_scale2x");
615 conSealVar("v_width");
616 conSealVar("v_height");
617 conSealVar("v_title");
618 conSealVar("v_fps");
620 vbuf = cast(typeof(vbuf))malloc(vbuf[0].sizeof*awdt*ahgt);
621 if (vbuf is null) assert(0, "KGI: out of memory");
622 vbuf[0..awdt*ahgt] = 0;
624 vbufsaved = cast(typeof(vbuf))malloc(vbufsaved[0].sizeof*awdt*ahgt);
625 if (vbufsaved is null) assert(0, "KGI: out of memory");
626 vbufsaved[0..awdt*ahgt] = vbuf[0..awdt*ahgt];
628 vcurbuf = cast(typeof(vcurbuf))malloc(vcurbuf[0].sizeof*kgiMaxCurW*kgiMaxCurH);
629 if (vbuf is null) assert(0, "KGI: out of memory");
630 vcurbuf[0..kgiMaxCurW*kgiMaxCurH] = 0;
631 unpackDefaultCursor();
633 vbufW = awdt;
634 vbufH = ahgt;
635 blit2x = a2x;
636 fps = afps;
637 kgiTitle = title;
639 if (!oldogl) {
640 setOpenGLContextVersion(3, 2); // up to GLSL 150
641 //openGLContextCompatible = false;
642 static if (is(typeof(openGLContextAllowFallback))) {
643 openGLContextAllowFallback = true;
647 startKGIThread();
649 return true;
653 public bool kgiInit () { return kgiInitEx(vbufW, vbufH, kgiTitle, blit2x, fps); }
654 public bool kgiInit (int awdt, int ahgt) { return kgiInitEx(awdt, ahgt, kgiTitle, blit2x, fps); }
655 public bool kgiInit (int awdt, int ahgt, string title) { return kgiInitEx(awdt, ahgt, title, blit2x, fps); }
656 public bool kgiInit (int awdt, int ahgt, string title, bool a2x) { return kgiInitEx(awdt, ahgt, title, a2x, fps); }
657 public bool kgiInit (int awdt, int ahgt, string title, bool a2x, uint afps) { return kgiInitEx(awdt, ahgt, title, a2x, afps); }
660 // ////////////////////////////////////////////////////////////////////////// //
661 private uint glgfxCompileShader (const(char)[] src) nothrow @trusted {
662 import iv.glbinds;
664 if (shaderVersionOk < 0) {
665 // check if we have sufficient shader version here
666 shaderVersionOk = 0;
667 bool found = false;
668 GLint svcount;
669 glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &svcount);
670 if (svcount > 0) {
671 foreach (GLuint n; 0..svcount) {
672 import core.stdc.string : strncmp;
673 auto v = glGetStringi(GL_SHADING_LANGUAGE_VERSION, n);
674 if (v is null) continue;
675 //if (strncmp(v, "130", 3) != 0) continue;
676 if (strncmp(cast(char*)v, "150".ptr, 3) != 0) continue;
677 if (v[3] > ' ') continue;
678 found = true;
679 break;
682 if (!found) return 0; //assert(0, "can't find OpenGL GLSL 150");
685 auto adr = glGetProcAddress("glTexParameterf");
686 if (adr is null) return 0;
689 shaderVersionOk = 1;
691 if (shaderVersionOk == 0) return 0;
692 return compileShaders(src);
696 private void glgfxInitTexture () nothrow @trusted {
697 import iv.glbinds;
699 //if (vbTexId) { glDeleteTextures(1, &vbTexId); vbTexId = 0; }
701 enum wrapOpt = GL_REPEAT;
702 enum filterOpt = GL_NEAREST; //GL_LINEAR;
703 enum ttype = GL_UNSIGNED_BYTE;
705 glGenTextures(1, &vbTexId);
706 if (vbTexId == 0) assert(0, "can't create kgi texture");
708 glGenTextures(1, &vbCurTexId);
709 if (vbTexId == 0) assert(0, "can't create kgicursor texture");
711 GLint gltextbinding;
712 glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
713 scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
715 glBindTexture(GL_TEXTURE_2D, vbTexId);
716 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
717 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
718 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
719 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
720 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
721 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
723 GLfloat[4] bclr = 0.0;
724 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
726 static if (KGIRGBA) enum TexType = GL_RGBA; else enum TexType = GL_BGRA;
727 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vbufW, vbufH, 0, TexType, GL_UNSIGNED_BYTE, vbufsaved);
730 glBindTexture(GL_TEXTURE_2D, vbCurTexId);
731 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
732 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
733 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
734 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
735 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
736 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
738 //GLfloat[4] bclr = 0.0;
739 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
741 //static if (KGIRGBA) enum TexType = GL_RGBA; else enum TexType = GL_BGRA;
742 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kgiMaxCurW, kgiMaxCurH, 0, TexType, GL_UNSIGNED_BYTE, vcurbuf);
746 private void glgfxUpdateTexture () nothrow @trusted {
747 import iv.glbinds;
749 consoleLock();
750 scope(exit) consoleUnlock();
752 version(LDC) {} else atomicFence();
753 vupcounter = 0;
755 static if (KGIRGBA) enum TexType = GL_RGBA; else enum TexType = GL_BGRA;
756 if (!oldogl) {
757 glTextureSubImage2D(vbTexId, 0, 0/*x*/, 0/*y*/, vbufW, vbufH, TexType, GL_UNSIGNED_BYTE, vbufsaved);
758 } else {
759 glBindTexture(GL_TEXTURE_2D, vbTexId);
760 glTexSubImage2D(GL_TEXTURE_2D, 0, 0/*x*/, 0/*y*/, vbufW, vbufH, TexType, GL_UNSIGNED_BYTE, vbufsaved);
761 glBindTexture(GL_TEXTURE_2D, 0);
763 atomicStore(updateTexture, false);
764 vupcounterlast = vupcounterlast.max;
766 if (atomicLoad(updateCurTexture)) {
767 if (!oldogl) {
768 glTextureSubImage2D(vbCurTexId, 0, 0/*x*/, 0/*y*/, kgiMaxCurW, kgiMaxCurH, TexType, GL_UNSIGNED_BYTE, vcurbuf);
769 } else {
770 glBindTexture(GL_TEXTURE_2D, vbCurTexId);
771 glTexSubImage2D(GL_TEXTURE_2D, 0, 0/*x*/, 0/*y*/, kgiMaxCurW, kgiMaxCurH, TexType, GL_UNSIGNED_BYTE, vcurbuf);
772 glBindTexture(GL_TEXTURE_2D, 0);
774 atomicStore(updateCurTexture, false);
779 private void glgfxUpdateCurTexture () nothrow @trusted {
780 import iv.glbinds;
782 if (atomicLoad(updateCurTexture)) {
783 consoleLock();
784 scope(exit) consoleUnlock();
786 static if (KGIRGBA) enum TexType = GL_RGBA; else enum TexType = GL_BGRA;
787 if (!oldogl) {
788 glTextureSubImage2D(vbCurTexId, 0, 0/*x*/, 0/*y*/, kgiMaxCurW, kgiMaxCurH, TexType, GL_UNSIGNED_BYTE, vcurbuf);
789 } else {
790 glBindTexture(GL_TEXTURE_2D, vbCurTexId);
791 glTexSubImage2D(GL_TEXTURE_2D, 0, 0/*x*/, 0/*y*/, kgiMaxCurW, kgiMaxCurH, TexType, GL_UNSIGNED_BYTE, vcurbuf);
792 glBindTexture(GL_TEXTURE_2D, 0);
795 atomicStore(updateCurTexture, false);
800 private void glgfxBlit () nothrow @trusted {
801 import iv.glbinds;
803 consoleLock();
804 scope(exit) consoleUnlock();
806 //if (vbwin is null || vbwin.closed || vbTexId == 0) return;
809 GLint glmatmode;
810 GLint gltextbinding;
811 GLint oldprg;
812 GLint oldfbr, oldfbw;
813 GLint[4] glviewport;
814 glGetIntegerv(GL_MATRIX_MODE, &glmatmode);
815 glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
816 glGetIntegerv(GL_VIEWPORT, glviewport.ptr);
817 glGetIntegerv(GL_CURRENT_PROGRAM, &oldprg);
818 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldfbr);
819 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldfbw);
820 glMatrixMode(GL_PROJECTION); glPushMatrix();
821 glMatrixMode(GL_MODELVIEW); glPushMatrix();
822 glMatrixMode(GL_TEXTURE); glPushMatrix();
823 glMatrixMode(GL_COLOR); glPushMatrix();
824 glPushAttrib(/*GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_CURRENT_BIT*/GL_ALL_ATTRIB_BITS); // let's play safe
825 // restore on exit
826 scope(exit) {
827 glPopAttrib(/*GL_ENABLE_BIT*/);
828 glMatrixMode(GL_PROJECTION); glPopMatrix();
829 glMatrixMode(GL_MODELVIEW); glPopMatrix();
830 glMatrixMode(GL_TEXTURE); glPopMatrix();
831 glMatrixMode(GL_COLOR); glPopMatrix();
832 glMatrixMode(glmatmode);
833 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, oldfbr);
834 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, oldfbw);
835 glBindTexture(GL_TEXTURE_2D, gltextbinding);
836 glUseProgram(oldprg);
837 glViewport(glviewport.ptr[0], glviewport.ptr[1], glviewport.ptr[2], glviewport.ptr[3]);
841 enum x = 0;
842 enum y = 0;
843 immutable w = vbufW;
844 immutable h = vbufH;
846 if (!oldogl) {
847 glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
848 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
850 if (scanlines && shaderVersionOk < 0) sdrScanlineId = glgfxCompileShader(sdrScanlineSrc);
851 glUseProgram(scanlines ? sdrScanlineId : 0);
854 glMatrixMode(GL_PROJECTION); // for ortho camera
855 glLoadIdentity();
856 // left, right, bottom, top, near, far
857 glViewport(0, 0, w*(blit2x ? 2 : 1), h*(blit2x ? 2 : 1));
858 glOrtho(0, w, h, 0, -1, 1); // top-to-bottom
859 glMatrixMode(GL_MODELVIEW);
860 glLoadIdentity();
862 glEnable(GL_TEXTURE_2D);
863 glDisable(GL_LIGHTING);
864 glDisable(GL_DITHER);
865 //glDisable(GL_BLEND);
866 glDisable(GL_DEPTH_TEST);
867 //glEnable(GL_BLEND);
868 //glBlendFunc(GL_SRC_ALPHA, GL_ONE);
869 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
870 glDisable(GL_BLEND);
871 glDisable(GL_STENCIL_TEST);
873 glColor4f(1, 1, 1, 1);
874 glBindTexture(GL_TEXTURE_2D, vbTexId);
875 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
876 glBegin(GL_QUADS);
877 glTexCoord2f(0.0f, 0.0f); glVertex2i(x, y); // top-left
878 glTexCoord2f(1.0f, 0.0f); glVertex2i(w, y); // top-right
879 glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); // bottom-right
880 glTexCoord2f(0.0f, 1.0f); glVertex2i(x, h); // bottom-left
881 glEnd();
883 if (mcurHidden == 0) {
884 glEnable(GL_BLEND);
885 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
886 glBindTexture(GL_TEXTURE_2D, vbCurTexId);
888 int cx0 = mcurX-mhotX;
889 int cy0 = mcurY-mhotY;
891 glBegin(GL_QUADS);
892 glTexCoord2f(0.0f, 0.0f); glVertex2i(cx0, cy0); // top-left
893 glTexCoord2f(1.0f, 0.0f); glVertex2i(cx0+kgiMaxCurW, cy0); // top-right
894 glTexCoord2f(1.0f, 1.0f); glVertex2i(cx0+kgiMaxCurW, cy0+kgiMaxCurH); // bottom-right
895 glTexCoord2f(0.0f, 1.0f); glVertex2i(cx0, cy0+kgiMaxCurH); // bottom-left
896 glEnd();
898 glDisable(GL_BLEND);
903 // ////////////////////////////////////////////////////////////////////////// //
904 alias VColor = uint;
906 /// vlRGBA struct to ease color components extraction/replacing
907 align(1) struct vlRGBA {
908 align(1):
909 static if (KGIRGBA) {
910 ubyte b, g, r, a;
911 } else {
912 ubyte r, g, b, a;
915 static assert(vlRGBA.sizeof == VColor.sizeof);
918 static if (KGIRGBA) {
919 enum : VColor {
920 vlAShift = 24,
921 vlRShift = 0,
922 vlGShift = 8,
923 vlBShift = 16,
925 } else {
926 enum : VColor {
927 vlAShift = 24,
928 vlRShift = 16,
929 vlGShift = 8,
930 vlBShift = 0,
933 static assert(vlAShift == 24, "invalid A position in color"); // alas
936 enum : VColor {
937 vlAMask = 0xffu<<vlAShift,
938 vlRMask = 0xffu<<vlRShift,
939 vlGMask = 0xffu<<vlGShift,
940 vlBMask = 0xffu<<vlBShift,
941 vlColorMask = vlRMask|vlGMask|vlBMask,
945 enum VColor Transparent = vlAMask; /// completely transparent pixel color
948 bool isTransparent(T : VColor) (T col) pure nothrow @safe @nogc {
949 pragma(inline, true);
950 return ((col&vlAMask) == vlAMask);
953 bool isOpaque(T : VColor) (T col) pure nothrow @safe @nogc {
954 pragma(inline, true);
955 return ((col&vlAMask) == 0);
958 // a=0: opaque
959 VColor rgbcol(TR, TG, TB, TA=ubyte) (TR r, TG g, TB b, TA a=0) pure nothrow @safe @nogc
960 if (__traits(isIntegral, TR) && __traits(isIntegral, TG) && __traits(isIntegral, TB) && __traits(isIntegral, TA)) {
961 pragma(inline, true);
962 return
963 (clampToByte(a)<<vlAShift)|
964 (clampToByte(r)<<vlRShift)|
965 (clampToByte(g)<<vlGShift)|
966 (clampToByte(b)<<vlBShift);
969 alias rgbacol = rgbcol;
972 // generate some templates (rgbRed, rgbSetRed, etc.)
973 private enum genRGBGetSet(string cname) =
974 "ubyte rgb"~cname~"() (VColor clr) pure nothrow @safe @nogc {\n"~
975 " pragma(inline, true);\n"~
976 " return ((clr>>vl"~cname[0]~"Shift)&0xff);\n"~
977 "}\n"~
978 "VColor rgbSet"~cname~"(T) (VColor clr, T v) pure nothrow @safe @nogc if (__traits(isIntegral, T)) {\n"~
979 " pragma(inline, true);\n"~
980 " return (clr&~vl"~cname[0]~"Mask)|(clampToByte(v)<<vl"~cname[0]~"Shift);\n"~
981 "}\n";
983 mixin(genRGBGetSet!"Alpha");
984 mixin(genRGBGetSet!"Red");
985 mixin(genRGBGetSet!"Green");
986 mixin(genRGBGetSet!"Blue");
989 // ////////////////////////////////////////////////////////////////////////// //
990 nothrow @trusted @nogc {
992 private void putPixelIntrNoCheck (in int xx, in int yy, in VColor col) {
993 pragma(inline, true);
994 uint* da = vbuf+yy*vbufW+xx;
995 if (col&vlAMask) {
996 immutable uint a = 256-(col>>24); // to not loose bits
997 immutable uint dc = (*da)&0xffffff;
998 immutable uint srb = (col&0xff00ff);
999 immutable uint sg = (col&0x00ff00);
1000 immutable uint drb = (dc&0xff00ff);
1001 immutable uint dg = (dc&0x00ff00);
1002 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
1003 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
1004 *da = orb|og;
1005 } else {
1006 *da = col;
1010 private void putPixelIntr (int xx, int yy, VColor col) {
1011 pragma(inline, true);
1012 if ((col&vlAMask) != vlAMask && xx >= 0 && yy >= 0 && xx < vbufW && yy < vbufH) {
1013 uint* da = vbuf+yy*vbufW+xx;
1014 if (col&vlAMask) {
1015 immutable uint a = 256-(col>>24); // to not loose bits
1016 immutable uint dc = (*da)&0xffffff;
1017 immutable uint srb = (col&0xff00ff);
1018 immutable uint sg = (col&0x00ff00);
1019 immutable uint drb = (dc&0xff00ff);
1020 immutable uint dg = (dc&0x00ff00);
1021 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
1022 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
1023 *da = orb|og;
1024 } else {
1025 *da = col;
1030 void putPixel (int xx, int yy, VColor col) {
1031 pragma(inline, true);
1032 if ((col&vlAMask) != vlAMask && xx >= 0 && yy >= 0 && xx < vbufW && yy < vbufH) {
1033 uint* da = vbuf+yy*vbufW+xx;
1034 if (col&vlAMask) {
1035 immutable uint a = 256-(col>>24); // to not loose bits
1036 immutable uint dc = (*da)&0xffffff;
1037 immutable uint srb = (col&0xff00ff);
1038 immutable uint sg = (col&0x00ff00);
1039 immutable uint drb = (dc&0xff00ff);
1040 immutable uint dg = (dc&0x00ff00);
1041 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
1042 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
1043 *da = orb|og;
1044 } else {
1045 *da = col;
1047 version(LDC) {} else atomicFence();
1048 ++vupcounter;
1052 private void setPixelIntr (int xx, int yy, VColor col) {
1053 pragma(inline, true);
1054 if (xx >= 0 && yy >= 0 && xx < vbufW && yy < vbufH) {
1055 uint* da = vbuf+yy*vbufW+xx;
1056 *da = col;
1060 void setPixel (int xx, int yy, VColor col) {
1061 pragma(inline, true);
1062 if (xx >= 0 && yy >= 0 && xx < vbufW && yy < vbufH) {
1063 uint* da = vbuf+yy*vbufW+xx;
1064 *da = col;
1065 version(LDC) {} else atomicFence();
1066 ++vupcounter;
1071 VColor getPixel (int xx, int yy) {
1072 pragma(inline, true);
1073 return (xx >= 0 && yy >= 0 && xx < vbufW && yy < vbufH ? vbuf[yy*vbufW+xx]&vlColorMask : Transparent);
1077 // ////////////////////////////////////////////////////////////////////////// //
1078 void cls (VColor col) {
1079 vbuf[0..vbufW*vbufH] = col;
1080 version(LDC) {} else atomicFence();
1081 ++vupcounter;
1085 // ////////////////////////////////////////////////////////////////////////// //
1086 void drawRect (int x, int y, int w, int h, immutable VColor col) {
1087 if (w < 1 || h < 1) return;
1088 if (x <= -w || y <= -h || x >= vbufW || y >= vbufH || isTransparent(col)) return;
1089 if (x < 0) { w += x; x = 0; }
1090 if (y < 0) { h += y; h = 0; }
1091 if (x+w >= vbufW) w = vbufW-x;
1092 if (y+h >= vbufH) h = vbufH-y;
1093 assert(x >= 0 && y >= 0 && x < vbufW && y < vbufH && w > 0 && h > 0 && x+w <= vbufW && y+h <= vbufH);
1094 if (isOpaque(col)) {
1095 uint d = y*vbufW+x;
1096 vbuf[d..d+w] = col;
1097 d += vbufW;
1098 foreach (immutable yy; y+1..y+h-1) {
1099 vbuf[d] = col;
1100 vbuf[d+w-1] = col;
1101 d += vbufW;
1103 if (h > 1) vbuf[d..d+w] = col;
1104 } else {
1105 foreach (immutable yy; y..y+h) {
1106 putPixelIntr(x, yy, col);
1107 putPixelIntr(x+w-1, yy, col);
1109 foreach (immutable xx; x+1..x+w-1) {
1110 putPixelIntr(xx, y, col);
1111 if (h > 1) putPixelIntr(xx, y+h-1, col);
1114 version(LDC) {} else atomicFence();
1115 ++vupcounter;
1118 void fillRect (int x, int y, int w, int h, immutable VColor col) {
1119 if (w < 1 || h < 1) return;
1120 if (x <= -w || y <= -h || x >= vbufW || y >= vbufH || isTransparent(col)) return;
1121 if (x < 0) { w += x; x = 0; }
1122 if (y < 0) { h += y; h = 0; }
1123 if (x+w >= vbufW) w = vbufW-x;
1124 if (y+h >= vbufH) h = vbufH-y;
1125 assert(x >= 0 && y >= 0 && x < vbufW && y < vbufH && w > 0 && h > 0 && x+w <= vbufW && y+h <= vbufH);
1126 if (isOpaque(col)) {
1127 uint d = y*vbufW+x;
1128 foreach (immutable yy; y..y+h) {
1129 vbuf[d..d+w] = col;
1130 d += vbufW;
1132 } else {
1133 foreach (immutable yy; y..y+h) {
1134 foreach (immutable xx; x..x+w) {
1135 putPixelIntr(xx, yy, col);
1139 version(LDC) {} else atomicFence();
1140 ++vupcounter;
1143 void hline (int x, int y, int len, immutable VColor col) { drawRect(x, y, len, 1, col); }
1144 void vline (int x, int y, int len, immutable VColor col) { drawRect(x, y, 1, len, col); }
1147 // ////////////////////////////////////////////////////////////////////////// //
1148 void drawLine(bool lastPoint=true) (int x0, int y0, int x1, int y1, immutable VColor col) {
1149 enum swap(string a, string b) = "{int tmp_="~a~";"~a~"="~b~";"~b~"=tmp_;}";
1151 if ((col&vlAMask) == vlAMask) return;
1153 if (x0 == x1 && y0 == y1) {
1154 static if (lastPoint) putPixel(x0, y0, col);
1155 return;
1158 // clip rectange
1159 int wx0 = 0, wy0 = 0, wx1 = vbufW-1, wy1 = vbufH-1;
1160 // other vars
1161 int stx, sty; // "steps" for x and y axes
1162 int dsx, dsy; // "lengthes" for x and y axes
1163 int dx2, dy2; // "double lengthes" for x and y axes
1164 int xd, yd; // current coord
1165 int e; // "error" (as in bresenham algo)
1166 int rem;
1167 int term;
1168 int *d0, d1;
1169 // horizontal setup
1170 if (x0 < x1) {
1171 // from left to right
1172 if (x0 > wx1 || x1 < wx0) return; // out of screen
1173 stx = 1; // going right
1174 } else {
1175 // from right to left
1176 if (x1 > wx1 || x0 < wx0) return; // out of screen
1177 stx = -1; // going left
1178 x0 = -x0;
1179 x1 = -x1;
1180 wx0 = -wx0;
1181 wx1 = -wx1;
1182 mixin(swap!("wx0", "wx1"));
1184 // vertical setup
1185 if (y0 < y1) {
1186 // from top to bottom
1187 if (y0 > wy1 || y1 < wy0) return; // out of screen
1188 sty = 1; // going down
1189 } else {
1190 // from bottom to top
1191 if (y1 > wy1 || y0 < wy0) return; // out of screen
1192 sty = -1; // going up
1193 y0 = -y0;
1194 y1 = -y1;
1195 wy0 = -wy0;
1196 wy1 = -wy1;
1197 mixin(swap!("wy0", "wy1"));
1199 dsx = x1-x0;
1200 dsy = y1-y0;
1201 if (dsx < dsy) {
1202 d0 = &yd;
1203 d1 = &xd;
1204 mixin(swap!("x0", "y0"));
1205 mixin(swap!("x1", "y1"));
1206 mixin(swap!("dsx", "dsy"));
1207 mixin(swap!("wx0", "wy0"));
1208 mixin(swap!("wx1", "wy1"));
1209 mixin(swap!("stx", "sty"));
1210 } else {
1211 d0 = &xd;
1212 d1 = &yd;
1214 dx2 = 2*dsx;
1215 dy2 = 2*dsy;
1216 xd = x0;
1217 yd = y0;
1218 e = 2*dsy-dsx;
1219 term = x1;
1220 bool xfixed = false;
1221 if (y0 < wy0) {
1222 // clip at top
1223 int temp = dx2*(wy0-y0)-dsx;
1224 xd += temp/dy2;
1225 rem = temp%dy2;
1226 if (xd > wx1) return; // x is moved out of clipping rect, nothing to do
1227 if (xd+1 >= wx0) {
1228 yd = wy0;
1229 e -= rem+dsx;
1230 if (rem > 0) { ++xd; e += dy2; }
1231 xfixed = true;
1234 if (!xfixed && x0 < wx0) {
1235 // clip at left
1236 int temp = dy2*(wx0-x0);
1237 yd += temp/dx2;
1238 rem = temp%dx2;
1239 if (yd > wy1 || yd == wy1 && rem >= dsx) return;
1240 xd = wx0;
1241 e += rem;
1242 if (rem >= dsx) { ++yd; e -= dx2; }
1244 if (y1 > wy1) {
1245 // clip at bottom
1246 int temp = dx2*(wy1-y0)+dsx;
1247 term = x0+temp/dy2;
1248 rem = temp%dy2;
1249 if (rem == 0) --term;
1251 if (term > wx1) term = wx1; // clip at right
1252 static if (lastPoint) {
1253 // draw last point
1254 ++term;
1255 } else {
1256 if (term == xd) return; // this is the only point, get out of here
1258 if (sty == -1) yd = -yd;
1259 if (stx == -1) { xd = -xd; term = -term; }
1260 dx2 -= dy2;
1261 // draw it; `putPixel()` can omit checks
1262 while (xd != term) {
1263 putPixelIntrNoCheck(*d0, *d1, col);
1264 // done drawing, move coords
1265 if (e >= 0) {
1266 yd += sty;
1267 e -= dx2;
1268 } else {
1269 e += dy2;
1271 xd += stx;
1273 version(LDC) {} else atomicFence();
1274 ++vupcounter;
1278 // ////////////////////////////////////////////////////////////////////////// //
1279 private void plot4points() (int cx, int cy, int x, int y, VColor clr) @trusted {
1280 putPixelIntr(cx+x, cy+y, clr);
1281 if (x != 0) putPixelIntr(cx-x, cy+y, clr);
1282 if (y != 0) putPixelIntr(cx+x, cy-y, clr);
1283 putPixelIntr(cx-x, cy-y, clr);
1287 void drawCircle (int cx, int cy, int radius, VColor clr) @trusted {
1288 if (radius > 0 && !isTransparent(clr)) {
1289 int error = -radius, x = radius, y = 0;
1290 if (radius == 1) { putPixelIntr(cx, cy, clr); return; }
1291 while (x > y) {
1292 plot4points(cx, cy, x, y, clr);
1293 plot4points(cx, cy, y, x, clr);
1294 error += y*2+1;
1295 ++y;
1296 if (error >= 0) { --x; error -= x*2; }
1298 plot4points(cx, cy, x, y, clr);
1299 version(LDC) {} else atomicFence();
1300 ++vupcounter;
1304 void fillCircle (int cx, int cy, int radius, VColor clr) @trusted {
1305 if (radius > 0 && !isTransparent(clr)) {
1306 int error = -radius, x = radius, y = 0;
1307 if (radius == 1) { putPixelIntr(cx, cy, clr); return; }
1308 while (x >= y) {
1309 int last_y = y;
1310 error += y;
1311 ++y;
1312 error += y;
1313 hline(cx-x, cy+last_y, 2*x+1, clr);
1314 if (x != 0 && last_y != 0) hline(cx-x, cy-last_y, 2*x+1, clr);
1315 if (error >= 0) {
1316 if (x != last_y) {
1317 hline(cx-last_y, cy+x, 2*last_y+1, clr);
1318 if (last_y != 0 && x != 0) hline(cx-last_y, cy-x, 2*last_y+1, clr);
1320 error -= x;
1321 --x;
1322 error -= x;
1325 version(LDC) {} else atomicFence();
1326 ++vupcounter;
1331 void drawEllipse (int x0, int y0, int w, int h, VColor clr) @trusted {
1332 import std.math : abs;
1333 if (w == 0 && h == 0) return;
1334 if (w == 1) { vline(x0, y0, h, clr); return; }
1335 if (h == 1) { hline(x0, y0, w, clr); return; }
1336 int x1 = x0+w-1;
1337 int y1 = y0+h-1;
1338 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
1339 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
1340 long err = dx+dy+b1*a*a; // error of 1.step
1341 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
1342 if (y0 > y1) y0 = y1; // ...exchange them
1343 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
1344 a *= 8*a; b1 = 8*b*b;
1345 do {
1346 long e2;
1347 putPixelIntr(x1, y0, clr); // I. Quadrant
1348 putPixelIntr(x0, y0, clr); // II. Quadrant
1349 putPixelIntr(x0, y1, clr); // III. Quadrant
1350 putPixelIntr(x1, y1, clr); // IV. Quadrant
1351 e2 = 2*err;
1352 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
1353 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
1354 } while (x0 <= x1);
1355 while (y0-y1 < b) {
1356 // too early stop of flat ellipses a=1
1357 putPixelIntr(x0-1, ++y0, clr); // complete tip of ellipse
1358 putPixelIntr(x0-1, --y1, clr);
1360 version(LDC) {} else atomicFence();
1361 ++vupcounter;
1364 void fillEllipse (int x0, int y0, int w, int h, VColor clr) @trusted {
1365 import std.math : abs;
1366 if (w == 0 && h == 0) return;
1367 if (w == 1) { vline(x0, y0, h, clr); return; }
1368 if (h == 1) { hline(x0, y0, w, clr); return; }
1369 int x1 = x0+w-1;
1370 int y1 = y0+h-1;
1371 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
1372 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
1373 long err = dx+dy+b1*a*a; // error of 1.step
1374 int prev_y0 = -1, prev_y1 = -1;
1375 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
1376 if (y0 > y1) y0 = y1; // ...exchange them
1377 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
1378 a *= 8*a; b1 = 8*b*b;
1379 do {
1380 long e2;
1381 if (y0 != prev_y0) { hline(x0, y0, x1-x0+1, clr); prev_y0 = y0; }
1382 if (y1 != y0 && y1 != prev_y1) { hline(x0, y1, x1-x0+1, clr); prev_y1 = y1; }
1383 e2 = 2*err;
1384 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
1385 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
1386 } while (x0 <= x1);
1387 while (y0-y1 < b) {
1388 // too early stop of flat ellipses a=1
1389 putPixelIntr(x0-1, ++y0, clr); // complete tip of ellipse
1390 putPixelIntr(x0-1, --y1, clr);
1392 version(LDC) {} else atomicFence();
1393 ++vupcounter;
1397 // //////////////////////////////////////////////////////////////////////// //
1398 int charWidth(string type="msx") () {
1399 static if (type == "msx") return 6;
1400 else static if (type == "dos") return 8;
1401 else static if (type == "d10") return 10;
1402 else static assert(0, "invalid font type");
1405 int charHeight(string type="msx") () {
1406 static if (type == "msx") return 8;
1407 else static if (type == "dos") return 8;
1408 else static if (type == "d10") return 10;
1409 else static assert(0, "invalid font type");
1412 void drawCharWdt(string type="msx") (int x, int y, int wdt, int shift, char ch, VColor fgcol, VColor bgcol=Transparent) @trusted {
1413 static if (type == "msx") { alias fontb8 = kgiFont6; enum fwdt = 8; enum fhgt = 8; enum fmask = 0x80; }
1414 else static if (type == "dos") { alias fontb8 = kgiFont8; enum fwdt = 8; enum fhgt = 8; enum fmask = 0x80; }
1415 else static if (type == "d10") { alias fontb8 = kgiFont10; enum fwdt = 10; enum fhgt = 10; enum fmask = 0x8000; }
1416 else static assert(0, "invalid font type");
1417 usize pos = ch*fhgt;
1418 if (wdt < 1 || shift >= fwdt) return;
1419 if (fgcol == Transparent && bgcol == Transparent) return;
1420 if (wdt > fwdt) wdt = fwdt;
1421 if (shift < 0) shift = 0;
1422 foreach (immutable int dy; 0..fhgt) {
1423 ushort b = cast(ushort)(fontb8.ptr[pos++]<<shift);
1424 foreach (immutable int dx; 0..wdt) {
1425 VColor c = (b&fmask ? fgcol : bgcol);
1426 putPixelIntr(x+dx, y+dy, c);
1427 b <<= 1;
1430 version(LDC) {} else atomicFence();
1431 ++vupcounter;
1434 // outline types
1435 enum : ubyte {
1436 OutLeft = 0x01,
1437 OutRight = 0x02,
1438 OutUp = 0x04,
1439 OutDown = 0x08,
1440 OutLU = 0x10, // left-up
1441 OutRU = 0x20, // right-up
1442 OutLD = 0x40, // left-down
1443 OutRD = 0x80, // right-down
1444 OutAll = 0xff,
1447 void drawCharWdtOut(string type="msx") (int x, int y, int wdt, int shift, char ch, VColor fgcol, VColor outcol=Transparent, ubyte ot=0) @trusted {
1448 static if (type == "msx") { alias fontb8 = kgiFont6; enum fwdt = 8; enum fhgt = 8; enum fmask = 0x80; }
1449 else static if (type == "dos") { alias fontb8 = kgiFont8; enum fwdt = 8; enum fhgt = 8; enum fmask = 0x80; }
1450 else static if (type == "d10") { alias fontb8 = kgiFont10; enum fwdt = 10; enum fhgt = 10; enum fmask = 0x8000; }
1451 else static assert(0, "invalid font type");
1452 if (fgcol == Transparent && outcol == Transparent) return;
1453 if (ot == 0 || outcol == Transparent) {
1454 // no outline? simple draw
1455 drawCharWdt(x, y, wdt, shift, ch, fgcol, Transparent);
1456 return;
1458 usize pos = ch*fhgt;
1459 if (wdt < 1 || shift >= fwdt) return;
1460 if (wdt > 8) wdt = fwdt;
1461 if (shift < 0) shift = 0;
1462 ubyte[fhgt+2][fwdt+2] bmp = 0; // char bitmap; 0: empty; 1: char; 2: outline
1463 foreach (immutable dy; 1..fhgt+1) {
1464 ushort b = cast(ushort)(fontb8.ptr[pos++]<<shift);
1465 foreach (immutable dx; 1..wdt+1) {
1466 if (b&fmask) {
1467 // put pixel
1468 bmp[dy][dx] = 1;
1469 // put outlines
1470 if ((ot&OutUp) && bmp[dy-1][dx] == 0) bmp[dy-1][dx] = 2;
1471 if ((ot&OutDown) && bmp[dy+1][dx] == 0) bmp[dy+1][dx] = 2;
1472 if ((ot&OutLeft) && bmp[dy][dx-1] == 0) bmp[dy][dx-1] = 2;
1473 if ((ot&OutRight) && bmp[dy][dx+1] == 0) bmp[dy][dx+1] = 2;
1474 if ((ot&OutLU) && bmp[dy-1][dx-1] == 0) bmp[dy-1][dx-1] = 2;
1475 if ((ot&OutRU) && bmp[dy-1][dx+1] == 0) bmp[dy-1][dx+1] = 2;
1476 if ((ot&OutLD) && bmp[dy+1][dx-1] == 0) bmp[dy+1][dx-1] = 2;
1477 if ((ot&OutRD) && bmp[dy+1][dx+1] == 0) bmp[dy+1][dx+1] = 2;
1479 b <<= 1;
1482 // now draw it
1483 --x;
1484 --y;
1485 foreach (immutable int dy; 0..fhgt+2) {
1486 foreach (immutable int dx; 0..fwdt+2) {
1487 if (auto t = bmp[dy][dx]) putPixelIntr(x+dx, y+dy, (t == 1 ? fgcol : outcol));
1490 version(LDC) {} else atomicFence();
1491 ++vupcounter;
1494 void drawChar(string type="msx") (int x, int y, char ch, VColor fgcol, VColor bgcol=Transparent) @trusted {
1495 drawCharWdt!type(x, y, charWidth!type, 0, ch, fgcol, bgcol);
1498 void drawCharOut(string type="msx") (int x, int y, char ch, VColor fgcol, VColor outcol=Transparent, ubyte ot=OutAll) @trusted {
1499 drawCharWdtOut!type(x, y, charWidth!type, 0, ch, fgcol, outcol, ot);
1502 void drawStr(string type="msx") (int x, int y, const(char)[] str, VColor fgcol, VColor bgcol=Transparent) @trusted {
1503 foreach (immutable char ch; str) {
1504 drawChar!type(x, y, ch, fgcol, bgcol);
1505 x += charWidth!type;
1509 void drawStrOut(string type="msx") (int x, int y, const(char)[] str, VColor fgcol, VColor outcol=Transparent, ubyte ot=OutAll) @trusted {
1510 foreach (immutable char ch; str) {
1511 drawCharOut!type(x, y, ch, fgcol, outcol, ot);
1512 x += charWidth!type;
1516 int strWidth(string type="msx") (const(char)[] str) {
1517 return cast(int)str.length*charWidth!type;
1520 int charWidthProp(string type="msx") (char ch) @trusted pure {
1521 static if (type == "msx") { alias fontw8 = kgiFont6PropWidth; }
1522 else static if (type == "dos") { alias fontw8 = kgiFont8PropWidth; }
1523 else static assert(0, "invalid font type");
1524 return (fontw8.ptr[ch]&0x0f);
1527 int strWidthProp(string type="msx") (const(char)[] str) @trusted pure {
1528 static if (type == "msx") { alias fontw8 = kgiFont6PropWidth; }
1529 else static if (type == "dos") { alias fontw8 = kgiFont8PropWidth; }
1530 else static assert(0, "invalid font type");
1531 int wdt = 0;
1532 foreach (immutable char ch; str) wdt += (fontw8[ch]&0x0f)+1;
1533 if (wdt > 0) --wdt; // don't count last empty pixel
1534 return wdt;
1537 int drawCharProp(string type="msx") (int x, int y, char ch, VColor fgcol, VColor bgcol=Transparent) @trusted {
1538 static if (type == "msx") { alias fontw8 = kgiFont6PropWidth; }
1539 else static if (type == "dos") { alias fontw8 = kgiFont8PropWidth; }
1540 else static assert(0, "invalid font type");
1541 immutable int wdt = (fontw8[ch]&0x0f);
1542 drawCharWdt!type(x, y, wdt, fontw8[ch]>>4, ch, fgcol, bgcol);
1543 return wdt;
1546 int drawCharPropOut(string type="msx") (int x, int y, char ch, VColor fgcol, VColor outcol=Transparent, ubyte ot=OutAll) @trusted {
1547 static if (type == "msx") { alias fontw8 = kgiFont6PropWidth; }
1548 else static if (type == "dos") { alias fontw8 = kgiFont8PropWidth; }
1549 else static assert(0, "invalid font type");
1550 immutable int wdt = (fontw8[ch]&0x0f);
1551 drawCharWdtOut!type(x, y, wdt, fontw8[ch]>>4, ch, fgcol, outcol, ot);
1552 return wdt;
1555 int drawStrProp(string type="msx") (int x, int y, const(char)[] str, VColor fgcol, VColor bgcol=Transparent) @trusted {
1556 bool vline = false;
1557 int sx = x;
1558 foreach (immutable char ch; str) {
1559 if (vline) {
1560 if (!isTransparent(bgcol)) {
1561 foreach (int dy; 0..8) putPixelIntr(x, y+dy, bgcol);
1562 // no need to advance vupcounter, 'cause `drawCharProp` will do it
1563 //version(LDC) {} else atomicFence();
1564 //++vupcounter;
1566 ++x;
1568 vline = true;
1569 x += drawCharProp!type(x, y, ch, fgcol, bgcol);
1571 return x-sx;
1574 int drawStrPropOut(string type="msx") (int x, int y, const(char)[] str, VColor fgcol, VColor outcol=Transparent, ubyte ot=OutAll) @trusted {
1575 int sx = x;
1576 foreach (immutable char ch; str) {
1577 x += drawCharPropOut!type(x, y, ch, fgcol, outcol, ot)+1;
1579 if (x > sx) --x; // don't count last empty pixel
1580 return x-sx;
1584 // ////////////////////////////////////////////////////////////////////////// //
1585 /** floodfill area; based on Tarry's maze algorithm.
1587 * fill area with color/pattern. trades memory for speed: doesn't recurse, doesn't allocate.
1588 * will use "transparency" byte as temporary, and will leave it dirty.
1590 * NOTES:
1591 * neither `isBorder` nor `patColor` will be called with out-of-range coordinates.
1593 void floodFillEx (int x, int y, scope bool delegate (int x, int y) nothrow @nogc isBorder, scope VColor delegate (int x, int y) nothrow @nogc patColor) nothrow @trusted @nogc {
1594 enum : ubyte {
1595 DirMask = 0x03,
1596 Seed = 0x10,
1597 Scanned = 0x80,
1600 static ubyte getmark (int x, int y) nothrow @trusted @nogc {
1601 pragma(inline, true);
1602 return cast(ubyte)(x >= 0 && y >= 0 && x < vbufW && y < vbufH ? vbuf[y*vbufW+x]>>vlAShift : Scanned);
1605 static void setmark (int x, int y, ubyte mark) nothrow @trusted @nogc {
1606 pragma(inline, true);
1607 if (x >= 0 && y >= 0 && x < vbufW && y < vbufH) vbuf[y*vbufW+x] = (vbuf[y*vbufW+x]&vlColorMask)|(mark<<vlAShift);
1610 if (x < 0 || y < 0 || x >= vbufW || y >= vbufH) return; // nothing to do
1611 if (isBorder(x, y)) return; // nothing to do
1613 //setPixel(x, y, getPixel(x, y)); // set update flag
1615 // one can mark bounding rectangle with Scanned
1616 // reset flags
1617 auto p = vbuf;
1618 foreach (immutable dy; 0..vbufH) {
1619 foreach (immutable dx; 0..vbufW) {
1620 *p &= vlColorMask; // "not visited" mark
1621 ++p;
1625 //setmark(x, y, Scanned|Fill|Seed);
1626 VColor pc = patColor(x, y);
1627 if (isOpaque(pc)) {
1628 vbuf[y*vbufW+x] = (pc&vlColorMask)|((Scanned|Seed)<<vlAShift);
1629 } else {
1630 // do alpha
1631 putPixelIntrNoCheck(x, y, pc);
1632 setmark(x, y, Scanned|Seed);
1635 ubyte dir = 0; // direction: right, left, up, down
1636 for (;;) {
1637 x += (dir == 0 ? 1 : dir == 1 ? -1 : 0);
1638 y += (dir == 3 ? 1 : dir == 2 ? -1 : 0);
1639 auto mk = getmark(x, y);
1640 if (mk == 0) {
1641 // not yet visited, check for border
1642 if (isBorder(x, y)) {
1643 mk = Scanned;
1644 setmark(x, y, Scanned);
1647 if ((mk&Scanned) == 0) {
1648 // not scanned
1649 //setmark(x, y, Scanned|Fill|dir);
1650 pc = patColor(x, y);
1651 if (isOpaque(pc)) {
1652 vbuf[y*vbufW+x] = (pc&vlColorMask)|((Scanned|dir)<<vlAShift);
1653 } else {
1654 // do alpha
1655 putPixelIntrNoCheck(x, y, pc);
1656 setmark(x, y, Scanned|dir);
1658 if (dir != 1) dir = 0; // make exit direction
1659 } else {
1660 // already scanned
1661 for (;;) {
1662 x -= (dir == 0 ? 1 : dir == 1 ? -1 : 0);
1663 y -= (dir == 3 ? 1 : dir == 2 ? -1 : 0);
1664 mk = getmark(x, y);
1665 if (mk&Seed) {
1666 // done, fill pixels (you can set Fill flag and check all pixels here)
1667 if ((mk&DirMask) == 3) {
1668 // set update flag
1669 version(LDC) {} else atomicFence();
1670 ++vupcounter;
1671 return;
1673 // remember new dir
1674 ++mk;
1675 setmark(x, y, mk);
1676 dir = mk&DirMask;
1677 break; // next pixel
1679 ++dir;
1680 if ((mk&DirMask) == (dir^1)) ++dir; // skip entry-direction
1681 if (dir <= 3) break; // next pixel
1682 dir = mk&DirMask;
1687 } // @nogc
1690 // ////////////////////////////////////////////////////////////////////////// //
1691 // fonts
1692 static public immutable ubyte[256*8] kgiFont6 = [
1693 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x3c,0x7e,0xdb,0xff,0xff,0xdb,0x66,0x3c,0x6c,0xfe,
1694 0xfe,0xfe,0x7c,0x38,0x10,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x10,0x38,0x54,0xfe,0x54,0x10,0x38,0x00,0x10,0x38,0x7c,0xfe,
1695 0xfe,0x10,0x38,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0xff,0xff,0xff,0xe7,0xe7,0xff,0xff,0xff,0x38,0x44,0x82,0x82,0x82,0x44,
1696 0x38,0x00,0xc7,0xbb,0x7d,0x7d,0x7d,0xbb,0xc7,0xff,0x0f,0x03,0x05,0x79,0x88,0x88,0x88,0x70,0x38,0x44,0x44,0x44,0x38,0x10,0x7c,0x10,
1697 0x30,0x28,0x24,0x24,0x28,0x20,0xe0,0xc0,0x3c,0x24,0x3c,0x24,0x24,0xe4,0xdc,0x18,0x10,0x54,0x38,0xee,0x38,0x54,0x10,0x00,0x10,0x10,
1698 0x10,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xf0,
1699 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xff,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
1700 0x10,0x10,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0xf0,0x10,0x10,0x10,0x10,
1701 0x10,0x10,0x10,0x1f,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0xf0,0x00,0x00,0x00,0x00,0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81,0x01,0x02,
1702 0x04,0x08,0x10,0x20,0x40,0x80,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x00,0x10,0x10,0xff,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
1703 0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x00,0x00,0x20,0x00,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x50,0x50,0xf8,0x50,0xf8,0x50,
1704 0x50,0x00,0x20,0x78,0xa0,0x70,0x28,0xf0,0x20,0x00,0xc0,0xc8,0x10,0x20,0x40,0x98,0x18,0x00,0x40,0xa0,0x40,0xa8,0x90,0x98,0x60,0x00,
1705 0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00,0x88,0x50,
1706 0x20,0xf8,0x20,0x50,0x88,0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x40,0x00,0x00,0x00,0x78,
1707 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x70,0x88,0x98,0xa8,0xc8,0x88,
1708 0x70,0x00,0x20,0x60,0xa0,0x20,0x20,0x20,0xf8,0x00,0x70,0x88,0x08,0x10,0x60,0x80,0xf8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
1709 0x10,0x30,0x50,0x90,0xf8,0x10,0x10,0x00,0xf8,0x80,0xe0,0x10,0x08,0x10,0xe0,0x00,0x30,0x40,0x80,0xf0,0x88,0x88,0x70,0x00,0xf8,0x88,
1710 0x10,0x20,0x20,0x20,0x20,0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x78,0x08,0x10,0x60,0x00,0x00,0x00,0x20,0x00,
1711 0x00,0x20,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x20,0x40,0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x00,0x00,0x00,0xf8,0x00,0xf8,0x00,
1712 0x00,0x00,0xc0,0x60,0x30,0x18,0x30,0x60,0xc0,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,0x70,0x88,0x08,0x68,0xa8,0xa8,0x70,0x00,
1713 0x20,0x50,0x88,0x88,0xf8,0x88,0x88,0x00,0xf0,0x48,0x48,0x70,0x48,0x48,0xf0,0x00,0x30,0x48,0x80,0x80,0x80,0x48,0x30,0x00,0xe0,0x50,
1714 0x48,0x48,0x48,0x50,0xe0,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0xb8,
1715 0x88,0x88,0x70,0x00,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x38,0x10,0x10,0x10,0x90,0x90,
1716 0x60,0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00,0x88,0xd8,0xa8,0xa8,0x88,0x88,0x88,0x00,
1717 0x88,0xc8,0xc8,0xa8,0x98,0x98,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,
1718 0x88,0x88,0xa8,0x90,0x68,0x00,0xf0,0x88,0x88,0xf0,0xa0,0x90,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,0xf8,0x20,0x20,0x20,
1719 0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x00,0x88,0x88,0x88,0xa8,0xa8,0xd8,
1720 0x88,0x00,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00,0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00,
1721 0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00,0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00,0x20,0x50,
1722 0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00,0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x08,
1723 0x78,0x88,0x78,0x00,0x80,0x80,0xb0,0xc8,0x88,0xc8,0xb0,0x00,0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00,0x08,0x08,0x68,0x98,0x88,0x98,
1724 0x68,0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x70,0x00,0x10,0x28,0x20,0xf8,0x20,0x20,0x20,0x00,0x00,0x00,0x68,0x98,0x98,0x68,0x08,0x70,
1725 0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x90,0x60,0x40,0x40,
1726 0x48,0x50,0x60,0x50,0x48,0x00,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x00,0xd0,0xa8,0xa8,0xa8,0xa8,0x00,0x00,0x00,0xb0,0xc8,
1727 0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x00,0x00,0xb0,0xc8,0xc8,0xb0,0x80,0x80,0x00,0x00,0x68,0x98,0x98,0x68,
1728 0x08,0x08,0x00,0x00,0xb0,0xc8,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0xf0,0x08,0xf0,0x00,0x40,0x40,0xf0,0x40,0x40,0x48,0x30,0x00,
1729 0x00,0x00,0x90,0x90,0x90,0x90,0x68,0x00,0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00,0x00,0x88,0xa8,0xa8,0xa8,0x50,0x00,0x00,0x00,
1730 0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x98,0x68,0x08,0x70,0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00,0x18,0x20,0x20,0x40,
1731 0x20,0x20,0x18,0x00,0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00,0xc0,0x20,0x20,0x10,0x20,0x20,0xc0,0x00,0x40,0xa8,0x10,0x00,0x00,0x00,
1732 0x00,0x00,0x00,0x00,0x20,0x50,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,
1733 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x00,0x00,0x00,0xff,0xff,
1734 0xff,0xff,0xff,0xff,0x00,0x00,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xfc,0xfc,0xfc,0xfc,
1735 0xfc,0xfc,0xfc,0xfc,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x11,0x22,0x44,0x88,0x11,0x22,
1736 0x44,0x88,0x88,0x44,0x22,0x11,0x88,0x44,0x22,0x11,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,
1737 0x80,0xc0,0xe0,0xf0,0xe0,0xc0,0x80,0x00,0x01,0x03,0x07,0x0f,0x07,0x03,0x01,0x00,0xff,0x7e,0x3c,0x18,0x18,0x3c,0x7e,0xff,0x81,0xc3,
1738 0xe7,0xff,0xff,0xe7,0xc3,0x81,0xf0,0xf0,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
1739 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0xf0,0xf0,0x33,0x33,0xcc,0xcc,0x33,0x33,0xcc,0xcc,0x00,0x20,0x20,0x50,0x50,0x88,
1740 0xf8,0x00,0x20,0x20,0x70,0x20,0x70,0x20,0x20,0x00,0x00,0x00,0x00,0x50,0x88,0xa8,0x50,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1741 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xff,0xff,
1742 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x68,0x90,0x90,0x90,0x68,0x00,0x30,0x48,0x48,0x70,0x48,0x48,0x70,0xc0,0xf8,0x88,0x80,0x80,
1743 0x80,0x80,0x80,0x00,0x00,0x50,0x70,0x88,0xf8,0x80,0x70,0x00,0x00,0x00,0x78,0x80,0xf0,0x80,0x78,0x00,0x00,0x00,0x78,0x90,0x90,0x90,
1744 0x60,0x00,0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x50,0x00,0x70,0x20,0x20,0x20,0x70,0x00,0xf8,0x20,0x70,0xa8,0xa8,0x70,0x20,0xf8,
1745 0x20,0x50,0x88,0xf8,0x88,0x50,0x20,0x00,0x70,0x88,0x88,0x88,0x50,0x50,0xd8,0x00,0x30,0x40,0x40,0x20,0x50,0x50,0x50,0x20,0x00,0x00,
1746 0x00,0x50,0xa8,0xa8,0x50,0x00,0x08,0x70,0xa8,0xa8,0xa8,0x70,0x80,0x00,0x38,0x40,0x80,0xf8,0x80,0x40,0x38,0x00,0x70,0x88,0x88,0x88,
1747 0x88,0x88,0x88,0x00,0x00,0xf8,0x00,0xf8,0x00,0xf8,0x00,0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0xf8,0x00,0xc0,0x30,0x08,0x30,0xc0,0x00,
1748 0xf8,0x00,0x50,0xf8,0x80,0xf0,0x80,0x80,0xf8,0x00,0x78,0x80,0x80,0xf0,0x80,0x80,0x78,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0xa0,0x40,
1749 0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x50,0x70,0x20,0x20,0x20,0x20,0x70,0x00,0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00,0x00,0x30,
1750 0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3e,0x20,0x20,0x20,0xa0,0x60,0x20,0x00,0xa0,0x50,0x50,0x50,
1751 0x00,0x00,0x00,0x00,0x40,0xa0,0x20,0x40,0xe0,0x00,0x00,0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x00,0x3c,0x42,0x99,0xa1,0xa1,0x99,
1752 0x42,0x3c,0x00,0x00,0x90,0xa8,0xe8,0xa8,0x90,0x00,0x00,0x00,0x60,0x10,0x70,0x90,0x68,0x00,0x00,0x00,0xf0,0x80,0xf0,0x88,0xf0,0x00,
1753 0x00,0x00,0x90,0x90,0x90,0xf8,0x08,0x00,0x00,0x00,0x30,0x50,0x50,0x70,0x88,0x00,0x00,0x00,0x70,0x88,0xf8,0x80,0x70,0x00,0x00,0x20,
1754 0x70,0xa8,0xa8,0x70,0x20,0x00,0x00,0x00,0x78,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x98,
1755 0xa8,0xc8,0x88,0x00,0x00,0x50,0x20,0x00,0x98,0xa8,0xc8,0x00,0x00,0x00,0x90,0xa0,0xc0,0xa0,0x90,0x00,0x00,0x00,0x38,0x28,0x28,0x48,
1756 0x88,0x00,0x00,0x00,0x88,0xd8,0xa8,0x88,0x88,0x00,0x00,0x00,0x88,0x88,0xf8,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
1757 0x00,0x00,0x78,0x48,0x48,0x48,0x48,0x00,0x00,0x00,0x78,0x88,0x78,0x28,0x48,0x00,0x00,0x00,0xf0,0x88,0xf0,0x80,0x80,0x00,0x00,0x00,
1758 0x78,0x80,0x80,0x80,0x78,0x00,0x00,0x00,0xf8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x88,0x50,0x20,0x40,0x80,0x00,0x00,0x00,0xa8,0x70,
1759 0x20,0x70,0xa8,0x00,0x00,0x00,0xf0,0x48,0x70,0x48,0xf0,0x00,0x00,0x00,0x40,0x40,0x70,0x48,0x70,0x00,0x00,0x00,0x88,0x88,0xc8,0xa8,
1760 0xc8,0x00,0x00,0x00,0xf0,0x08,0x70,0x08,0xf0,0x00,0x00,0x00,0xa8,0xa8,0xa8,0xa8,0xf8,0x00,0x00,0x00,0x70,0x88,0x38,0x88,0x70,0x00,
1761 0x00,0x00,0xa8,0xa8,0xa8,0xf8,0x08,0x00,0x00,0x00,0x48,0x48,0x78,0x08,0x08,0x00,0x00,0x00,0xc0,0x40,0x70,0x48,0x70,0x00,0x90,0xa8,
1762 0xa8,0xe8,0xa8,0xa8,0x90,0x00,0x20,0x50,0x88,0x88,0xf8,0x88,0x88,0x00,0xf8,0x88,0x80,0xf0,0x88,0x88,0xf0,0x00,0x90,0x90,0x90,0x90,
1763 0x90,0xf8,0x08,0x00,0x38,0x28,0x28,0x48,0x48,0xf8,0x88,0x00,0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00,0x20,0x70,0xa8,0xa8,0xa8,0x70,
1764 0x20,0x00,0xf8,0x88,0x88,0x80,0x80,0x80,0x80,0x00,0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x98,0xa8,0xc8,0x88,0x88,0x00,
1765 0x50,0x20,0x88,0x98,0xa8,0xc8,0x88,0x00,0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00,0x18,0x28,0x48,0x48,0x48,0x48,0x88,0x00,0x88,0xd8,
1766 0xa8,0xa8,0x88,0x88,0x88,0x00,0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0xf8,0x88,0x88,0x88,
1767 0x88,0x88,0x88,0x00,0x78,0x88,0x88,0x78,0x28,0x48,0x88,0x00,0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0x80,0x80,0x88,
1768 0x70,0x00,0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x50,0x20,0x40,0x80,0x00,0xa8,0xa8,0x70,0x20,0x70,0xa8,0xa8,0x00,
1769 0xf0,0x48,0x48,0x70,0x48,0x48,0xf0,0x00,0x80,0x80,0x80,0xf0,0x88,0x88,0xf0,0x00,0x88,0x88,0x88,0xc8,0xa8,0xa8,0xc8,0x00,0xf0,0x08,
1770 0x08,0x30,0x08,0x08,0xf0,0x00,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xf8,0x00,0x70,0x88,0x08,0x78,0x08,0x88,0x70,0x00,0xa8,0xa8,0xa8,0xa8,
1771 0xa8,0xf8,0x08,0x00,0x88,0x88,0x88,0x88,0x78,0x08,0x08,0x00,0xc0,0x40,0x40,0x70,0x48,0x48,0x70,0x00,
1774 static public immutable ubyte[256*8] kgiFont8 = [
1775 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e,0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e,0x6c,0xfe,
1776 0xfe,0xfe,0x7c,0x38,0x10,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x38,0x7c,0x38,0xfe,0xfe,0xd6,0x10,0x38,0x10,0x10,0x38,0x7c,
1777 0xfe,0x7c,0x10,0x38,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,
1778 0x3c,0x00,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0x0f,0x07,0x0f,0x7d,0xcc,0xcc,0xcc,0x78,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,
1779 0x3f,0x33,0x3f,0x30,0x30,0x70,0xf0,0xe0,0x7f,0x63,0x7f,0x63,0x63,0x67,0xe6,0xc0,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,0x80,0xe0,
1780 0xf8,0xfe,0xf8,0xe0,0x80,0x00,0x02,0x0e,0x3e,0xfe,0x3e,0x0e,0x02,0x00,0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18,0x66,0x66,0x66,0x66,
1781 0x66,0x00,0x66,0x00,0x7f,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x00,0x7e,0xc3,0x78,0xcc,0xcc,0x78,0x8c,0xf8,0x00,0x00,0x00,0x00,0x7e,0x7e,
1782 0x7e,0x00,0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,
1783 0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,0x00,0x24,
1784 0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
1785 0x00,0x00,0x00,0x00,0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,
1786 0x6c,0x00,0x30,0x7c,0xc0,0x78,0x0c,0xf8,0x30,0x00,0x00,0xc6,0xcc,0x18,0x30,0x66,0xc6,0x00,0x38,0x6c,0x38,0x76,0xdc,0xcc,0x76,0x00,
1787 0x60,0x60,0xc0,0x00,0x00,0x00,0x00,0x00,0x18,0x30,0x60,0x60,0x60,0x30,0x18,0x00,0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00,0x00,0x66,
1788 0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x30,0x60,0x00,0x00,0x00,0xfc,
1789 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,0x78,0xcc,0xdc,0xfc,0xec,0xcc,
1790 0x78,0x00,0x30,0xf0,0x30,0x30,0x30,0x30,0xfc,0x00,0x78,0xcc,0x0c,0x38,0x60,0xcc,0xfc,0x00,0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78,0x00,
1791 0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x0c,0x00,0xfc,0xc0,0xf8,0x0c,0x0c,0xcc,0x78,0x00,0x38,0x60,0xc0,0xf8,0xcc,0xcc,0x78,0x00,0xfc,0xcc,
1792 0x0c,0x18,0x30,0x60,0x60,0x00,0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78,0x00,0x78,0xcc,0xcc,0x7c,0x0c,0x18,0x70,0x00,0x00,0x00,0x30,0x30,
1793 0x00,0x30,0x30,0x00,0x00,0x00,0x30,0x30,0x00,0x70,0x30,0x60,0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x00,0x00,0x00,0xfc,0x00,0xfc,0x00,
1794 0x00,0x00,0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,0x78,0xcc,0x0c,0x18,0x30,0x00,0x30,0x00,0x7c,0xc6,0xde,0xde,0xde,0xc0,0x78,0x00,
1795 0x30,0x78,0xcc,0xcc,0xfc,0xcc,0xcc,0x00,0xfc,0x66,0x66,0x7c,0x66,0x66,0xfc,0x00,0x3c,0x66,0xc0,0xc0,0xc0,0x66,0x3c,0x00,0xfc,0x6c,
1796 0x66,0x66,0x66,0x6c,0xfc,0x00,0xfe,0x62,0x68,0x78,0x68,0x62,0xfe,0x00,0xfe,0x62,0x68,0x78,0x68,0x60,0xf0,0x00,0x3c,0x66,0xc0,0xc0,
1797 0xce,0x66,0x3e,0x00,0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x1e,0x0c,0x0c,0x0c,0xcc,0xcc,
1798 0x78,0x00,0xe6,0x66,0x6c,0x78,0x6c,0x66,0xe6,0x00,0xf0,0x60,0x60,0x60,0x62,0x66,0xfe,0x00,0xc6,0xee,0xfe,0xd6,0xc6,0xc6,0xc6,0x00,
1799 0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0x00,0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x38,0x00,0xfc,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00,0x78,0xcc,
1800 0xcc,0xcc,0xdc,0x78,0x1c,0x00,0xfc,0x66,0x66,0x7c,0x78,0x6c,0xe6,0x00,0x78,0xcc,0xe0,0x38,0x1c,0xcc,0x78,0x00,0xfc,0xb4,0x30,0x30,
1801 0x30,0x30,0x78,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xfc,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00,0xc6,0xc6,0xc6,0xd6,0xfe,0xee,
1802 0xc6,0x00,0xc6,0xc6,0x6c,0x38,0x6c,0xc6,0xc6,0x00,0xcc,0xcc,0xcc,0x78,0x30,0x30,0x78,0x00,0xfe,0xcc,0x98,0x30,0x62,0xc6,0xfe,0x00,
1803 0x78,0x60,0x60,0x60,0x60,0x60,0x78,0x00,0xc0,0x60,0x30,0x18,0x0c,0x06,0x02,0x00,0x78,0x18,0x18,0x18,0x18,0x18,0x78,0x00,0x10,0x38,
1804 0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x0c,
1805 0x7c,0xcc,0x76,0x00,0xe0,0x60,0x7c,0x66,0x66,0x66,0xbc,0x00,0x00,0x00,0x78,0xcc,0xc0,0xcc,0x78,0x00,0x1c,0x0c,0x0c,0x7c,0xcc,0xcc,
1806 0x76,0x00,0x00,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,0x38,0x6c,0x60,0xf0,0x60,0x60,0xf0,0x00,0x00,0x00,0x76,0xcc,0xcc,0x7c,0x0c,0xf8,
1807 0xe0,0x60,0x6c,0x76,0x66,0x66,0xe6,0x00,0x30,0x00,0x70,0x30,0x30,0x30,0x78,0x00,0x18,0x00,0x78,0x18,0x18,0x18,0xd8,0x70,0xe0,0x60,
1808 0x66,0x6c,0x78,0x6c,0xe6,0x00,0x70,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0xec,0xfe,0xd6,0xc6,0xc6,0x00,0x00,0x00,0xf8,0xcc,
1809 0xcc,0xcc,0xcc,0x00,0x00,0x00,0x78,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0xdc,0x66,0x66,0x7c,0x60,0xf0,0x00,0x00,0x76,0xcc,0xcc,0x7c,
1810 0x0c,0x1e,0x00,0x00,0xd8,0x6c,0x6c,0x60,0xf0,0x00,0x00,0x00,0x7c,0xc0,0x78,0x0c,0xf8,0x00,0x10,0x30,0x7c,0x30,0x30,0x34,0x18,0x00,
1811 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0xcc,0xcc,0xcc,0x78,0x30,0x00,0x00,0x00,0xc6,0xc6,0xd6,0xfe,0x6c,0x00,0x00,0x00,
1812 0xc6,0x6c,0x38,0x6c,0xc6,0x00,0x00,0x00,0xcc,0xcc,0xcc,0x7c,0x0c,0xf8,0x00,0x00,0xfc,0x98,0x30,0x64,0xfc,0x00,0x1c,0x30,0x30,0xe0,
1813 0x30,0x30,0x1c,0x00,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0xe0,0x30,0x30,0x1c,0x30,0x30,0xe0,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,
1814 0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xc6,0xfe,0x00,0x78,0xcc,0xc0,0xcc,0x78,0x18,0x0c,0x78,0x00,0xcc,0x00,0xcc,0xcc,0xcc,0x7e,0x00,
1815 0x1c,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,0x7e,0xc3,0x3c,0x06,0x3e,0x66,0x3f,0x00,0xcc,0x00,0x78,0x0c,0x7c,0xcc,0x7e,0x00,0xe0,0x00,
1816 0x78,0x0c,0x7c,0xcc,0x7e,0x00,0x30,0x30,0x78,0x0c,0x7c,0xcc,0x7e,0x00,0x00,0x00,0x7c,0xc0,0xc0,0x7c,0x06,0x3c,0x7e,0xc3,0x3c,0x66,
1817 0x7e,0x60,0x3c,0x00,0xcc,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,0xe0,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00,0xcc,0x00,0x70,0x30,0x30,0x30,
1818 0x78,0x00,0x7c,0xc6,0x38,0x18,0x18,0x18,0x3c,0x00,0xe0,0x00,0x70,0x30,0x30,0x30,0x78,0x00,0xcc,0x30,0x78,0xcc,0xcc,0xfc,0xcc,0x00,
1819 0x30,0x30,0x00,0x78,0xcc,0xfc,0xcc,0x00,0x1c,0x00,0xfc,0x60,0x78,0x60,0xfc,0x00,0x00,0x00,0x7f,0x0c,0x7f,0xcc,0x7f,0x00,0x3e,0x6c,
1820 0xcc,0xfe,0xcc,0xcc,0xce,0x00,0x78,0xcc,0x00,0x78,0xcc,0xcc,0x78,0x00,0x00,0xcc,0x00,0x78,0xcc,0xcc,0x78,0x00,0x00,0xe0,0x00,0x78,
1821 0xcc,0xcc,0x78,0x00,0x78,0xcc,0x00,0xcc,0xcc,0xcc,0x7e,0x00,0x00,0xe0,0x00,0xcc,0xcc,0xcc,0x7e,0x00,0x00,0xcc,0x00,0xcc,0xcc,0xfc,
1822 0x0c,0xf8,0xc6,0x38,0x7c,0xc6,0xc6,0x7c,0x38,0x00,0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x18,0x18,0x7e,0xc0,0xc0,0x7e,0x18,0x18,
1823 0x38,0x6c,0x64,0xf0,0x60,0xe6,0xfc,0x00,0xcc,0xcc,0x78,0xfc,0x30,0xfc,0x30,0x00,0xf0,0xd8,0xd8,0xf4,0xcc,0xde,0xcc,0x0e,0x0e,0x1b,
1824 0x18,0x7e,0x18,0x18,0xd8,0x70,0x1c,0x00,0x78,0x0c,0x7c,0xcc,0x7e,0x00,0x38,0x00,0x70,0x30,0x30,0x30,0x78,0x00,0x00,0x1c,0x00,0x78,
1825 0xcc,0xcc,0x78,0x00,0x00,0x1c,0x00,0xcc,0xcc,0xcc,0x7e,0x00,0x00,0xf8,0x00,0xf8,0xcc,0xcc,0xcc,0x00,0xfc,0x00,0xcc,0xec,0xfc,0xdc,
1826 0xcc,0x00,0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00,0x3c,0x66,0x66,0x3c,0x00,0x7e,0x00,0x00,0x30,0x00,0x30,0x60,0xc0,0xcc,0x78,0x00,
1827 0x00,0x00,0x00,0xfc,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,0xfc,0x0c,0x0c,0x00,0x00,0xc6,0xcc,0xd8,0x3e,0x63,0xce,0x98,0x1f,0xc6,0xcc,
1828 0xd8,0xf3,0x67,0xcf,0x9f,0x03,0x00,0x18,0x00,0x18,0x18,0x3c,0x3c,0x18,0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00,0x00,0xcc,0x66,0x33,
1829 0x66,0xcc,0x00,0x00,0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0xdc,0x76,0xdc,0x76,0xdc,0x76,
1830 0xdc,0x76,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,
1831 0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x36,0x36,
1832 0xf6,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0xf6,0x06,
1833 0xfe,0x00,0x00,0x00,0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,
1834 0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,
1835 0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,
1836 0x1f,0x18,0x1f,0x18,0x18,0x18,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x3f,0x30,
1837 0x37,0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,
1838 0x36,0x36,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,
1839 0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,0x36,0x36,
1840 0x36,0x36,0x3f,0x00,0x00,0x00,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
1841 0x3f,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xf7,0x36,0x36,0x36,0x18,0x18,0xff,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,
1842 0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
1843 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,
1844 0x76,0xdc,0xc8,0xdc,0x76,0x00,0x00,0x78,0xcc,0xf8,0xcc,0xf8,0xc0,0xc0,0x00,0xfe,0xc6,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0xfe,0x6c,0x6c,
1845 0x6c,0x6c,0x6c,0x00,0xfe,0x66,0x30,0x18,0x30,0x66,0xfe,0x00,0x00,0x00,0x7e,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x66,0x66,0x66,0x66,0x7c,
1846 0x60,0xc0,0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x00,0xfc,0x30,0x78,0xcc,0xcc,0x78,0x30,0xfc,0x38,0x6c,0xc6,0xfe,0xc6,0x6c,0x38,0x00,
1847 0x38,0x6c,0xc6,0xc6,0x6c,0x6c,0xee,0x00,0x1c,0x30,0x18,0x7c,0xcc,0xcc,0x78,0x00,0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00,0x06,0x0c,
1848 0x7e,0xdb,0xdb,0x7e,0x60,0xc0,0x3c,0x60,0xc0,0xfc,0xc0,0x60,0x3c,0x00,0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x00,0x00,0xfc,0x00,0xfc,
1849 0x00,0xfc,0x00,0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0xfc,0x00,0x60,0x30,0x18,0x30,0x60,0x00,0xfc,0x00,0x18,0x30,0x60,0x30,0x18,0x00,
1850 0xfc,0x00,0x0e,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70,0x30,0x30,0x00,0xfc,0x00,0x30,0x30,0x00,
1851 0x00,0x72,0x9c,0x00,0x72,0x9c,0x00,0x00,0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
1852 0x00,0x00,0x18,0x00,0x00,0x00,0x0f,0x0c,0x0c,0x0c,0xec,0x6c,0x3c,0x1c,0x78,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x78,0x0c,0x38,0x60,
1853 0x7c,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1856 static public immutable ushort[256*10] kgiFont10 = [
1857 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3f00,0x4080,0x5280,0x4080,0x5e80,0x4c80,0x2100,0x1e00,
1858 0x0000,0x0000,0x3f00,0x7f80,0x6d80,0x7f80,0x6180,0x7380,0x3f00,0x1e00,0x0000,0x0000,0x3b80,0x7fc0,0x7fc0,0x7fc0,0x3f80,0x1f00,0x0e00,
1859 0x0400,0x0000,0x0400,0x0e00,0x1f00,0x3f80,0x7fc0,0x3f80,0x1f00,0x0e00,0x0400,0x0000,0x0000,0x0e00,0x1f00,0x0e00,0x3f80,0x7fc0,0x3580,
1860 0x0400,0x0e00,0x0000,0x0400,0x0e00,0x1f00,0x3f80,0x7fc0,0x7fc0,0x3580,0x0400,0x0e00,0x0000,0x0000,0x0000,0x0000,0x0c00,0x1e00,0x1e00,
1861 0x0c00,0x0000,0x0000,0x0000,0xffc0,0xffc0,0xffc0,0xf3c0,0xe1c0,0xe1c0,0xf3c0,0xffc0,0xffc0,0xffc0,0x0000,0x0000,0x1e00,0x3300,0x2100,
1862 0x2100,0x3300,0x1e00,0x0000,0x0000,0xffc0,0xffc0,0xe1c0,0xccc0,0xdec0,0xdec0,0xccc0,0xe1c0,0xffc0,0xffc0,0x0000,0x0780,0x0380,0x0780,
1863 0x3e80,0x6600,0x6600,0x6600,0x3c00,0x0000,0x0000,0x1e00,0x3300,0x3300,0x3300,0x1e00,0x0c00,0x3f00,0x0c00,0x0000,0x0400,0x0600,0x0700,
1864 0x0500,0x0500,0x0400,0x1c00,0x3c00,0x1800,0x0000,0x0000,0x1f80,0x1f80,0x1080,0x1080,0x1180,0x3380,0x7100,0x2000,0x0000,0x0000,0x0c00,
1865 0x6d80,0x1e00,0x7380,0x7380,0x1e00,0x6d80,0x0c00,0x0000,0x1000,0x1800,0x1c00,0x1e00,0x1f00,0x1e00,0x1c00,0x1800,0x1000,0x0000,0x0100,
1866 0x0300,0x0700,0x0f00,0x1f00,0x0f00,0x0700,0x0300,0x0100,0x0000,0x0000,0x0c00,0x1e00,0x3f00,0x0c00,0x0c00,0x3f00,0x1e00,0x0c00,0x0000,
1867 0x0000,0x3300,0x3300,0x3300,0x3300,0x3300,0x0000,0x3300,0x0000,0x0000,0x0000,0x3f80,0x6d80,0x6d80,0x3d80,0x0d80,0x0d80,0x0d80,0x0000,
1868 0x0000,0x0000,0x1f00,0x3000,0x1f00,0x3180,0x1f00,0x0180,0x1f00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7f80,0x7f80,0x7f80,
1869 0x0000,0x0000,0x0000,0x0c00,0x1e00,0x3f00,0x0c00,0x0c00,0x3f00,0x1e00,0x0c00,0xffc0,0x0000,0x0c00,0x1e00,0x3f00,0x0c00,0x0c00,0x0c00,
1870 0x0c00,0x0c00,0x0000,0x0000,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x3f00,0x1e00,0x0c00,0x0000,0x0000,0x0000,0x0600,0x0300,0x7f80,0x0300,
1871 0x0600,0x0000,0x0000,0x0000,0x0000,0x0000,0x1800,0x3000,0x7f80,0x3000,0x1800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,
1872 0x6000,0x6000,0x7f80,0x0000,0x0000,0x0000,0x0000,0x1100,0x3180,0x7fc0,0x3180,0x1100,0x0000,0x0000,0x0000,0x0000,0x0000,0x0400,0x0e00,
1873 0x1f00,0x3f80,0x7fc0,0x0000,0x0000,0x0000,0x0000,0x0000,0x7fc0,0x3f80,0x1f00,0x0e00,0x0400,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
1874 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c00,0x1e00,0x1e00,0x0c00,0x0c00,0x0000,0x0c00,0x0000,0x0000,0x0000,0x1b00,
1875 0x1b00,0x1b00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1b00,0x1b00,0x7fc0,0x1b00,0x7fc0,0x1b00,0x1b00,0x0000,0x0000,0x0400,
1876 0x1f00,0x3580,0x3400,0x1f00,0x0580,0x3580,0x1f00,0x0400,0x0000,0x0000,0x3180,0x3300,0x0600,0x0c00,0x1980,0x3180,0x0000,0x0000,0x0000,
1877 0x0000,0x1c00,0x3300,0x3300,0x1f80,0x3300,0x3300,0x1d80,0x0000,0x0000,0x0000,0x0e00,0x0c00,0x1800,0x0000,0x0000,0x0000,0x0000,0x0000,
1878 0x0000,0x0000,0x0600,0x0c00,0x1800,0x1800,0x1800,0x0c00,0x0600,0x0000,0x0000,0x0000,0x1800,0x0c00,0x0600,0x0600,0x0600,0x0c00,0x1800,
1879 0x0000,0x0000,0x0000,0x0000,0x3300,0x1e00,0x7f80,0x1e00,0x3300,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x3f00,0x0c00,0x0c00,
1880 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x1800,0x0000,0x0000,0x0000,0x0000,0x0000,0x3f00,0x0000,
1881 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x0180,0x0300,0x0600,0x0c00,
1882 0x1800,0x3000,0x6000,0x0000,0x0000,0x0000,0x1f00,0x3380,0x3780,0x3f80,0x3d80,0x3980,0x1f00,0x0000,0x0000,0x0000,0x0c00,0x1c00,0x0c00,
1883 0x0c00,0x0c00,0x0c00,0x3f00,0x0000,0x0000,0x0000,0x1f00,0x3180,0x0180,0x0f00,0x1800,0x3180,0x3f80,0x0000,0x0000,0x0000,0x1f00,0x3180,
1884 0x0180,0x0700,0x0180,0x3180,0x1f00,0x0000,0x0000,0x0000,0x0700,0x0f00,0x1b00,0x3300,0x3f80,0x0300,0x0780,0x0000,0x0000,0x0000,0x3f80,
1885 0x3000,0x3000,0x3f00,0x0180,0x3180,0x1f00,0x0000,0x0000,0x0000,0x0f00,0x1800,0x3000,0x3f00,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0000,
1886 0x3f80,0x3180,0x0180,0x0300,0x0600,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,0x1f00,0x3180,0x3180,0x1f00,0x0000,0x0000,
1887 0x0000,0x1f00,0x3180,0x3180,0x1f80,0x0180,0x0300,0x1e00,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0000,0x0000,0x0c00,0x0c00,0x0000,
1888 0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0000,0x0000,0x0c00,0x0c00,0x1800,0x0000,0x0000,0x0300,0x0600,0x0c00,0x1800,0x0c00,0x0600,0x0300,
1889 0x0000,0x0000,0x0000,0x0000,0x0000,0x3f00,0x0000,0x3f00,0x0000,0x0000,0x0000,0x0000,0x0000,0x1800,0x0c00,0x0600,0x0300,0x0600,0x0c00,
1890 0x1800,0x0000,0x0000,0x0000,0x1e00,0x3300,0x0300,0x0300,0x0600,0x0c00,0x0000,0x0c00,0x0000,0x0000,0x3f00,0x6180,0x6780,0x6d80,0x6780,
1891 0x6000,0x3f00,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,0x3f80,0x3180,0x3180,0x3180,0x0000,0x0000,0x0000,0x3f00,0x3180,0x3180,0x3f00,
1892 0x3180,0x3180,0x3f00,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3000,0x3000,0x3000,0x3180,0x1f00,0x0000,0x0000,0x0000,0x3e00,0x3300,0x3180,
1893 0x3180,0x3180,0x3300,0x3e00,0x0000,0x0000,0x0000,0x3f80,0x3000,0x3000,0x3f00,0x3000,0x3000,0x3f80,0x0000,0x0000,0x0000,0x3f80,0x3000,
1894 0x3000,0x3f00,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3000,0x3380,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0000,0x3180,
1895 0x3180,0x3180,0x3f80,0x3180,0x3180,0x3180,0x0000,0x0000,0x0000,0x1e00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x1e00,0x0000,0x0000,0x0000,
1896 0x0700,0x0300,0x0300,0x0300,0x3300,0x3300,0x1e00,0x0000,0x0000,0x0000,0x3180,0x3180,0x3300,0x3e00,0x3300,0x3180,0x3180,0x0000,0x0000,
1897 0x0000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3f80,0x0000,0x0000,0x0000,0x6180,0x7380,0x7f80,0x6d80,0x6180,0x6180,0x6180,0x0000,
1898 0x0000,0x0000,0x3180,0x3980,0x3d80,0x3780,0x3380,0x3180,0x3180,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,0x3180,0x3180,0x3180,0x1f00,
1899 0x0000,0x0000,0x0000,0x3f00,0x3180,0x3180,0x3f00,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,0x3180,0x3180,0x3380,
1900 0x1f00,0x0380,0x0000,0x0000,0x3f00,0x3180,0x3180,0x3f00,0x3300,0x3180,0x3180,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3000,0x1f00,0x0180,
1901 0x3180,0x1f00,0x0000,0x0000,0x0000,0x7f80,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x3180,0x3180,0x3180,0x3180,
1902 0x3180,0x3180,0x1f00,0x0000,0x0000,0x0000,0x3180,0x3180,0x3180,0x3180,0x1b00,0x0e00,0x0400,0x0000,0x0000,0x0000,0x6180,0x6180,0x6180,
1903 0x6d80,0x7f80,0x7380,0x6180,0x0000,0x0000,0x0000,0x6180,0x3300,0x1e00,0x0c00,0x1e00,0x3300,0x6180,0x0000,0x0000,0x0000,0x6180,0x6180,
1904 0x3300,0x1e00,0x0c00,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x3f80,0x0300,0x0600,0x0c00,0x1800,0x3000,0x3f80,0x0000,0x0000,0x0000,0x1e00,
1905 0x1800,0x1800,0x1800,0x1800,0x1800,0x1e00,0x0000,0x0000,0x0000,0x6000,0x3000,0x1800,0x0c00,0x0600,0x0300,0x0000,0x0000,0x0000,0x0000,
1906 0x1e00,0x0600,0x0600,0x0600,0x0600,0x0600,0x1e00,0x0000,0x0000,0x0000,0x0400,0x0e00,0x1b00,0x3180,0x0000,0x0000,0x0000,0x0000,0x0000,
1907 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffc0,0x0000,0x0000,0x1c00,0x0c00,0x0600,0x0000,0x0000,0x0000,0x0000,0x0000,
1908 0x0000,0x0000,0x0000,0x0000,0x1f00,0x0180,0x1f80,0x3180,0x1f80,0x0000,0x0000,0x0000,0x3000,0x3000,0x3f00,0x3180,0x3180,0x3180,0x3f00,
1909 0x0000,0x0000,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3000,0x3180,0x1f00,0x0000,0x0000,0x0000,0x0180,0x0180,0x1f80,0x3180,0x3180,0x3180,
1910 0x1f80,0x0000,0x0000,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3f80,0x3000,0x1f00,0x0000,0x0000,0x0000,0x0f00,0x1800,0x1800,0x3e00,0x1800,
1911 0x1800,0x1800,0x0000,0x0000,0x0000,0x0000,0x0000,0x1f80,0x3180,0x3180,0x3180,0x1f80,0x0180,0x1f00,0x0000,0x3000,0x3000,0x3f00,0x3180,
1912 0x3180,0x3180,0x3180,0x0000,0x0000,0x0000,0x0c00,0x0000,0x1c00,0x0c00,0x0c00,0x0c00,0x1e00,0x0000,0x0000,0x0000,0x0600,0x0000,0x0e00,
1913 0x0600,0x0600,0x0600,0x0600,0x0600,0x1c00,0x0000,0x3000,0x3000,0x3180,0x3300,0x3e00,0x3300,0x3180,0x0000,0x0000,0x0000,0x1c00,0x0c00,
1914 0x0c00,0x0c00,0x0c00,0x0c00,0x0700,0x0000,0x0000,0x0000,0x0000,0x0000,0x3300,0x7f80,0x6d80,0x6d80,0x6180,0x0000,0x0000,0x0000,0x0000,
1915 0x0000,0x3f00,0x3180,0x3180,0x3180,0x3180,0x0000,0x0000,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0000,
1916 0x0000,0x0000,0x3f00,0x3180,0x3180,0x3f00,0x3000,0x3000,0x0000,0x0000,0x0000,0x0000,0x1f80,0x3180,0x3180,0x1f80,0x0180,0x01c0,0x0000,
1917 0x0000,0x0000,0x0000,0x3f00,0x3180,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1f80,0x3000,0x1f00,0x0180,0x3f00,0x0000,
1918 0x0000,0x0000,0x1800,0x1800,0x3e00,0x1800,0x1800,0x1800,0x0f00,0x0000,0x0000,0x0000,0x0000,0x0000,0x3180,0x3180,0x3180,0x3180,0x1f80,
1919 0x0000,0x0000,0x0000,0x0000,0x0000,0x3180,0x3180,0x1b00,0x0e00,0x0400,0x0000,0x0000,0x0000,0x0000,0x0000,0x6180,0x6d80,0x6d80,0x7f80,
1920 0x3300,0x0000,0x0000,0x0000,0x0000,0x0000,0x3180,0x1b00,0x0e00,0x1b00,0x3180,0x0000,0x0000,0x0000,0x0000,0x0000,0x3180,0x3180,0x3180,
1921 0x1f80,0x0180,0x1f00,0x0000,0x0000,0x0000,0x0000,0x3f00,0x0600,0x0c00,0x1800,0x3f00,0x0000,0x0000,0x0000,0x0e00,0x1800,0x1800,0x3000,
1922 0x1800,0x1800,0x0e00,0x0000,0x0000,0x0c00,0x0c00,0x0c00,0x0c00,0x0000,0x0c00,0x0c00,0x0c00,0x0c00,0x0000,0x0000,0x1c00,0x0600,0x0600,
1923 0x0300,0x0600,0x0600,0x1c00,0x0000,0x0000,0x0000,0x0000,0x0000,0x3800,0x6d80,0x0700,0x0000,0x0000,0x0000,0x0000,0x0000,0x0400,0x0e00,
1924 0x1b00,0x3180,0x3180,0x3180,0x3f80,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3000,0x3000,0x3000,0x3180,0x1f00,0x0c00,0x1800,0x0000,0x1b00,
1925 0x0000,0x3180,0x3180,0x3180,0x3180,0x1f80,0x0000,0x0000,0x0600,0x0c00,0x0000,0x1f00,0x3180,0x3f80,0x3000,0x1f00,0x0000,0x0000,0x0e00,
1926 0x1b00,0x0000,0x1f00,0x0180,0x1f80,0x3180,0x1f80,0x0000,0x0000,0x0000,0x1b00,0x0000,0x1f00,0x0180,0x1f80,0x3180,0x1f80,0x0000,0x0000,
1927 0x0c00,0x0600,0x0000,0x1f00,0x0180,0x1f80,0x3180,0x1f80,0x0000,0x0000,0x0e00,0x1b00,0x0e00,0x1f00,0x0180,0x1f80,0x3180,0x1f80,0x0000,
1928 0x0000,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3000,0x3180,0x1f00,0x0c00,0x1800,0x0e00,0x1b00,0x0000,0x1f00,0x3180,0x3f80,0x3000,0x1f00,
1929 0x0000,0x0000,0x0000,0x1b00,0x0000,0x1f00,0x3180,0x3f80,0x3000,0x1f00,0x0000,0x0000,0x0c00,0x0600,0x0000,0x1f00,0x3180,0x3f80,0x3000,
1930 0x1f00,0x0000,0x0000,0x0000,0x3600,0x0000,0x1c00,0x0c00,0x0c00,0x0c00,0x1e00,0x0000,0x0000,0x1c00,0x3600,0x0000,0x1c00,0x0c00,0x0c00,
1931 0x0c00,0x1e00,0x0000,0x0000,0x1800,0x0c00,0x0000,0x1c00,0x0c00,0x0c00,0x0c00,0x1e00,0x0000,0x0000,0x0000,0x1b00,0x0000,0x1f00,0x3180,
1932 0x3f80,0x3180,0x3180,0x0000,0x0000,0x0e00,0x1b00,0x0e00,0x1f00,0x3180,0x3f80,0x3180,0x3180,0x0000,0x0000,0x0600,0x0c00,0x0000,0x3f80,
1933 0x3000,0x3f00,0x3000,0x3f80,0x0000,0x0000,0x0000,0x0000,0x0000,0x3b80,0x0ec0,0x3fc0,0x6e00,0x3b80,0x0000,0x0000,0x0000,0x1f80,0x3600,
1934 0x6600,0x7f80,0x6600,0x6600,0x6780,0x0000,0x0000,0x0e00,0x1b00,0x0000,0x1f00,0x3180,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0000,0x1b00,
1935 0x0000,0x1f00,0x3180,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0c00,0x0600,0x0000,0x1f00,0x3180,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0e00,
1936 0x1b00,0x0000,0x3180,0x3180,0x3180,0x3180,0x1f80,0x0000,0x0000,0x0c00,0x0600,0x0000,0x3180,0x3180,0x3180,0x3180,0x1f80,0x0000,0x0000,
1937 0x0000,0x1b00,0x0000,0x3180,0x3180,0x3180,0x1f80,0x0180,0x1f00,0x0000,0x0000,0x1b00,0x0000,0x1f00,0x3180,0x3180,0x3180,0x1f00,0x0000,
1938 0x0000,0x0000,0x1b00,0x0000,0x3180,0x3180,0x3180,0x3180,0x1f80,0x0000,0x0000,0x0000,0x0000,0x0400,0x1f00,0x3580,0x3400,0x3580,0x1f00,
1939 0x0400,0x0000,0x0000,0x0f00,0x1980,0x1800,0x3e00,0x1800,0x1800,0x3000,0x3f80,0x0000,0x0000,0x6180,0x6180,0x3300,0x1e00,0x3f00,0x0c00,
1940 0x3f00,0x0c00,0x0000,0x0000,0x7f00,0x6180,0x6d80,0x6d80,0x7f00,0x6c00,0x6c00,0x6700,0x0000,0x0000,0x0700,0x0c00,0x0c00,0x1e00,0x0c00,
1941 0x0c00,0x0c00,0x3800,0x0000,0x0600,0x0c00,0x0000,0x1f00,0x0180,0x1f80,0x3180,0x1f80,0x0000,0x0000,0x0c00,0x1800,0x0000,0x1c00,0x0c00,
1942 0x0c00,0x0c00,0x1e00,0x0000,0x0000,0x0600,0x0c00,0x0000,0x1f00,0x3180,0x3180,0x3180,0x1f00,0x0000,0x0000,0x0600,0x0c00,0x0000,0x3180,
1943 0x3180,0x3180,0x3180,0x1f80,0x0000,0x0000,0x1d80,0x3700,0x0000,0x3f00,0x3180,0x3180,0x3180,0x3180,0x0000,0x0000,0x1d80,0x3700,0x0000,
1944 0x3980,0x3d80,0x3780,0x3380,0x3180,0x0000,0x0000,0x0000,0x1e00,0x0300,0x1f00,0x3300,0x1f00,0x0000,0x0000,0x0000,0x0000,0x0000,0x1e00,
1945 0x3300,0x3300,0x3300,0x1e00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0000,0x0c00,0x1800,0x3000,0x3000,0x3300,0x1e00,0x0000,0x0000,
1946 0x0000,0x0000,0x0000,0x3f80,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3f80,0x0180,0x0180,0x0180,0x0000,0x0000,
1947 0x0000,0x2080,0x2100,0x2200,0x2400,0x0b00,0x1180,0x2300,0x4380,0x0000,0x0000,0x2080,0x2100,0x2200,0x2400,0x0a80,0x1280,0x2380,0x4080,
1948 0x0000,0x0000,0x0c00,0x0000,0x0c00,0x0c00,0x1e00,0x1e00,0x0c00,0x0000,0x0000,0x0000,0x0000,0x1980,0x3300,0x6600,0x3300,0x1980,0x0000,
1949 0x0000,0x0000,0x0000,0x0000,0x6600,0x3300,0x1980,0x3300,0x6600,0x0000,0x0000,0x0000,0x2200,0x8880,0x2200,0x8880,0x2200,0x8880,0x2200,
1950 0x8880,0x2200,0x8880,0x5540,0xaa80,0x5540,0xaa80,0x5540,0xaa80,0x5540,0xaa80,0x5540,0xaa80,0xbb80,0xeec0,0xbb80,0xeec0,0xbb80,0xeec0,
1951 0xbb80,0xeec0,0xbb80,0xeec0,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0xfc00,
1952 0xfc00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0xfc00,0xfc00,0x0c00,0x0c00,0xfc00,0xfc00,0x0c00,0x0c00,0x3300,0x3300,0x3300,0x3300,
1953 0xf300,0xf300,0x3300,0x3300,0x3300,0x3300,0x0000,0x0000,0x0000,0x0000,0xff00,0xff00,0x3300,0x3300,0x3300,0x3300,0x0000,0x0000,0xfc00,
1954 0xfc00,0x0c00,0x0c00,0xfc00,0xfc00,0x0c00,0x0c00,0x3300,0x3300,0xf300,0xf300,0x0300,0x0300,0xf300,0xf300,0x3300,0x3300,0x3300,0x3300,
1955 0x3300,0x3300,0x3300,0x3300,0x3300,0x3300,0x3300,0x3300,0x0000,0x0000,0xff00,0xff00,0x0300,0x0300,0xf300,0xf300,0x3300,0x3300,0x3300,
1956 0x3300,0xf300,0xf300,0x0300,0x0300,0xff00,0xff00,0x0000,0x0000,0x3300,0x3300,0x3300,0x3300,0xff00,0xff00,0x0000,0x0000,0x0000,0x0000,
1957 0x1800,0x1800,0xf800,0xf800,0x1800,0x1800,0xf800,0xf800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xfc00,0xfc00,0x0c00,0x0c00,0x0c00,
1958 0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0fc0,0x0fc0,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0c00,0x0c00,0xffc0,0xffc0,0x0000,0x0000,
1959 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffc0,0xffc0,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0fc0,0x0fc0,0x0c00,
1960 0x0c00,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x0000,0xffc0,0xffc0,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0c00,0x0c00,0xffc0,0xffc0,
1961 0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0fc0,0x0fc0,0x0c00,0x0c00,0x0fc0,0x0fc0,0x0c00,0x0c00,0x3300,0x3300,0x3300,0x3300,0x33c0,
1962 0x33c0,0x3300,0x3300,0x3300,0x3300,0x3300,0x3300,0x33c0,0x33c0,0x3000,0x3000,0x3fc0,0x3fc0,0x0000,0x0000,0x0000,0x0000,0x3fc0,0x3fc0,
1963 0x3000,0x3000,0x33c0,0x33c0,0x3300,0x3300,0x3300,0x3300,0xf3c0,0xf3c0,0x0000,0x0000,0xffc0,0xffc0,0x0000,0x0000,0x0000,0x0000,0xffc0,
1964 0xffc0,0x0000,0x0000,0xf3c0,0xf3c0,0x3300,0x3300,0x3300,0x3300,0x33c0,0x33c0,0x3000,0x3000,0x33c0,0x33c0,0x3300,0x3300,0x0000,0x0000,
1965 0xffc0,0xffc0,0x0000,0x0000,0xffc0,0xffc0,0x0000,0x0000,0x3300,0x3300,0xf3c0,0xf3c0,0x0000,0x0000,0xf3c0,0xf3c0,0x3300,0x3300,0x0c00,
1966 0x0c00,0xffc0,0xffc0,0x0000,0x0000,0xffc0,0xffc0,0x0000,0x0000,0x3300,0x3300,0x3300,0x3300,0xffc0,0xffc0,0x0000,0x0000,0x0000,0x0000,
1967 0x0000,0x0000,0xffc0,0xffc0,0x0000,0x0000,0xffc0,0xffc0,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x0000,0xffc0,0xffc0,0x3300,0x3300,0x3300,
1968 0x3300,0x3300,0x3300,0x3300,0x3300,0x3fc0,0x3fc0,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0fc0,0x0fc0,0x0c00,0x0c00,0x0fc0,0x0fc0,
1969 0x0000,0x0000,0x0000,0x0000,0x0fc0,0x0fc0,0x0c00,0x0c00,0x0fc0,0x0fc0,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x0000,0x3fc0,0x3fc0,0x3300,
1970 0x3300,0x3300,0x3300,0x3300,0x3300,0x3300,0x3300,0xf3c0,0xf3c0,0x3300,0x3300,0x3300,0x3300,0x0c00,0x0c00,0xffc0,0xffc0,0x0000,0x0000,
1971 0xffc0,0xffc0,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0xfc00,0xfc00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0fc0,
1972 0x0fc0,0x0c00,0x0c00,0x0c00,0x0c00,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0x0000,0x0000,0x0000,0x0000,
1973 0x0000,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0xf800,0x07c0,0x07c0,0x07c0,
1974 0x07c0,0x07c0,0x07c0,0x07c0,0x07c0,0x07c0,0x07c0,0xffc0,0xffc0,0xffc0,0xffc0,0xffc0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
1975 0x0000,0x1d80,0x3700,0x3200,0x3700,0x1d80,0x0000,0x0000,0x0000,0x1e00,0x3300,0x3300,0x3600,0x3300,0x3180,0x3700,0x3000,0x0000,0x0000,
1976 0x3f80,0x3180,0x3000,0x3000,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x0000,0x7f80,0x3300,0x3300,0x3300,0x3300,0x3300,0x0000,0x0000,
1977 0x0000,0x3f80,0x1800,0x0c00,0x0600,0x0c00,0x1800,0x3f80,0x0000,0x0000,0x0000,0x0000,0x0000,0x1f80,0x3600,0x3300,0x3300,0x1e00,0x0000,
1978 0x0000,0x0000,0x0000,0x0000,0x6300,0x6300,0x6700,0x7d80,0x6000,0x6000,0x0000,0x0000,0x0000,0x0000,0x3f00,0x0c00,0x0c00,0x0c00,0x0600,
1979 0x0000,0x0000,0x0000,0x1e00,0x0c00,0x3f00,0x6d80,0x6d80,0x3f00,0x0c00,0x1e00,0x0000,0x0000,0x1e00,0x3300,0x3300,0x3f00,0x3300,0x3300,
1980 0x1e00,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,0x3180,0x3180,0x1b00,0x3b80,0x0000,0x0000,0x0000,0x1f00,0x0c00,0x0600,0x1f00,0x3180,
1981 0x3180,0x1f00,0x0000,0x0000,0x0000,0x0000,0x0000,0x3b80,0x66c0,0x64c0,0x6cc0,0x3b80,0x0000,0x0000,0x0000,0x0000,0x0180,0x3f00,0x6780,
1982 0x6d80,0x7980,0x3f00,0x6000,0x0000,0x0000,0x0000,0x0000,0x1f00,0x3000,0x1e00,0x3000,0x1f00,0x0000,0x0000,0x0000,0x1f00,0x3180,0x3180,
1983 0x3180,0x3180,0x3180,0x3180,0x0000,0x0000,0x0000,0x0000,0x3f00,0x0000,0x3f00,0x0000,0x3f00,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,
1984 0x3f00,0x0c00,0x0c00,0x0000,0x3f00,0x0000,0x0000,0x0000,0x0600,0x0c00,0x1800,0x0c00,0x0600,0x0000,0x3f00,0x0000,0x0000,0x0000,0x1800,
1985 0x0c00,0x0600,0x0c00,0x1800,0x0000,0x3f00,0x0000,0x0000,0x0000,0x0700,0x0d80,0x0d80,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,
1986 0x0c00,0x0c00,0x0c00,0x0c00,0x0c00,0x6c00,0x6c00,0x3800,0x0000,0x0000,0x0000,0x0c00,0x0000,0x3f00,0x0000,0x0c00,0x0000,0x0000,0x0000,
1987 0x0000,0x3800,0x6d80,0x0700,0x0000,0x3800,0x6d80,0x0700,0x0000,0x0000,0x0000,0x0e00,0x1b00,0x1b00,0x0e00,0x0000,0x0000,0x0000,0x0000,
1988 0x0000,0x0000,0x0000,0x0000,0x0c00,0x0c00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c00,0x0000,0x0000,0x0000,
1989 0x0000,0x0000,0x0000,0x07c0,0x0600,0x0600,0x6600,0x3600,0x1e00,0x0e00,0x0600,0x0200,0x0000,0x3e00,0x3300,0x3300,0x3300,0x3300,0x0000,
1990 0x0000,0x0000,0x0000,0x0000,0x1e00,0x0300,0x0e00,0x1800,0x1f00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1e00,0x1e00,0x1e00,
1991 0x1e00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
1994 // bits 0..3: width
1995 // bits 4..7: lshift
1996 public immutable ubyte[256] kgiFont6PropWidth = () {
1997 ubyte[256] res;
1998 foreach (immutable cnum; 0..256) {
1999 import core.bitop : bsf, bsr;
2000 immutable doshift =
2001 (cnum >= 32 && cnum <= 127) ||
2002 (cnum >= 143 && cnum <= 144) ||
2003 (cnum >= 166 && cnum <= 167) ||
2004 (cnum >= 192 && cnum <= 255);
2005 int shift = 0;
2006 if (doshift) {
2007 shift = 8;
2008 foreach (immutable dy; 0..8) {
2009 immutable b = kgiFont6[cnum*8+dy];
2010 if (b) {
2011 immutable mn = 7-bsr(b);
2012 if (mn < shift) shift = mn;
2016 ubyte wdt = 0;
2017 foreach (immutable dy; 0..8) {
2018 immutable b = (kgiFont6[cnum*8+dy]<<shift);
2019 immutable cwdt = (b ? 8-bsf(b) : 0);
2020 if (cwdt > wdt) wdt = cast(ubyte)cwdt;
2022 switch (cnum) {
2023 case 0: wdt = 8; break; // 8px space
2024 case 32: wdt = 5; break; // 5px space
2025 case 17: .. case 27: wdt = 8; break; // single frames
2026 case 48: .. case 57: wdt = 5; break; // digits are monospaced
2027 case 127: .. case 142: wdt = 8; break; // filled frames
2028 case 145: .. case 151: wdt = 8; break; // filled frames
2029 case 155: .. case 159: wdt = 8; break; // filled frames
2030 default:
2032 res[cnum] = (wdt&0x0f)|((shift<<4)&0xf0);
2034 return res;
2035 }();
2037 // bits 0..3: width
2038 // bits 4..7: lshift
2039 public immutable ubyte[256] kgiFont8PropWidth = () {
2040 ubyte[256] res;
2041 foreach (immutable cnum; 0..256) {
2042 import core.bitop : bsf, bsr;
2043 immutable doshift =
2044 (cnum >= 32 && cnum <= 127) ||
2045 (cnum >= 143 && cnum <= 144) ||
2046 (cnum >= 166 && cnum <= 167) ||
2047 (cnum >= 192 && cnum <= 255);
2048 int shift = 0;
2049 if (doshift) {
2050 shift = 8;
2051 foreach (immutable dy; 0..8) {
2052 immutable b = kgiFont8[cnum*8+dy];
2053 if (b) {
2054 immutable mn = 7-bsr(b);
2055 if (mn < shift) shift = mn;
2059 ubyte wdt = 0;
2060 foreach (immutable dy; 0..8) {
2061 immutable b = (kgiFont8[cnum*8+dy]<<shift);
2062 immutable cwdt = (b ? 8-bsf(b) : 0);
2063 if (cwdt > wdt) wdt = cast(ubyte)cwdt;
2065 switch (cnum) {
2066 case 0: wdt = 8; break; // 8px space
2067 case 32: wdt = 5; break; // 5px space
2068 case 48: .. case 57: wdt = 5; break; // digits are monospaced
2069 case 176: .. case 223: wdt = 8; break; // pseudographics (frames, etc)
2070 default:
2072 res[cnum] = (wdt&0x0f)|((shift<<4)&0xf0);
2074 return res;
2075 }();
2078 // ////////////////////////////////////////////////////////////////////////// //
2079 private:
2081 const(char)[] sdrGetPart(string typepfx) (const(char)[] s) nothrow @trusted @nogc {
2082 // skips prefix "---"
2083 static usize findMark(string at) (const(char)[] s, uint idx=0) nothrow @trusted @nogc {
2084 while (idx < s.length) {
2085 if (idx == 0 || s.ptr[idx-1] == '\n') {
2086 while (idx < s.length && (s.ptr[idx] == ' ' || s.ptr[idx] == '\t')) ++idx;
2087 if (s.length-idx >= 3 && s.ptr[idx] == '-' && s.ptr[idx+1] == '-' && s.ptr[idx+2] == '-') {
2088 static if (at == "start") {
2089 while (idx > 0 && s.ptr[idx-1] != '\n') --idx;
2090 } else static if (at == "end") {
2091 idx += 3;
2092 while (idx < s.length && s.ptr[idx] == '-') ++idx;
2093 while (idx < s.length && (s.ptr[idx] == ' ' || s.ptr[idx] == '\t')) ++idx;
2094 } else {
2095 static assert(0, "wtf?!");
2097 return idx;
2100 ++idx;
2102 return idx;
2105 usize idx = findMark!"end"(s);
2106 while (idx < s.length) {
2107 if (s.length-idx >= typepfx.length && s[idx..idx+typepfx.length] == typepfx) {
2108 while (idx < s.length && s.ptr[idx] != '\n') ++idx;
2109 while (idx < s.length && s.ptr[idx] <= ' ') ++idx;
2110 if (idx >= s.length) return null;
2111 auto eidx = findMark!"start"(s, idx);
2112 if (eidx > s.length) eidx = s.length;
2113 while (eidx > idx && s.ptr[eidx-1] <= ' ') --eidx;
2114 return (eidx > idx ? s[idx..eidx] : null);
2115 } else {
2116 while (idx < s.length && s.ptr[idx] != '\n') ++idx;
2117 idx = findMark!"end"(s, idx);
2120 return null;
2124 // find var id: glGetUniformLocation(prg, bufasciiz.ptr)
2125 // set var: glUniformXXX()
2127 // returns 0 or programid
2128 uint compileShaders (const(char)[] src) nothrow @trusted {
2129 import iv.glbinds;
2131 GLuint prg = 0;
2132 GLuint fsid = 0, vsid = 0;
2134 auto fragsrc = sdrGetPart!"frag"(src);
2135 auto vertsrc = sdrGetPart!"vert"(src);
2137 if (fragsrc.length == 0 && vertsrc.length == 0) return 0;
2139 if (fragsrc.length) {
2140 fsid = createShader!GL_FRAGMENT_SHADER(fragsrc);
2141 if (fsid == 0) return 0;
2144 if (vertsrc.length) {
2145 vsid = createShader!GL_VERTEX_SHADER(vertsrc);
2146 if (vsid == 0) {
2147 if (fsid != 0) glDeleteShader(fsid);
2148 return 0;
2152 prg = glCreateProgram();
2153 if (prg == 0) {
2154 if (fsid != 0) glDeleteShader(fsid);
2155 if (vsid != 0) glDeleteShader(vsid);
2156 conwriteln("GLKGI ERROR: can't create shader program.");
2157 return 0;
2160 if (fsid) glAttachShader(prg, fsid);
2161 if (vsid) glAttachShader(prg, vsid);
2162 glLinkProgram(prg);
2164 GLint lres = 0;
2165 glGetProgramiv(prg, GL_LINK_STATUS, &lres);
2166 if (lres != GL_TRUE) {
2167 glDeleteProgram(prg);
2168 if (fsid != 0) glDeleteShader(fsid);
2169 if (vsid != 0) glDeleteShader(vsid);
2170 conwriteln("GLKGI ERROR: can't link shader program.");
2171 return 0;
2174 return prg;
2178 // returns 0 or shaderid
2179 uint createShader(uint type) (const(char)[] src) nothrow @trusted {
2180 import iv.glbinds;
2182 auto shaderId = glCreateShader(type);
2183 if (!shaderId) {
2184 conwriteln("GLKGI ERROR: can't create shader.");
2185 return 0;
2187 auto sptr = src.ptr;
2188 GLint slen = cast(int)src.length;
2189 glShaderSource(shaderId, 1, &sptr, &slen);
2190 glCompileShader(shaderId);
2191 GLint success = 0;
2192 glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
2193 if (!success || showShaderWarnings) {
2194 import core.stdc.stdlib : malloc, free;
2195 GLint logSize = 0;
2196 glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &logSize);
2197 if (logSize > 0) {
2198 auto logStrZ = cast(GLchar*)malloc(logSize);
2199 if (logStrZ !is null) {
2200 //import core.stdc.stdio : printf;
2201 scope(exit) free(logStrZ);
2202 glGetShaderInfoLog(shaderId, logSize, null, logStrZ);
2203 if (logSize > 0 && logStrZ[logSize-1] == 0) --logSize;
2204 //printf("shader '%.*s' compilation messages:\n%s\n", cast(uint)ashaderName.length, ashaderName.ptr, logStrZ);
2205 conwriteln("GLKGI: shader compilation messages:\n", logStrZ[0..logSize]);
2209 if (!success) {
2210 glDeleteShader(shaderId);
2211 conwriteln("GLKGI ERROR: can't compile shader.");
2212 return 0;
2214 return shaderId;
2218 // ////////////////////////////////////////////////////////////////////////// //
2219 // shaders
2220 static string sdrScanlineSrc = q{
2221 ---frag---
2222 #version 120
2224 uniform sampler2D tex;
2226 void main () {
2227 vec4 color = texture2D(tex, gl_TexCoord[0].xy);
2228 if (mod(floor(gl_FragCoord.y), 2) == 1) { color.x *= 0.75; color.y *= 0.75; color.z *= 0.75; }
2229 gl_FragColor = color;
2232 ---vert---
2233 #version 120
2235 void main () {
2236 gl_TexCoord[0] = gl_MultiTexCoord0;
2237 //gl_Position = gl_ProjectionMatrix*gl_ModelViewMatrix*gl_Vertex;
2238 gl_Position = gl_ProjectionMatrix*gl_Vertex;
2243 // ////////////////////////////////////////////////////////////////////////// //
2244 // default cursor (hi, Death Track!)
2245 private enum defaultCurWidth = 17;
2246 private enum defaultCurHeight = 23;
2247 private static immutable ubyte[defaultCurWidth*defaultCurHeight] defaultCurImg = [
2248 0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2249 0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
2250 1,0,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
2251 1,1,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,
2252 1,1,3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,
2253 1,1,3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,
2254 1,1,3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,
2255 1,1,3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,
2256 1,1,3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,
2257 1,1,3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,
2258 1,1,3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,
2259 1,1,3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,
2260 1,1,3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,
2261 1,1,3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,
2262 1,1,3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,
2263 1,1,3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,
2264 1,1,3,3,5,6,3,3,1,1,1,1,1,1,1,0,0,
2265 1,1,3,3,6,3,3,1,1,1,1,1,1,1,1,0,0,
2266 1,1,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,
2267 1,1,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
2268 1,1,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
2269 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2270 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
2272 private static immutable VColor[9] defaultCurPal = [
2273 rgbacol( 0, 0, 0, 0),
2274 rgbacol( 0, 0, 0,127),
2275 rgbacol( 85,255,255,255),
2276 rgbacol( 85, 85,255,255),
2277 rgbacol(255, 85, 85,255),
2278 rgbacol(170, 0,170,255),
2279 rgbacol( 85, 85, 85,255),
2280 rgbacol( 0, 0, 0,255),
2281 rgbacol( 0, 0,170,255),