DEADSOFTWARE

FlexUI: completely reworked graphics layer -- it is using drawing contexts now
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Mon, 2 Oct 2017 22:28:58 +0000 (01:28 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Mon, 2 Oct 2017 23:34:42 +0000 (02:34 +0300)
src/flexui/fui_common.pas
src/flexui/fui_ctls.pas
src/flexui/fui_gfx_gl.pas
src/flexui/fui_gfx_gl_cursor.inc [new file with mode: 0644]
src/flexui/fui_gfx_gl_fonts.inc [new file with mode: 0644]

index 76354c3b966c4acb99342519f15efcddb3768bef..744ddff1bd71de3c59974e8e70b34aac3e263c92 100644 (file)
@@ -92,6 +92,29 @@ type
     function blend (ar, ag, ab, aa: Integer): TGxRGBA; inline;
   end;
 
+  TGxRect = packed record
+  public
+    x, y, w, h: Integer;
+
+  public
+    constructor Create (ax, ay, aw, ah: Integer);
+
+    function empty (): Boolean; inline; // invalid rects are empty too
+    function valid (): Boolean; inline;
+
+    // modifies this rect, so it won't be bigger than `r`
+    // returns `false` if this rect becomes empty
+    function intersect (constref r: TGxRect): Boolean; inline;
+  end;
+
+  TGxOfs = packed record
+  public
+    xofs, yofs: Integer;
+
+  public
+    constructor Create (axofs, ayofs: Integer);
+  end;
+
 
 // ////////////////////////////////////////////////////////////////////////// //
 // return `false` if destination rect is empty
@@ -105,7 +128,6 @@ implementation
 uses
   utils;
 
-
 // ////////////////////////////////////////////////////////////////////////// //
 constructor TLaySize.Create (aw, ah: Integer); begin w := aw; h := ah; end;
 function TLaySize.getIdx (idx: Integer): Integer; inline; begin if (idx = 0) then result := w else if (idx = 1) then result := h else result := -1; end;
@@ -174,6 +196,22 @@ begin
 end;
 
 
+// ////////////////////////////////////////////////////////////////////////// //
+constructor TGxRect.Create (ax, ay, aw, ah: Integer); begin x := ax; y := ay; w := aw; h := ah; end;
+
+function TGxRect.empty (): Boolean; inline; begin result := (w <= 0) or (h <= 0); end;
+function TGxRect.valid (): Boolean; inline; begin result := (w < 0) or (h < 0); end;
+
+function TGxRect.intersect (constref r: TGxRect): Boolean; inline;
+begin
+  result := intersectRect(x, y, w, h, r.x, r.y, r.w, r.h);
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+constructor TGxOfs.Create (axofs, ayofs: Integer); begin xofs := axofs; yofs := ayofs; end;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 //TODO: overflow checks
 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean; inline;
index b7d5bfa2a27dca30cc392399161d64abca808b53..c573de87b2816387c38ccdddea88d5ba297a7262 100644 (file)
@@ -70,11 +70,7 @@ type
     mFrameColor: array[0..ClrIdxMax] of TGxRGBA;
     mFrameTextColor: array[0..ClrIdxMax] of TGxRGBA;
     mFrameIconColor: array[0..ClrIdxMax] of TGxRGBA;
-    mDarken: array[0..ClrIdxMax] of Integer; // -1: none
-
-  private
-    scis: TScissorSave;
-    scallowed: Boolean;
+    mDarken: array[0..ClrIdxMax] of Integer; // >255: none
 
   protected
     procedure updateStyle (); virtual;
@@ -110,15 +106,11 @@ type
 
     procedure calcFullClientSize ();
 
+  protected
+    var savedClip: TGxRect; // valid only in `draw*()` calls
     //WARNING! do not call scissor functions outside `.draw*()` API!
     // set scissor to this rect (in local coords)
-    procedure setScissor (lx, ly, lw, lh: Integer);
-    // reset scissor to whole control
-    procedure resetScissor (fullArea: Boolean); inline; // "full area" means "with frame"
-
-    // DO NOT USE!
-    // set scissor to this rect (in global coords)
-    procedure setScissorGLInternal (x, y, w, h: Integer);
+    procedure setScissor (lx, ly, lw, lh: Integer); // valid only in `draw*()` calls
 
   public
     actionCB: TActionCB;
@@ -474,8 +466,7 @@ type
   protected
     mBoolVar: PBoolean;
     mChecked: Boolean;
-    mCheckedStr: AnsiString;
-    mUncheckedStr: AnsiString;
+    mIcon: TGxContext.TMarkIcon;
     mSwitchColor: array[0..ClrIdxMax] of TGxRGBA;
 
   protected
@@ -553,6 +544,7 @@ procedure uiLayoutCtl (ctl: TUIControl);
 // ////////////////////////////////////////////////////////////////////////// //
 var
   fuiRenderScale: Single = 1.0;
+  uiContext: TGxContext = nil;
 
 
 implementation
@@ -780,7 +772,9 @@ var
   ctl: TUIControl;
 begin
   processKills();
-  gxBeginUIDraw(fuiRenderScale);
+  //if (uiContext = nil) then uiContext := TGxContext.Create();
+  gxSetContext(uiContext, fuiRenderScale);
+  uiContext.resetClip();
   try
     for f := 0 to High(uiTopList) do
     begin
@@ -789,11 +783,11 @@ begin
       if (f <> High(uiTopList)) then
       begin
         cidx := ctl.getColorIndex;
-        if (ctl.mDarken[cidx] > 0) then darkenRect(ctl.x0, ctl.y0, ctl.width, ctl.height, ctl.mDarken[cidx]);
+        uiContext.darkenRect(ctl.x0, ctl.y0, ctl.width, ctl.height, ctl.mDarken[cidx]);
       end;
     end;
   finally
-    gxEndUIDraw();
+    gxSetContext(nil);
   end;
 end;
 
@@ -884,7 +878,7 @@ begin
   mX := 0;
   mY := 0;
   mWidth := 64;
-  mHeight := 8;
+  mHeight := uiContext.charHeight(' ');
   mFrameWidth := 0;
   mFrameHeight := 0;
   mEnabled := true;
@@ -892,11 +886,10 @@ begin
   mChildren := nil;
   mFocused := nil;
   mEscClose := false;
-  scallowed := false;
   mDrawShadow := false;
   actionCB := nil;
   // layouter interface
-  //mDefSize := TLaySize.Create(64, 8); // default size
+  //mDefSize := TLaySize.Create(64, uiContext.charHeight(' ')); // default size
   mDefSize := TLaySize.Create(0, 0); // default size
   mMaxSize := TLaySize.Create(-1, -1); // maximum size
   mPadding := TLaySize.Create(0, 0);
@@ -983,21 +976,21 @@ begin
   mFrameColor[ClrIdxActive] := root.get('frame-color', 'active', cst).asRGBADef(TGxRGBA.Create(255, 255, 255));
   mFrameTextColor[ClrIdxActive] := root.get('frame-text-color', 'active', cst).asRGBADef(TGxRGBA.Create(255, 255, 255));
   mFrameIconColor[ClrIdxActive] := root.get('frame-icon-color', 'active', cst).asRGBADef(TGxRGBA.Create(0, 255, 0));
-  mDarken[ClrIdxActive] := root.get('darken', 'active', cst).asInt(-1);
+  mDarken[ClrIdxActive] := root.get('darken', 'active', cst).asInt(666);
   // disabled
   mBackColor[ClrIdxDisabled] := root.get('back-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(0, 0, 128));
   mTextColor[ClrIdxDisabled] := root.get('text-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127));
   mFrameColor[ClrIdxDisabled] := root.get('frame-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127));
   mFrameTextColor[ClrIdxDisabled] := root.get('frame-text-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127));
   mFrameIconColor[ClrIdxDisabled] := root.get('frame-icon-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(0, 127, 0));
-  mDarken[ClrIdxDisabled] := root.get('darken', 'disabled', cst).asInt(-1);
+  mDarken[ClrIdxDisabled] := root.get('darken', 'disabled', cst).asInt(666);
   // inactive
   mBackColor[ClrIdxInactive] := root.get('back-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(0, 0, 128));
   mTextColor[ClrIdxInactive] := root.get('text-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255));
   mFrameColor[ClrIdxInactive] := root.get('frame-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255));
   mFrameTextColor[ClrIdxInactive] := root.get('frame-text-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255));
   mFrameIconColor[ClrIdxInactive] := root.get('frame-icon-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(0, 255, 0));
-  mDarken[ClrIdxInactive] := root.get('darken', 'inactive', cst).asInt(-1);
+  mDarken[ClrIdxInactive] := root.get('darken', 'inactive', cst).asInt(666);
 end;
 
 
@@ -1838,51 +1831,30 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-procedure TUIControl.setScissorGLInternal (x, y, w, h: Integer);
-begin
-  if not scallowed then exit;
-  x := trunc(x*fuiRenderScale);
-  y := trunc(y*fuiRenderScale);
-  w := trunc(w*fuiRenderScale);
-  h := trunc(h*fuiRenderScale);
-  scis.combineRect(x, y, w, h);
-end;
-
 procedure TUIControl.setScissor (lx, ly, lw, lh: Integer);
 var
   gx, gy, wdt, hgt, cgx, cgy: Integer;
 begin
-  if not scallowed then exit;
-
-  if not intersectRect(lx, ly, lw, lh, 0, 0, mWidth, mHeight) then
+  if (not intersectRect(lx, ly, lw, lh, 0, 0, mWidth, mHeight)) then
   begin
-    scis.combineRect(0, 0, 0, 0);
+    uiContext.clip := TGxRect.Create(0, 0, 0, 0);
     exit;
   end;
 
   getDrawRect(gx, gy, wdt, hgt);
+
   toGlobal(lx, ly, cgx, cgy);
-  if not intersectRect(gx, gy, wdt, hgt, cgx, cgy, lw, lh) then
+  if (not intersectRect(gx, gy, wdt, hgt, cgx, cgy, lw, lh)) then
   begin
-    scis.combineRect(0, 0, 0, 0);
+    uiContext.clip := TGxRect.Create(0, 0, 0, 0);
     exit;
   end;
 
-  setScissorGLInternal(gx, gy, wdt, hgt);
+  uiContext.clip := savedClip;
+  uiContext.combineClip(TGxRect.Create(gx, gy, wdt, hgt));
+  //uiContext.clip := TGxRect.Create(gx, gy, wdt, hgt);
 end;
 
-procedure TUIControl.resetScissor (fullArea: Boolean); inline;
-begin
-  if not scallowed then exit;
-  if (fullArea) then
-  begin
-    setScissor(0, 0, mWidth, mHeight);
-  end
-  else
-  begin
-    setScissor(mFrameWidth, mFrameHeight, mWidth-mFrameWidth*2, mHeight-mFrameHeight*2);
-  end;
-end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
@@ -1890,13 +1862,26 @@ procedure TUIControl.draw ();
 var
   f: Integer;
   gx, gy: Integer;
+
+  procedure resetScissor (fullArea: Boolean); inline;
+  begin
+    uiContext.clip := savedClip;
+    if (fullArea) then
+    begin
+      setScissor(0, 0, mWidth, mHeight);
+    end
+    else
+    begin
+      setScissor(mFrameWidth, mFrameHeight, mWidth-mFrameWidth*2, mHeight-mFrameHeight*2);
+    end;
+  end;
+
 begin
-  if (mWidth < 1) or (mHeight < 1) then exit;
+  if (mWidth < 1) or (mHeight < 1) or (uiContext = nil) or (not uiContext.active) then exit;
   toGlobal(0, 0, gx, gy);
 
-  scis.save(true); // scissoring enabled
+  savedClip := uiContext.clip;
   try
-    scallowed := true;
     resetScissor(true); // full area
     drawControl(gx, gy);
     resetScissor(false); // client area
@@ -1904,8 +1889,7 @@ begin
     resetScissor(true); // full area
     drawControlPost(gx, gy);
   finally
-    scis.restore();
-    scallowed := false;
+    uiContext.clip := savedClip;
   end;
 end;
 
@@ -1917,11 +1901,12 @@ end;
 procedure TUIControl.drawControlPost (gx, gy: Integer);
 begin
   // shadow
-  if mDrawShadow and (mWidth > 0) and (mHeight > 0) then
+  if (mParent = nil) and (mDrawShadow) and (mWidth > 0) and (mHeight > 0) then
   begin
-    setScissorGLInternal(gx+8, gy+8, mWidth, mHeight);
-    darkenRect(gx+mWidth, gy+8, 8, mHeight, 128);
-    darkenRect(gx+8, gy+mHeight, mWidth-8, 8, 128);
+    //setScissorGLInternal(gx+8, gy+8, mWidth, mHeight);
+    uiContext.resetClip();
+    uiContext.darkenRect(gx+mWidth, gy+8, 8, mHeight, 128);
+    uiContext.darkenRect(gx+8, gy+mHeight, mWidth-8, 8, 128);
   end;
 end;
 
@@ -2061,11 +2046,14 @@ begin
   mFitToScreen := true;
   mFrameWidth := 8;
   mFrameHeight := 8;
-  if (mWidth < mFrameWidth*2+3*8) then mWidth := mFrameWidth*2+3*8;
+  if (mWidth < mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then mWidth := mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close);
   if (mHeight < mFrameHeight*2) then mHeight := mFrameHeight*2;
   if (Length(mTitle) > 0) then
   begin
-    if (mWidth < Length(mTitle)*8+mFrameWidth*2+3*8) then mWidth := Length(mTitle)*8+mFrameWidth*2+3*8;
+    if (mWidth < uiContext.textWidth(mTitle)+mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then
+    begin
+      mWidth := uiContext.textWidth(mTitle)+mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close);
+    end;
   end;
   mCanFocus := false;
   mDragScroll := TXMode.None;
@@ -2126,54 +2114,61 @@ end;
 
 procedure TUITopWindow.drawControl (gx, gy: Integer);
 begin
-  fillRect(gx, gy, mWidth, mHeight, mBackColor[getColorIndex]);
+  uiContext.color := mBackColor[getColorIndex];
+  uiContext.fillRect(gx, gy, mWidth, mHeight);
 end;
 
 
 procedure TUITopWindow.drawControlPost (gx, gy: Integer);
 var
   cidx: Integer;
-  tx, hgt, sbhgt: Integer;
+  tx, hgt, sbhgt, iwdt: Integer;
 begin
   cidx := getColorIndex;
   if (mDragScroll = TXMode.Drag) then
   begin
-    drawRectUI(gx+4, gy+4, mWidth-8, mHeight-8, mFrameColor[cidx]);
+    uiContext.color := mFrameColor[cidx];
+    uiContext.rect(gx+4, gy+4, mWidth-8, mHeight-8);
   end
   else
   begin
-    drawRectUI(gx+3, gy+3, mWidth-6, mHeight-6, mFrameColor[cidx]);
-    drawRectUI(gx+5, gy+5, mWidth-10, mHeight-10, mFrameColor[cidx]);
+    uiContext.color := mFrameColor[cidx];
+    uiContext.rect(gx+3, gy+3, mWidth-6, mHeight-6);
+    uiContext.rect(gx+5, gy+5, mWidth-10, mHeight-10);
     // vertical scroll bar
     hgt := mHeight-mFrameHeight*2;
     if (hgt > 0) and (mFullSize.h > hgt) then
     begin
       //writeln(mTitle, ': height=', mHeight-mFrameHeight*2, '; fullsize=', mFullSize.toString);
       sbhgt := mHeight-mFrameHeight*2+2;
-      fillRect(gx+mWidth-mFrameWidth+1, gy+7, mFrameWidth-3, sbhgt, mFrameColor[cidx]);
+      uiContext.fillRect(gx+mWidth-mFrameWidth+1, gy+7, mFrameWidth-3, sbhgt);
       hgt += mScrollY;
       if (hgt > mFullSize.h) then hgt := mFullSize.h;
       hgt := sbhgt*hgt div mFullSize.h;
       if (hgt > 0) then
       begin
         setScissor(mWidth-mFrameWidth+1, 7, mFrameWidth-3, sbhgt);
-        darkenRect(gx+mWidth-mFrameWidth+1, gy+7+hgt, mFrameWidth-3, sbhgt, 128);
+        uiContext.darkenRect(gx+mWidth-mFrameWidth+1, gy+7+hgt, mFrameWidth-3, sbhgt, 128);
       end;
     end;
     // frame icon
-    setScissor(mFrameWidth, 0, 3*8, 8);
-    fillRect(gx+mFrameWidth, gy, 3*8, 8, mBackColor[cidx]);
-    drawText8(gx+mFrameWidth, gy, '[ ]', mFrameColor[cidx]);
-    if mInClose then drawText8(gx+mFrameWidth+7, gy, '#', mFrameIconColor[cidx])
-    else drawText8(gx+mFrameWidth+7, gy, '*', mFrameIconColor[cidx]);
+    iwdt := uiContext.iconWinWidth(TGxContext.TWinIcon.Close);
+    setScissor(mFrameWidth, 0, iwdt, 8);
+    uiContext.color := mBackColor[cidx];
+    uiContext.fillRect(gx+mFrameWidth, gy, iwdt, 8);
+    uiContext.color := mFrameIconColor[cidx];
+    uiContext.drawIconWin(TGxContext.TWinIcon.Close, gx+mFrameWidth, gy, mInClose);
   end;
   // title
   if (Length(mTitle) > 0) then
   begin
-    setScissor(mFrameWidth+3*8, 0, mWidth-mFrameWidth*2-3*8, 8);
-    tx := (gx+3*8)+((mWidth-3*8)-Length(mTitle)*8) div 2;
-    fillRect(tx-3, gy, Length(mTitle)*8+3+2, 8, mBackColor[cidx]);
-    drawText8(tx, gy, mTitle, mFrameTextColor[cidx]);
+    iwdt := uiContext.iconWinWidth(TGxContext.TWinIcon.Close);
+    setScissor(mFrameWidth+iwdt, 0, mWidth-mFrameWidth*2-iwdt, 8);
+    tx := (gx+iwdt)+((mWidth-iwdt)-uiContext.textWidth(mTitle)) div 2;
+    uiContext.color := mBackColor[cidx];
+    uiContext.fillRect(tx-3, gy, uiContext.textWidth(mTitle)+3+2, 8);
+    uiContext.color := mFrameTextColor[cidx];
+    uiContext.drawText(tx, gy, mTitle);
   end;
   // shadow
   inherited drawControlPost(gx, gy);
@@ -2268,7 +2263,7 @@ begin
       if (ly < 8) then
       begin
         uiGrabCtl := self;
-        if (lx >= mFrameWidth) and (lx < mFrameWidth+3*8) then
+        if (lx >= mFrameWidth) and (lx < mFrameWidth+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then
         begin
           //uiRemoveWindow(self);
           mWaitingClose := true;
@@ -2314,7 +2309,7 @@ begin
     begin
       if mWaitingClose then
       begin
-        if (lx >= mFrameWidth) and (lx < mFrameWidth+3*8) then
+        if (lx >= mFrameWidth) and (lx < mFrameWidth+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then
         begin
           if (not assigned(closeRequestCB)) or (closeRequestCB(self)) then
           begin
@@ -2332,7 +2327,7 @@ begin
     begin
       if mWaitingClose then
       begin
-        mInClose := (lx >= mFrameWidth) and (lx < mFrameWidth+3*8);
+        mInClose := (lx >= mFrameWidth) and (lx < mFrameWidth+uiContext.iconWinWidth(TGxContext.TWinIcon.Close));
         ev.eat();
         exit;
       end;
@@ -2368,7 +2363,7 @@ end;
 procedure TUIBox.setCaption (const acap: AnsiString);
 begin
   mCaption := acap;
-  mDefSize := TLaySize.Create(Length(mCaption)*8+3, 8);
+  mDefSize := TLaySize.Create(uiContext.textWidth(mCaption)+3, uiContext.textHeight(mCaption));
 end;
 
 
@@ -2423,23 +2418,30 @@ var
   xpos: Integer;
 begin
   cidx := getColorIndex;
-  fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]);
+  uiContext.color := mBackColor[cidx];
+  uiContext.fillRect(gx, gy, mWidth, mHeight);
   if mHasFrame then
   begin
     // draw frame
-    drawRectUI(gx+3, gy+3, mWidth-6, mHeight-6, mFrameColor[cidx]);
+    uiContext.color := mFrameColor[cidx];
+    uiContext.rect(gx+3, gy+3, mWidth-6, mHeight-6);
   end;
   // draw caption
   if (Length(mCaption) > 0) then
   begin
          if (mHAlign < 0) then xpos := 3
-    else if (mHAlign > 0) then xpos := mWidth-mFrameWidth*2-Length(mCaption)*8
-    else xpos := (mWidth-mFrameWidth*2-Length(mCaption)*8) div 2;
+    else if (mHAlign > 0) then xpos := mWidth-mFrameWidth*2-uiContext.textWidth(mCaption)
+    else xpos := (mWidth-mFrameWidth*2-uiContext.textWidth(mCaption)) div 2;
     xpos += gx+mFrameWidth;
 
     setScissor(mFrameWidth+1, 0, mWidth-mFrameWidth-2, 8);
-    if mHasFrame then fillRect(xpos-3, gy, Length(mCaption)*8+4, 8, mBackColor[cidx]);
-    drawText8(xpos, gy, mCaption, mFrameTextColor[cidx]);
+    if mHasFrame then
+    begin
+      uiContext.color := mBackColor[cidx];
+      uiContext.fillRect(xpos-3, gy, uiContext.textWidth(mCaption)+4, 8);
+    end;
+    uiContext.color := mFrameTextColor[cidx];
+    uiContext.drawText(xpos, gy, mCaption);
   end;
 end;
 
@@ -2554,14 +2556,9 @@ var
   cidx: Integer;
 begin
   cidx := getColorIndex;
-  if mHoriz then
-  begin
-    drawHLine(gx, gy+(mHeight div 2), mWidth, mTextColor[cidx]);
-  end
-  else
-  begin
-    drawVLine(gx+(mWidth div 2), gy, mHeight, mTextColor[cidx]);
-  end;
+  uiContext.color := mTextColor[cidx];
+  if mHoriz then uiContext.hline(gx, gy+(mHeight div 2), mWidth)
+  else uiContext.vline(gx+(mWidth div 2), gy, mHeight);
 end;
 
 
@@ -2593,7 +2590,7 @@ begin
   mHoriz := true; // nobody cares
   mHeader := false;
   mLine := false;
-  mDefSize.h := 8;
+  mDefSize.h := uiContext.charHeight(' ');
   mCtl4Style := 'static';
 end;
 
@@ -2601,7 +2598,7 @@ end;
 procedure TUIStaticText.setText (const atext: AnsiString);
 begin
   mText := atext;
-  mDefSize := TLaySize.Create(Length(mText)*8, 8);
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText), uiContext.textHeight(mText));
 end;
 
 
@@ -2639,29 +2636,29 @@ procedure TUIStaticText.drawControl (gx, gy: Integer);
 var
   xpos, ypos: Integer;
   cidx: Integer;
-  clr: TGxRGBA;
 begin
   cidx := getColorIndex;
-  fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]);
+  uiContext.color := mBackColor[cidx];
+  uiContext.fillRect(gx, gy, mWidth, mHeight);
 
        if (mHAlign < 0) then xpos := 0
-  else if (mHAlign > 0) then xpos := mWidth-Length(mText)*8
-  else xpos := (mWidth-Length(mText)*8) div 2;
+  else if (mHAlign > 0) then xpos := mWidth-uiContext.textWidth(mText)
+  else xpos := (mWidth-uiContext.textWidth(mText)) div 2;
 
   if (Length(mText) > 0) then
   begin
-    if (mHeader) then clr := mFrameTextColor[cidx] else clr := mTextColor[cidx];
+    if (mHeader) then uiContext.color := mFrameTextColor[cidx] else uiContext.color := mTextColor[cidx];
 
          if (mVAlign < 0) then ypos := 0
-    else if (mVAlign > 0) then ypos := mHeight-8
-    else ypos := (mHeight-8) div 2;
+    else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText)
+    else ypos := (mHeight-uiContext.textHeight(mText)) div 2;
 
-    drawText8(gx+xpos, gy+ypos, mText, clr);
+    uiContext.drawText(gx+xpos, gy+ypos, mText);
   end;
 
   if (mLine) then
   begin
-    if (mHeader) then clr := mFrameColor[cidx] else clr := mTextColor[cidx];
+    if (mHeader) then uiContext.color := mFrameColor[cidx] else uiContext.color := mTextColor[cidx];
 
          if (mVAlign < 0) then ypos := 0
     else if (mVAlign > 0) then ypos := mHeight-1
@@ -2670,12 +2667,12 @@ begin
 
     if (Length(mText) = 0) then
     begin
-      drawHLine(gx, ypos, mWidth, clr);
+      uiContext.hline(gx, ypos, mWidth);
     end
     else
     begin
-      drawHLine(gx, ypos, xpos-1, clr);
-      drawHLine(gx+xpos+Length(mText)*8, ypos, mWidth, clr);
+      uiContext.hline(gx, ypos, xpos-1);
+      uiContext.hline(gx+xpos+uiContext.textWidth(mText), ypos, mWidth);
     end;
   end;
 end;
@@ -2688,7 +2685,7 @@ begin
   mHAlign := -1;
   mVAlign := 0;
   mCanFocus := false;
-  mDefSize := TLaySize.Create(Length(mText)*8, 8);
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText), uiContext.textHeight(mText));
   mCtl4Style := 'label';
   mLinkId := '';
 end;
@@ -2730,7 +2727,7 @@ begin
         if (mHotChar = #0) then
         begin
           mHotChar := s[f];
-          mHotOfs := Length(mText)*8;
+          mHotOfs := Length(mText);
         end;
         mText += s[f];
       end;
@@ -2742,7 +2739,13 @@ begin
       Inc(f);
     end;
   end;
-  mDefSize := TLaySize.Create(Length(mText)*8, 8);
+  // fix hotchar offset
+  if (mHotChar <> #0) and (mHotOfs > 0) then
+  begin
+    mHotOfs := uiContext.textWidth(Copy(mText, 1, mHotOfs+1))-uiContext.charWidth(mText[mHotOfs+1]);
+  end;
+  // fix size
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText), uiContext.textHeight(mText));
 end;
 
 
@@ -2776,22 +2779,25 @@ var
   cidx: Integer;
 begin
   cidx := getColorIndex;
-  fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]);
+  uiContext.color := mBackColor[cidx];
+  uiContext.fillRect(gx, gy, mWidth, mHeight);
   if (Length(mText) > 0) then
   begin
          if (mHAlign < 0) then xpos := 0
-    else if (mHAlign > 0) then xpos := mWidth-Length(mText)*8
-    else xpos := (mWidth-Length(mText)*8) div 2;
+    else if (mHAlign > 0) then xpos := mWidth-uiContext.textWidth(mText)
+    else xpos := (mWidth-uiContext.textWidth(mText)) div 2;
 
          if (mVAlign < 0) then ypos := 0
-    else if (mVAlign > 0) then ypos := mHeight-8
-    else ypos := (mHeight-8) div 2;
+    else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText)
+    else ypos := (mHeight-uiContext.textHeight(mText)) div 2;
 
-    drawText8(gx+xpos, gy+ypos, mText, mTextColor[cidx]);
+    uiContext.color := mTextColor[cidx];
+    uiContext.drawText(gx+xpos, gy+ypos, mText);
 
     if (Length(mLinkId) > 0) and (mHotChar <> #0) and (mHotChar <> ' ') then
     begin
-      drawText8(gx+xpos+8+mHotOfs, gy+ypos, mHotChar, mHotColor[cidx]);
+      uiContext.color := mHotColor[cidx];
+      uiContext.drawChar(gx+xpos+mHotOfs, gy+ypos, mHotChar);
     end;
   end;
 end;
@@ -2848,7 +2854,7 @@ begin
   mHAlign := -1;
   mVAlign := 0;
   mCanFocus := true;
-  mDefSize := TLaySize.Create(Length(mText)*8+8, 10);
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2);
   mCtl4Style := 'button';
 end;
 
@@ -2856,7 +2862,7 @@ end;
 procedure TUIButton.setText (const s: AnsiString);
 begin
   inherited setText(s);
-  mDefSize := TLaySize.Create(Length(mText)*8+8*2, 10);
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2);
 end;
 
 
@@ -2867,24 +2873,30 @@ var
 begin
   cidx := getColorIndex;
 
-  fillRect(gx+1, gy, mWidth-2, mHeight, mBackColor[cidx]);
-  fillRect(gx, gy+1, 1, mHeight-2, mBackColor[cidx]);
-  fillRect(gx+mWidth-1, gy+1, 1, mHeight-2, mBackColor[cidx]);
+  uiContext.color := mBackColor[cidx];
+  uiContext.fillRect(gx+1, gy, mWidth-2, mHeight);
+  uiContext.fillRect(gx, gy+1, 1, mHeight-2);
+  uiContext.fillRect(gx+mWidth-1, gy+1, 1, mHeight-2);
 
   if (Length(mText) > 0) then
   begin
          if (mHAlign < 0) then xpos := 0
-    else if (mHAlign > 0) then xpos := mWidth-Length(mText)*8
-    else xpos := (mWidth-Length(mText)*8) div 2;
+    else if (mHAlign > 0) then xpos := mWidth-uiContext.textWidth(mText)
+    else xpos := (mWidth-uiContext.textWidth(mText)) div 2;
 
          if (mVAlign < 0) then ypos := 0
-    else if (mVAlign > 0) then ypos := mHeight-8
-    else ypos := (mHeight-8) div 2;
+    else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText)
+    else ypos := (mHeight-uiContext.textHeight(mText)) div 2;
 
     setScissor(8, 0, mWidth-16, mHeight);
-    drawText8(gx+xpos+8, gy+ypos, mText, mTextColor[cidx]);
+    uiContext.color := mTextColor[cidx];
+    uiContext.drawText(gx+xpos+8, gy+ypos, mText);
 
-    if (mHotChar <> #0) and (mHotChar <> ' ') then drawText8(gx+xpos+8+mHotOfs, gy+ypos, mHotChar, mHotColor[cidx]);
+    if (mHotChar <> #0) and (mHotChar <> ' ') then
+    begin
+      uiContext.color := mHotColor[cidx];
+      uiContext.drawChar(gx+xpos+8+mHotOfs, gy+ypos, mHotChar);
+    end;
   end;
 end;
 
@@ -2930,7 +2942,8 @@ begin
   mHAlign := -1;
   mVAlign := 0;
   mCanFocus := true;
-  mDefSize := TLaySize.Create(Length(mText)*8+8*3, 8);
+  mIcon := TGxContext.TMarkIcon.Checkbox;
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText)+3+uiContext.iconMarkWidth(mIcon), uiContext.iconMarkHeight(mIcon));
   mCtl4Style := 'switchbox';
   mChecked := false;
   mBoolVar := @mChecked;
@@ -2952,7 +2965,7 @@ end;
 procedure TUISwitchBox.setText (const s: AnsiString);
 begin
   inherited setText(s);
-  mDefSize := TLaySize.Create(Length(mText)*8+8*3, 8);
+  mDefSize := TLaySize.Create(uiContext.textWidth(mText)+3+uiContext.iconMarkWidth(mIcon), uiContext.iconMarkHeight(mIcon));
 end;
 
 
@@ -2993,37 +3006,31 @@ begin
   cidx := getColorIndex;
 
        if (mHAlign < 0) then xpos := 0
-  else if (mHAlign > 0) then xpos := mWidth-(Length(mText)+4)*8
-  else xpos := (mWidth-(Length(mText)+4)*8) div 2;
+  else if (mHAlign > 0) then xpos := mWidth-(uiContext.textWidth(mText)+3+uiContext.iconMarkWidth(mIcon))
+  else xpos := (mWidth-(uiContext.textWidth(mText)+3+uiContext.iconMarkWidth(mIcon))) div 2;
 
        if (mVAlign < 0) then ypos := 0
-  else if (mVAlign > 0) then ypos := mHeight-8
-  else ypos := (mHeight-8) div 2;
+  else if (mVAlign > 0) then ypos := mHeight-uiContext.iconMarkHeight(mIcon)
+  else ypos := (mHeight-uiContext.iconMarkHeight(mIcon)) div 2;
 
+  uiContext.color := mBackColor[cidx];
+  uiContext.fillRect(gx, gy, mWidth, mHeight);
 
-  fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]);
+  uiContext.color := mSwitchColor[cidx];
+  uiContext.drawIconMark(mIcon, gx, gy, checked);
 
-  if (checked) then
-  begin
-    if (Length(mCheckedStr) <> 3) or (mCheckedStr[2] <> '*') then
-    begin
-      drawText8(gx+xpos, gy+ypos, mCheckedStr, mSwitchColor[cidx]);
-    end
-    else
-    begin
-      drawText8(gx+xpos, gy+ypos, mCheckedStr[1], mSwitchColor[cidx]);
-      drawText8(gx+xpos+2*8, gy+ypos, mCheckedStr[3], mSwitchColor[cidx]);
-      drawText8(gx+xpos+7, gy+ypos, '*', mSwitchColor[cidx]);
-    end;
-  end
-  else
-  begin
-    drawText8(gx+xpos, gy+ypos, mUncheckedStr, mSwitchColor[cidx]);
-  end;
+       if (mVAlign < 0) then ypos := 0
+  else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText)
+  else ypos := (mHeight-uiContext.textHeight(mText)) div 2;
 
-  drawText8(gx+xpos+8*3, gy+ypos, mText, mTextColor[cidx]);
+  uiContext.color := mTextColor[cidx];
+  uiContext.drawText(gx+xpos+3+uiContext.iconMarkWidth(mIcon), gy+ypos, mText);
 
-  if (mHotChar <> #0) and (mHotChar <> ' ') then drawText8(gx+xpos+8*3+mHotOfs, gy+ypos, mHotChar, mHotColor[cidx]);
+  if (mHotChar <> #0) and (mHotChar <> ' ') then
+  begin
+    uiContext.color := mHotColor[cidx];
+    uiContext.drawChar(gx+xpos+3+uiContext.iconMarkWidth(mIcon)+mHotOfs, gy+ypos, mHotChar);
+  end;
 end;
 
 
@@ -3067,8 +3074,8 @@ begin
   inherited;
   mChecked := false;
   mBoolVar := @mChecked;
-  mCheckedStr := '[x]';
-  mUncheckedStr := '[ ]';
+  mIcon := TGxContext.TMarkIcon.Checkbox;
+  setText('');
 end;
 
 
@@ -3097,9 +3104,9 @@ begin
   inherited;
   mChecked := false;
   mBoolVar := @mChecked;
-  mCheckedStr := '(*)';
-  mUncheckedStr := '( )';
   mRadioGroup := '';
+  mIcon := TGxContext.TMarkIcon.Radiobox;
+  setText('');
 end;
 
 
@@ -3164,4 +3171,6 @@ initialization
   registerCtlClass(TUIButton, 'button');
   registerCtlClass(TUICheckBox, 'checkbox');
   registerCtlClass(TUIRadioBox, 'radiobox');
+
+  uiContext := TGxContext.Create();
 end.
index 4a2e5bf1709df418a2060689d0b5449d83f81471..b0ccd2285e7af84a8ffa1f8e8444a1ebfca45378 100644 (file)
@@ -27,58 +27,112 @@ uses
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-// setup 2D OpenGL mode; will be called automatically in `glInit()`
-procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
-
-// the following calls MUST be paired AT ALL COSTS!
-procedure gxBeginUIDraw (scale: Single=1.0);
-procedure gxEndUIDraw ();
+type
+  TGxFont = class
+  protected
+    mName: AnsiString;
+    mHeight: Integer;
+    mBaseLine: Integer;
 
+  public
+    function charWidth (const ch: AnsiChar): Integer; virtual; abstract;
+    function textWidth (const s: AnsiString): Integer; virtual; abstract;
 
-type
-  TScissorSave = record
   public
-    wassc: Boolean;
-    scxywh: packed array[0..3] of GLint;
+    property name: AnsiString read mName;
+    property height: Integer read mHeight;
+    property baseLine: Integer read mBaseLine;
+  end;
 
+  TGxContext = class
   public
+    type
+      TMarkIcon = (
+        Checkbox,
+        Radiobox
+      );
+
+    type
+      TWinIcon = (
+        Close
+      );
+
+  protected
+    mActive: Boolean;
+    mColor: TGxRGBA;
+    mFont: TGxFont;
+    // for active contexts
+    mScaled: Boolean;
+    mScale: Single;
+    mClipRect: TGxRect;
+    mClipOfs: TGxOfs;
+
+  protected
+    function getFont (): AnsiString;
+    procedure setFont (const aname: AnsiString);
+
+    procedure onActivate ();
+    procedure onDeactivate ();
+
+    procedure setColor (const clr: TGxRGBA);
+
+    procedure realizeClip (); // setup scissoring
+
+    procedure setClipOfs (const aofs: TGxOfs);
+    procedure setClipRect (const aclip: TGxRect);
 
   public
-    procedure save (enableScissoring: Boolean);
-    procedure restore ();
+    constructor Create ();
+    destructor Destroy (); override;
 
-    // set new scissor rect, bounded by the saved scissor rect
-    procedure combineRect (x, y, w, h: Integer);
+    procedure line (x1, y1, x2, y2: Integer);
+    procedure hline (x, y, len: Integer);
+    procedure vline (x, y, len: Integer);
+    procedure rect (x, y, w, h: Integer);
+    procedure fillRect (x, y, w, h: Integer);
+    procedure darkenRect (x, y, w, h: Integer; a: Integer);
+
+    function charWidth (const ch: AnsiChar): Integer;
+    function charHeight (const ch: AnsiChar): Integer;
+    function textWidth (const s: AnsiString): Integer;
+    function textHeight (const s: AnsiString): Integer;
+    function drawChar (x, y: Integer; const ch: AnsiChar): Integer; // returns char width
+    function drawText (x, y: Integer; const s: AnsiString): Integer; // returns text width
+
+    function iconMarkWidth (ic: TMarkIcon): Integer;
+    function iconMarkHeight (ic: TMarkIcon): Integer;
+    procedure drawIconMark (ic: TMarkIcon; x, y: Integer; marked: Boolean);
+
+    function iconWinWidth (ic: TWinIcon): Integer;
+    function iconWinHeight (ic: TWinIcon): Integer;
+    procedure drawIconWin (ic: TWinIcon; x, y: Integer; pressed: Boolean);
+
+    procedure resetClip ();
+
+    function setOffset (constref aofs: TGxOfs): TGxOfs; // returns previous offset
+    function setClip (constref aclip: TGxRect): TGxRect; // returns previous clip
+
+    function combineClip (constref aclip: TGxRect): TGxRect; // returns previous clip
+
+  public
+    property active: Boolean read mActive;
+    property color: TGxRGBA read mColor write setColor;
+    property font: AnsiString read getFont write setFont;
+    property offset: TGxOfs read mClipOfs write setClipOfs;
+    property clip: TGxRect read mClipRect write setClipRect; // clipping is unaffected by offset
   end;
 
 
+// set active context; `ctx` can be `nil`
+procedure gxSetContext (ctx: TGxContext; ascale: Single=1.0);
+
+
+// setup 2D OpenGL mode; will be called automatically in `glInit()`
+procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
+
 procedure oglDrawCursor ();
 procedure oglDrawCursorAt (msX, msY: Integer);
 
-function setupGLColor (r, g, b, a: Integer): Boolean;
-function setupGLColor (constref clr: TGxRGBA): Boolean;
-function isScaled (): Boolean;
-
-function textWidth6 (const s: AnsiString): Integer;
-function textWidth8 (const s: AnsiString): Integer;
-// return width (including last empty pixel)
-function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; constref clr: TGxRGBA; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
-procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
-procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
-procedure drawHLine (x, y, len: Integer; constref clr: TGxRGBA);
-procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
-procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
-procedure darkenRect (x, y, w, h: Integer; a: Integer);
-procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
-function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-// x-centered at `x`
-function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
@@ -90,559 +144,427 @@ implementation
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
+//TODO: OpenGL framebuffers and shaders state
+type
+  TSavedGLState = record
+  public
+    glmatmode: GLint;
+    gltextbinding: GLint;
+    //oldprg: GLint;
+    //oldfbr, oldfbw: GLint;
+    glvport: packed array [0..3] of GLint;
+    saved: Boolean;
+
+  public
+    constructor Create (dosave: Boolean);
+    procedure save ();
+    procedure restore ();
+  end;
+
+constructor TSavedGLState.Create (dosave: Boolean);
 begin
-  glViewport(0, 0, winWidth, winHeight);
+  FillChar(self, sizeof(self), 0);
+  if (dosave) then save();
+end;
 
-  glDisable(GL_BLEND);
-  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  glDisable(GL_LINE_SMOOTH);
-  glDisable(GL_POINT_SMOOTH);
-  glDisable(GL_DEPTH_TEST);
-  glDisable(GL_TEXTURE_2D);
-  glDisable(GL_LIGHTING);
-  glDisable(GL_DITHER);
-  glDisable(GL_STENCIL_TEST);
-  glDisable(GL_SCISSOR_TEST);
-  glDisable(GL_CULL_FACE);
+procedure TSavedGLState.save ();
+begin
+  if (saved) then raise Exception.Create('cannot save into already saved OpenGL state');
+  glGetIntegerv(GL_MATRIX_MODE, @glmatmode);
+  glGetIntegerv(GL_TEXTURE_BINDING_2D, @gltextbinding);
+  glGetIntegerv(GL_VIEWPORT, @glvport[0]);
+  //glGetIntegerv(GL_CURRENT_PROGRAM, &oldprg);
+  //glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldfbr);
+  //glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldfbw);
+  glMatrixMode(GL_PROJECTION); glPushMatrix();
+  glMatrixMode(GL_MODELVIEW); glPushMatrix();
+  glMatrixMode(GL_TEXTURE); glPushMatrix();
+  glMatrixMode(GL_COLOR); glPushMatrix();
+  glPushAttrib({GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_CURRENT_BIT}GL_ALL_ATTRIB_BITS); // let's play safe
+  saved := true;
+end;
 
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  if (upsideDown) then
+procedure TSavedGLState.restore ();
+begin
+  if (not saved) then raise Exception.Create('cannot restore unsaved OpenGL state');
+  glPopAttrib({GL_ENABLE_BIT});
+  glMatrixMode(GL_PROJECTION); glPopMatrix();
+  glMatrixMode(GL_MODELVIEW); glPopMatrix();
+  glMatrixMode(GL_TEXTURE); glPopMatrix();
+  glMatrixMode(GL_COLOR); glPopMatrix();
+  glMatrixMode(glmatmode);
+  //if (glHasFunc!"glBindFramebufferEXT") glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, oldfbr);
+  //if (glHasFunc!"glBindFramebufferEXT") glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, oldfbw);
+  glBindTexture(GL_TEXTURE_2D, gltextbinding);
+  //if (glHasFunc!"glUseProgram") glUseProgram(oldprg);
+  glViewport(glvport[0], glvport[1], glvport[2], glvport[3]);
+  saved := false;
+end;
+
+
+var
+  curCtx: TGxContext = nil;
+  savedGLState: TSavedGLState;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+// set active context; `ctx` can be `nil`
+procedure gxSetContext (ctx: TGxContext; ascale: Single=1.0);
+begin
+  if (savedGLState.saved) then savedGLState.restore();
+
+  if (curCtx <> nil) then
   begin
-    glOrtho(0, winWidth, 0, winHeight, -1, 1); // set origin to bottom left
-  end
-  else
+    curCtx.onDeactivate();
+    curCtx.mActive := false;
+  end;
+
+  curCtx := ctx;
+  if (ctx <> nil) then
   begin
-    glOrtho(0, winWidth, winHeight, 0, -1, 1); // set origin to top left
+    ctx.mActive := true;
+    savedGLState.save();
+    oglSetup2D(fuiScrWdt, fuiScrHgt);
+    glScalef(ascale, ascale, 1.0);
+    ctx.mScaled := (ascale <> 1.0);
+    ctx.mScale := ascale;
+    ctx.onActivate();
   end;
+end;
 
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
 
-  glClearColor(0, 0, 0, 0);
-  glColor4f(1, 1, 1, 1);
-end;
+// ////////////////////////////////////////////////////////////////////////// //
+type
+  TScissorSave = record
+  public
+    wassc: Boolean;
+    scxywh: packed array[0..3] of GLint;
+
+  public
+
+  public
+    procedure save (enableScissoring: Boolean);
+    procedure restore ();
+
+    // set new scissor rect, bounded by the saved scissor rect
+    procedure combineRect (x, y, w, h: Integer);
+  end;
 
 
-procedure gxBeginUIDraw (scale: Single=1.0);
+procedure TScissorSave.save (enableScissoring: Boolean);
 begin
-  glMatrixMode(GL_MODELVIEW);
-  glPushMatrix();
-  glLoadIdentity();
-  glScalef(scale, scale, 1);
+  wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
+  if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
+  //conwritefln('(%d,%d)-(%d,%d)', [scxywh[0], scxywh[1], scxywh[2], scxywh[3]]);
+  if enableScissoring and (not wassc) then glEnable(GL_SCISSOR_TEST);
 end;
 
-procedure gxEndUIDraw ();
+procedure TScissorSave.restore ();
 begin
-  glMatrixMode(GL_MODELVIEW);
-  glPopMatrix();
+  glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
+  if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
+end;
+
+procedure TScissorSave.combineRect (x, y, w, h: Integer);
+//var ox, oy, ow, oh: Integer;
+begin
+  if (w < 1) or (h < 1) then begin glScissor(0, 0, 0, 0); exit; end;
+  y := fuiScrHgt-(y+h);
+  //ox := x; oy := y; ow := w; oh := h;
+  if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then
+  begin
+    //writeln('oops: COMBINE: old=(', ox, ',', oy, ')-(', ox+ow-1, ',', oy+oh-1, '); sci: (', scxywh[0], ',', scxywh[1], ')-(', scxywh[0]+scxywh[2]-1, ',', scxywh[1]+scxywh[3]-1, ')');
+    //writeln('oops: COMBINE: oldx=<', ox, '-', ox+ow-1, '>; oldy=<', oy, ',', oy+oh-1, '> : scix=<', scxywh[0], '-', scxywh[0]+scxywh[2]-1, '>; sciy=<', scxywh[1], '-', scxywh[1]+scxywh[3]-1, '>');
+    glScissor(0, 0, 0, 0);
+  end
+  else
+  begin
+    glScissor(x, y, w, h);
+  end;
 end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-// cursor (hi, Death Track!)
-const curTexWidth = 32;
-const curTexHeight = 32;
-const curWidth = 17;
-const curHeight = 23;
-
-const cursorImg: array[0..curWidth*curHeight-1] of Byte = (
-  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,
-  3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,0,0,
-  3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,0,0,
-  3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,0,0,
-  3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,0,0,
-  3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,0,0,
-  3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,0,0,
-  3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,0,0,
-  3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,0,0,
-  3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,0,0,
-  3,3,5,6,3,3,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,6,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-);
-const cursorPal: array[0..9*4-1] of Byte = (
-    0,  0,  0,  0,
-    0,  0,  0,163,
-   85,255,255,255,
-   85, 85,255,255,
-  255, 85, 85,255,
-  170,  0,170,255,
-   85, 85, 85,255,
-    0,  0,  0,255,
-    0,  0,170,255
-);
+{$INCLUDE fui_gfx_gl_fonts.inc}
 
+type
+  TGxBmpFont = class(TGxFont)
+  private
+    mTexId: GLuint; // OpenGL texture id
+    mWidth: Integer; // <=0: proportional
+    mFontBmp: PByte;
+    mFontWdt: PByte;
+    mFreeFontWdt: Boolean;
+
+  protected
+    procedure oglCreateTexture ();
+    procedure oglDestroyTexture ();
+
+    function drawTextInternal (x, y: Integer; const s: AnsiString): Integer; // return width (not including last empty pixel)
+
+  public
+    constructor Create (const aname: AnsiString; awdt, ahgt: Integer; const afont: PByte; const awdtable: PByte=nil);
+    destructor Destroy (); override;
+
+    function charWidth (const ch: AnsiChar): Integer; override;
+    function textWidth (const s: AnsiString): Integer; override;
+  end;
 
-var
-  curtexid: GLuint = 0;
 
-procedure createCursorTexture ();
+constructor TGxBmpFont.Create (const aname: AnsiString; awdt, ahgt: Integer; const afont: PByte; const awdtable: PByte=nil);
 var
-  tex, tpp: PByte;
   c: Integer;
-  x, y: Integer;
 begin
-  if (curtexid <> 0) then exit; //begin glDeleteTextures(1, @curtexid); curtexid := 0; end;
-
-  GetMem(tex, curTexWidth*curTexHeight*4);
-  try
-    FillChar(tex^, curTexWidth*curTexHeight*4, 0);
+  if (afont = nil) then raise Exception.Create('internal error in font creation');
+  if (ahgt < 1) then raise Exception.Create('internal error in font creation');
+  if (awdt > 0) then
+  begin
+    //if (awdtable <> nil) then raise Exception.Create('internal error in font creation');
+    mFreeFontWdt := true;
+    // create width table
+    GetMem(mFontWdt, 256);
+    for c := 0 to 255 do mFontWdt[c] := awdt-1;
+  end
+  else
+  begin
+    if (awdtable = nil) then raise Exception.Create('internal error in font creation');
+    awdt := 0;
+    mFontWdt := awdtable;
+  end;
+  mName := aname;
+  mWidth := awdt;
+  mHeight := ahgt;
+  mBaseLine := ahgt-1; //FIXME
+  mFontBmp := afont;
+  mTexId := 0;
+end;
 
-    // draw shadow
-    for y := 0 to curHeight-1 do
-    begin
-      for x := 0 to curWidth-1 do
-      begin
-        if (cursorImg[y*curWidth+x] <> 0) then
-        begin
-          c := 1*4;
-          tpp := tex+((y+1)*(curTexWidth*4)+(x+3)*4);
-          tpp^ := cursorPal[c+0]; Inc(tpp);
-          tpp^ := cursorPal[c+1]; Inc(tpp);
-          tpp^ := cursorPal[c+2]; Inc(tpp);
-          tpp^ := cursorPal[c+3]; Inc(tpp);
-          tpp^ := cursorPal[c+0]; Inc(tpp);
-          tpp^ := cursorPal[c+1]; Inc(tpp);
-          tpp^ := cursorPal[c+2]; Inc(tpp);
-          tpp^ := cursorPal[c+3]; Inc(tpp);
-        end;
-      end;
-    end;
 
-    // draw cursor
-    for y := 0 to curHeight-1 do
-    begin
-      for x := 0 to curWidth-1 do
-      begin
-        c := cursorImg[y*curWidth+x]*4;
-        if (c <> 0) then
-        begin
-          tpp := tex+(y*(curTexWidth*4)+x*4);
-          tpp^ := cursorPal[c+0]; Inc(tpp);
-          tpp^ := cursorPal[c+1]; Inc(tpp);
-          tpp^ := cursorPal[c+2]; Inc(tpp);
-          tpp^ := cursorPal[c+3]; Inc(tpp);
-        end;
-      end;
-    end;
+destructor TGxBmpFont.Destroy ();
+begin
+  if (mFreeFontWdt) and (mFontWdt <> nil) then FreeMem(mFontWdt);
+  mName := '';
+  mWidth := 0;
+  mHeight := 0;
+  mBaseLine := 0;
+  mFontBmp := nil;
+  mFontWdt := nil;
+  mFreeFontWdt := false;
+  mTexId := 0;
+  inherited;
+end;
 
-    glGenTextures(1, @curtexid);
-    if (curtexid = 0) then raise Exception.Create('can''t create cursor texture');
 
-    glBindTexture(GL_TEXTURE_2D, curtexid);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+procedure TGxBmpFont.oglCreateTexture ();
+begin
+  mTexId := createFontTexture(mFontBmp, mFontWdt, (mWidth <= 0));
+end;
 
-    //GLfloat[4] bclr = 0.0;
-    //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
 
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, curTexWidth, curTexHeight, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
-    glFlush();
-  finally
-    FreeMem(tex);
+procedure TGxBmpFont.oglDestroyTexture ();
+begin
+  if (mTexId <> 0) then
+  begin
+    glDeleteTextures(1, @mTexId);
+    mTexId := 0;
   end;
 end;
 
-procedure oglDrawCursorAt (msX, msY: Integer);
+
+function TGxBmpFont.charWidth (const ch: AnsiChar): Integer;
 begin
-  //if (curtexid = 0) then createCursorTexture() else glBindTexture(GL_TEXTURE_2D, curtexid);
-  glBindTexture(GL_TEXTURE_2D, curtexid);
-  // blend it
-  glEnable(GL_BLEND);
-  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  glEnable(GL_TEXTURE_2D);
-  glDisable(GL_STENCIL_TEST);
-  glDisable(GL_SCISSOR_TEST);
-  glDisable(GL_LIGHTING);
-  glDisable(GL_DEPTH_TEST);
-  glDisable(GL_CULL_FACE);
-  // color and opacity
-  glColor4f(1, 1, 1, 0.9);
-  //Dec(msX, 2);
-  glBegin(GL_QUADS);
-    glTexCoord2f(0.0, 0.0); glVertex2i(msX, msY); // top-left
-    glTexCoord2f(1.0, 0.0); glVertex2i(msX+curTexWidth, msY); // top-right
-    glTexCoord2f(1.0, 1.0); glVertex2i(msX+curTexWidth, msY+curTexHeight); // bottom-right
-    glTexCoord2f(0.0, 1.0); glVertex2i(msX, msY+curTexHeight); // bottom-left
-  glEnd();
-  //Inc(msX, 2);
-  glDisable(GL_BLEND);
-  glDisable(GL_TEXTURE_2D);
-  glColor4f(1, 1, 1, 1);
-  glBindTexture(GL_TEXTURE_2D, 0);
+  result := (mFontWdt[Byte(ch)] and $0f);
 end;
 
-procedure oglDrawCursor (); begin oglDrawCursorAt(fuiMouseX, fuiMouseY); end;
+
+function TGxBmpFont.textWidth (const s: AnsiString): Integer;
+var
+  ch: AnsiChar;
+begin
+  if (Length(s) > 0) then
+  begin
+    result := -1;
+    for ch in s do result += (mFontWdt[Byte(ch)] and $0f)+1;
+  end
+  else
+  begin
+    result := 0;
+  end;
+end;
 
 
-// ////////////////////////////////////////////////////////////////////////// //
-// fonts
-const kgiFont6: array[0..256*8-1] of Byte  = (
-$00,$00,$00,$00,$00,$00,$00,$00,$3c,$42,$a5,$81,$a5,$99,$42,$3c,$3c,$7e,$db,$ff,$ff,$db,$66,$3c,$6c,$fe,
-$fe,$fe,$7c,$38,$10,$00,$10,$38,$7c,$fe,$7c,$38,$10,$00,$10,$38,$54,$fe,$54,$10,$38,$00,$10,$38,$7c,$fe,
-$fe,$10,$38,$00,$00,$00,$00,$30,$30,$00,$00,$00,$ff,$ff,$ff,$e7,$e7,$ff,$ff,$ff,$38,$44,$82,$82,$82,$44,
-$38,$00,$c7,$bb,$7d,$7d,$7d,$bb,$c7,$ff,$0f,$03,$05,$79,$88,$88,$88,$70,$38,$44,$44,$44,$38,$10,$7c,$10,
-$30,$28,$24,$24,$28,$20,$e0,$c0,$3c,$24,$3c,$24,$24,$e4,$dc,$18,$10,$54,$38,$ee,$38,$54,$10,$00,$10,$10,
-$10,$7c,$10,$10,$10,$10,$10,$10,$10,$ff,$00,$00,$00,$00,$00,$00,$00,$ff,$10,$10,$10,$10,$10,$10,$10,$f0,
-$10,$10,$10,$10,$10,$10,$10,$1f,$10,$10,$10,$10,$10,$10,$10,$ff,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,
-$10,$10,$00,$00,$00,$ff,$00,$00,$00,$00,$00,$00,$00,$1f,$10,$10,$10,$10,$00,$00,$00,$f0,$10,$10,$10,$10,
-$10,$10,$10,$1f,$00,$00,$00,$00,$10,$10,$10,$f0,$00,$00,$00,$00,$81,$42,$24,$18,$18,$24,$42,$81,$01,$02,
-$04,$08,$10,$20,$40,$80,$80,$40,$20,$10,$08,$04,$02,$01,$00,$10,$10,$ff,$10,$10,$00,$00,$00,$00,$00,$00,
-$00,$00,$00,$00,$20,$20,$20,$20,$00,$00,$20,$00,$50,$50,$50,$00,$00,$00,$00,$00,$50,$50,$f8,$50,$f8,$50,
-$50,$00,$20,$78,$a0,$70,$28,$f0,$20,$00,$c0,$c8,$10,$20,$40,$98,$18,$00,$40,$a0,$40,$a8,$90,$98,$60,$00,
-$10,$20,$40,$00,$00,$00,$00,$00,$10,$20,$40,$40,$40,$20,$10,$00,$40,$20,$10,$10,$10,$20,$40,$00,$88,$50,
-$20,$f8,$20,$50,$88,$00,$00,$20,$20,$f8,$20,$20,$00,$00,$00,$00,$00,$00,$00,$20,$20,$40,$00,$00,$00,$78,
-$00,$00,$00,$00,$00,$00,$00,$00,$00,$60,$60,$00,$00,$00,$08,$10,$20,$40,$80,$00,$70,$88,$98,$a8,$c8,$88,
-$70,$00,$20,$60,$a0,$20,$20,$20,$f8,$00,$70,$88,$08,$10,$60,$80,$f8,$00,$70,$88,$08,$30,$08,$88,$70,$00,
-$10,$30,$50,$90,$f8,$10,$10,$00,$f8,$80,$e0,$10,$08,$10,$e0,$00,$30,$40,$80,$f0,$88,$88,$70,$00,$f8,$88,
-$10,$20,$20,$20,$20,$00,$70,$88,$88,$70,$88,$88,$70,$00,$70,$88,$88,$78,$08,$10,$60,$00,$00,$00,$20,$00,
-$00,$20,$00,$00,$00,$00,$20,$00,$00,$20,$20,$40,$18,$30,$60,$c0,$60,$30,$18,$00,$00,$00,$f8,$00,$f8,$00,
-$00,$00,$c0,$60,$30,$18,$30,$60,$c0,$00,$70,$88,$08,$10,$20,$00,$20,$00,$70,$88,$08,$68,$a8,$a8,$70,$00,
-$20,$50,$88,$88,$f8,$88,$88,$00,$f0,$48,$48,$70,$48,$48,$f0,$00,$30,$48,$80,$80,$80,$48,$30,$00,$e0,$50,
-$48,$48,$48,$50,$e0,$00,$f8,$80,$80,$f0,$80,$80,$f8,$00,$f8,$80,$80,$f0,$80,$80,$80,$00,$70,$88,$80,$b8,
-$88,$88,$70,$00,$88,$88,$88,$f8,$88,$88,$88,$00,$70,$20,$20,$20,$20,$20,$70,$00,$38,$10,$10,$10,$90,$90,
-$60,$00,$88,$90,$a0,$c0,$a0,$90,$88,$00,$80,$80,$80,$80,$80,$80,$f8,$00,$88,$d8,$a8,$a8,$88,$88,$88,$00,
-$88,$c8,$c8,$a8,$98,$98,$88,$00,$70,$88,$88,$88,$88,$88,$70,$00,$f0,$88,$88,$f0,$80,$80,$80,$00,$70,$88,
-$88,$88,$a8,$90,$68,$00,$f0,$88,$88,$f0,$a0,$90,$88,$00,$70,$88,$80,$70,$08,$88,$70,$00,$f8,$20,$20,$20,
-$20,$20,$20,$00,$88,$88,$88,$88,$88,$88,$70,$00,$88,$88,$88,$88,$50,$50,$20,$00,$88,$88,$88,$a8,$a8,$d8,
-$88,$00,$88,$88,$50,$20,$50,$88,$88,$00,$88,$88,$88,$70,$20,$20,$20,$00,$f8,$08,$10,$20,$40,$80,$f8,$00,
-$70,$40,$40,$40,$40,$40,$70,$00,$00,$00,$80,$40,$20,$10,$08,$00,$70,$10,$10,$10,$10,$10,$70,$00,$20,$50,
-$88,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$f8,$00,$40,$20,$10,$00,$00,$00,$00,$00,$00,$00,$70,$08,
-$78,$88,$78,$00,$80,$80,$b0,$c8,$88,$c8,$b0,$00,$00,$00,$70,$88,$80,$88,$70,$00,$08,$08,$68,$98,$88,$98,
-$68,$00,$00,$00,$70,$88,$f8,$80,$70,$00,$10,$28,$20,$f8,$20,$20,$20,$00,$00,$00,$68,$98,$98,$68,$08,$70,
-$80,$80,$f0,$88,$88,$88,$88,$00,$20,$00,$60,$20,$20,$20,$70,$00,$10,$00,$30,$10,$10,$10,$90,$60,$40,$40,
-$48,$50,$60,$50,$48,$00,$60,$20,$20,$20,$20,$20,$70,$00,$00,$00,$d0,$a8,$a8,$a8,$a8,$00,$00,$00,$b0,$c8,
-$88,$88,$88,$00,$00,$00,$70,$88,$88,$88,$70,$00,$00,$00,$b0,$c8,$c8,$b0,$80,$80,$00,$00,$68,$98,$98,$68,
-$08,$08,$00,$00,$b0,$c8,$80,$80,$80,$00,$00,$00,$78,$80,$f0,$08,$f0,$00,$40,$40,$f0,$40,$40,$48,$30,$00,
-$00,$00,$90,$90,$90,$90,$68,$00,$00,$00,$88,$88,$88,$50,$20,$00,$00,$00,$88,$a8,$a8,$a8,$50,$00,$00,$00,
-$88,$50,$20,$50,$88,$00,$00,$00,$88,$88,$98,$68,$08,$70,$00,$00,$f8,$10,$20,$40,$f8,$00,$18,$20,$20,$40,
-$20,$20,$18,$00,$20,$20,$20,$00,$20,$20,$20,$00,$c0,$20,$20,$10,$20,$20,$c0,$00,$40,$a8,$10,$00,$00,$00,
-$00,$00,$00,$00,$20,$50,$f8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$ff,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,
-$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$3c,$3c,$00,$00,$00,$ff,$ff,
-$ff,$ff,$ff,$ff,$00,$00,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$0f,$0f,$0f,$0f,$f0,$f0,$f0,$f0,$fc,$fc,$fc,$fc,
-$fc,$fc,$fc,$fc,$03,$03,$03,$03,$03,$03,$03,$03,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$11,$22,$44,$88,$11,$22,
-$44,$88,$88,$44,$22,$11,$88,$44,$22,$11,$fe,$7c,$38,$10,$00,$00,$00,$00,$00,$00,$00,$00,$10,$38,$7c,$fe,
-$80,$c0,$e0,$f0,$e0,$c0,$80,$00,$01,$03,$07,$0f,$07,$03,$01,$00,$ff,$7e,$3c,$18,$18,$3c,$7e,$ff,$81,$c3,
-$e7,$ff,$ff,$e7,$c3,$81,$f0,$f0,$f0,$f0,$00,$00,$00,$00,$00,$00,$00,$00,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,
-$00,$00,$00,$00,$00,$00,$00,$00,$f0,$f0,$f0,$f0,$33,$33,$cc,$cc,$33,$33,$cc,$cc,$00,$20,$20,$50,$50,$88,
-$f8,$00,$20,$20,$70,$20,$70,$20,$20,$00,$00,$00,$00,$50,$88,$a8,$50,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,
-$00,$00,$00,$00,$ff,$ff,$ff,$ff,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$ff,$ff,
-$ff,$ff,$00,$00,$00,$00,$00,$00,$68,$90,$90,$90,$68,$00,$30,$48,$48,$70,$48,$48,$70,$c0,$f8,$88,$80,$80,
-$80,$80,$80,$00,$00,$50,$70,$88,$f8,$80,$70,$00,$00,$00,$78,$80,$f0,$80,$78,$00,$00,$00,$78,$90,$90,$90,
-$60,$00,$20,$00,$60,$20,$20,$20,$70,$00,$50,$00,$70,$20,$20,$20,$70,$00,$f8,$20,$70,$a8,$a8,$70,$20,$f8,
-$20,$50,$88,$f8,$88,$50,$20,$00,$70,$88,$88,$88,$50,$50,$d8,$00,$30,$40,$40,$20,$50,$50,$50,$20,$00,$00,
-$00,$50,$a8,$a8,$50,$00,$08,$70,$a8,$a8,$a8,$70,$80,$00,$38,$40,$80,$f8,$80,$40,$38,$00,$70,$88,$88,$88,
-$88,$88,$88,$00,$00,$f8,$00,$f8,$00,$f8,$00,$00,$20,$20,$f8,$20,$20,$00,$f8,$00,$c0,$30,$08,$30,$c0,$00,
-$f8,$00,$50,$f8,$80,$f0,$80,$80,$f8,$00,$78,$80,$80,$f0,$80,$80,$78,$00,$20,$20,$20,$20,$20,$20,$a0,$40,
-$70,$20,$20,$20,$20,$20,$70,$00,$50,$70,$20,$20,$20,$20,$70,$00,$00,$18,$24,$24,$18,$00,$00,$00,$00,$30,
-$78,$78,$30,$00,$00,$00,$00,$00,$00,$00,$30,$00,$00,$00,$3e,$20,$20,$20,$a0,$60,$20,$00,$a0,$50,$50,$50,
-$00,$00,$00,$00,$40,$a0,$20,$40,$e0,$00,$00,$00,$00,$38,$38,$38,$38,$38,$38,$00,$3c,$42,$99,$a1,$a1,$99,
-$42,$3c,$00,$00,$90,$a8,$e8,$a8,$90,$00,$00,$00,$60,$10,$70,$90,$68,$00,$00,$00,$f0,$80,$f0,$88,$f0,$00,
-$00,$00,$90,$90,$90,$f8,$08,$00,$00,$00,$30,$50,$50,$70,$88,$00,$00,$00,$70,$88,$f8,$80,$70,$00,$00,$20,
-$70,$a8,$a8,$70,$20,$00,$00,$00,$78,$48,$40,$40,$40,$00,$00,$00,$88,$50,$20,$50,$88,$00,$00,$00,$88,$98,
-$a8,$c8,$88,$00,$00,$50,$20,$00,$98,$a8,$c8,$00,$00,$00,$90,$a0,$c0,$a0,$90,$00,$00,$00,$38,$28,$28,$48,
-$88,$00,$00,$00,$88,$d8,$a8,$88,$88,$00,$00,$00,$88,$88,$f8,$88,$88,$00,$00,$00,$70,$88,$88,$88,$70,$00,
-$00,$00,$78,$48,$48,$48,$48,$00,$00,$00,$78,$88,$78,$28,$48,$00,$00,$00,$f0,$88,$f0,$80,$80,$00,$00,$00,
-$78,$80,$80,$80,$78,$00,$00,$00,$f8,$20,$20,$20,$20,$00,$00,$00,$88,$50,$20,$40,$80,$00,$00,$00,$a8,$70,
-$20,$70,$a8,$00,$00,$00,$f0,$48,$70,$48,$f0,$00,$00,$00,$40,$40,$70,$48,$70,$00,$00,$00,$88,$88,$c8,$a8,
-$c8,$00,$00,$00,$f0,$08,$70,$08,$f0,$00,$00,$00,$a8,$a8,$a8,$a8,$f8,$00,$00,$00,$70,$88,$38,$88,$70,$00,
-$00,$00,$a8,$a8,$a8,$f8,$08,$00,$00,$00,$48,$48,$78,$08,$08,$00,$00,$00,$c0,$40,$70,$48,$70,$00,$90,$a8,
-$a8,$e8,$a8,$a8,$90,$00,$20,$50,$88,$88,$f8,$88,$88,$00,$f8,$88,$80,$f0,$88,$88,$f0,$00,$90,$90,$90,$90,
-$90,$f8,$08,$00,$38,$28,$28,$48,$48,$f8,$88,$00,$f8,$80,$80,$f0,$80,$80,$f8,$00,$20,$70,$a8,$a8,$a8,$70,
-$20,$00,$f8,$88,$88,$80,$80,$80,$80,$00,$88,$88,$50,$20,$50,$88,$88,$00,$88,$88,$98,$a8,$c8,$88,$88,$00,
-$50,$20,$88,$98,$a8,$c8,$88,$00,$88,$90,$a0,$c0,$a0,$90,$88,$00,$18,$28,$48,$48,$48,$48,$88,$00,$88,$d8,
-$a8,$a8,$88,$88,$88,$00,$88,$88,$88,$f8,$88,$88,$88,$00,$70,$88,$88,$88,$88,$88,$70,$00,$f8,$88,$88,$88,
-$88,$88,$88,$00,$78,$88,$88,$78,$28,$48,$88,$00,$f0,$88,$88,$f0,$80,$80,$80,$00,$70,$88,$80,$80,$80,$88,
-$70,$00,$f8,$20,$20,$20,$20,$20,$20,$00,$88,$88,$88,$50,$20,$40,$80,$00,$a8,$a8,$70,$20,$70,$a8,$a8,$00,
-$f0,$48,$48,$70,$48,$48,$f0,$00,$80,$80,$80,$f0,$88,$88,$f0,$00,$88,$88,$88,$c8,$a8,$a8,$c8,$00,$f0,$08,
-$08,$30,$08,$08,$f0,$00,$a8,$a8,$a8,$a8,$a8,$a8,$f8,$00,$70,$88,$08,$78,$08,$88,$70,$00,$a8,$a8,$a8,$a8,
-$a8,$f8,$08,$00,$88,$88,$88,$88,$78,$08,$08,$00,$c0,$40,$40,$70,$48,$48,$70,$00
-);
-
-const kgiFont8: array[0..256*8-1] of Byte  = (
-$00,$00,$00,$00,$00,$00,$00,$00,$7e,$81,$a5,$81,$bd,$99,$81,$7e,$7e,$ff,$db,$ff,$c3,$e7,$ff,$7e,$6c,$fe,
-$fe,$fe,$7c,$38,$10,$00,$10,$38,$7c,$fe,$7c,$38,$10,$00,$38,$7c,$38,$fe,$fe,$d6,$10,$38,$10,$10,$38,$7c,
-$fe,$7c,$10,$38,$00,$00,$18,$3c,$3c,$18,$00,$00,$ff,$ff,$e7,$c3,$c3,$e7,$ff,$ff,$00,$3c,$66,$42,$42,$66,
-$3c,$00,$ff,$c3,$99,$bd,$bd,$99,$c3,$ff,$0f,$07,$0f,$7d,$cc,$cc,$cc,$78,$3c,$66,$66,$66,$3c,$18,$7e,$18,
-$3f,$33,$3f,$30,$30,$70,$f0,$e0,$7f,$63,$7f,$63,$63,$67,$e6,$c0,$99,$5a,$3c,$e7,$e7,$3c,$5a,$99,$80,$e0,
-$f8,$fe,$f8,$e0,$80,$00,$02,$0e,$3e,$fe,$3e,$0e,$02,$00,$18,$3c,$7e,$18,$18,$7e,$3c,$18,$66,$66,$66,$66,
-$66,$00,$66,$00,$7f,$db,$db,$7b,$1b,$1b,$1b,$00,$7e,$c3,$78,$cc,$cc,$78,$8c,$f8,$00,$00,$00,$00,$7e,$7e,
-$7e,$00,$18,$3c,$7e,$18,$7e,$3c,$18,$ff,$18,$3c,$7e,$18,$18,$18,$18,$00,$18,$18,$18,$18,$7e,$3c,$18,$00,
-$00,$18,$0c,$fe,$0c,$18,$00,$00,$00,$30,$60,$fe,$60,$30,$00,$00,$00,$00,$c0,$c0,$c0,$fe,$00,$00,$00,$24,
-$66,$ff,$66,$24,$00,$00,$00,$18,$3c,$7e,$ff,$ff,$00,$00,$00,$ff,$ff,$7e,$3c,$18,$00,$00,$00,$00,$00,$00,
-$00,$00,$00,$00,$30,$78,$78,$30,$30,$00,$30,$00,$6c,$6c,$6c,$00,$00,$00,$00,$00,$6c,$6c,$fe,$6c,$fe,$6c,
-$6c,$00,$30,$7c,$c0,$78,$0c,$f8,$30,$00,$00,$c6,$cc,$18,$30,$66,$c6,$00,$38,$6c,$38,$76,$dc,$cc,$76,$00,
-$60,$60,$c0,$00,$00,$00,$00,$00,$18,$30,$60,$60,$60,$30,$18,$00,$60,$30,$18,$18,$18,$30,$60,$00,$00,$66,
-$3c,$ff,$3c,$66,$00,$00,$00,$30,$30,$fc,$30,$30,$00,$00,$00,$00,$00,$00,$00,$70,$30,$60,$00,$00,$00,$fc,
-$00,$00,$00,$00,$00,$00,$00,$00,$00,$30,$30,$00,$06,$0c,$18,$30,$60,$c0,$80,$00,$78,$cc,$dc,$fc,$ec,$cc,
-$78,$00,$30,$f0,$30,$30,$30,$30,$fc,$00,$78,$cc,$0c,$38,$60,$cc,$fc,$00,$78,$cc,$0c,$38,$0c,$cc,$78,$00,
-$1c,$3c,$6c,$cc,$fe,$0c,$0c,$00,$fc,$c0,$f8,$0c,$0c,$cc,$78,$00,$38,$60,$c0,$f8,$cc,$cc,$78,$00,$fc,$cc,
-$0c,$18,$30,$60,$60,$00,$78,$cc,$cc,$78,$cc,$cc,$78,$00,$78,$cc,$cc,$7c,$0c,$18,$70,$00,$00,$00,$30,$30,
-$00,$30,$30,$00,$00,$00,$30,$30,$00,$70,$30,$60,$18,$30,$60,$c0,$60,$30,$18,$00,$00,$00,$fc,$00,$fc,$00,
-$00,$00,$60,$30,$18,$0c,$18,$30,$60,$00,$78,$cc,$0c,$18,$30,$00,$30,$00,$7c,$c6,$de,$de,$de,$c0,$78,$00,
-$30,$78,$cc,$cc,$fc,$cc,$cc,$00,$fc,$66,$66,$7c,$66,$66,$fc,$00,$3c,$66,$c0,$c0,$c0,$66,$3c,$00,$fc,$6c,
-$66,$66,$66,$6c,$fc,$00,$fe,$62,$68,$78,$68,$62,$fe,$00,$fe,$62,$68,$78,$68,$60,$f0,$00,$3c,$66,$c0,$c0,
-$ce,$66,$3e,$00,$cc,$cc,$cc,$fc,$cc,$cc,$cc,$00,$78,$30,$30,$30,$30,$30,$78,$00,$1e,$0c,$0c,$0c,$cc,$cc,
-$78,$00,$e6,$66,$6c,$78,$6c,$66,$e6,$00,$f0,$60,$60,$60,$62,$66,$fe,$00,$c6,$ee,$fe,$d6,$c6,$c6,$c6,$00,
-$c6,$e6,$f6,$de,$ce,$c6,$c6,$00,$38,$6c,$c6,$c6,$c6,$6c,$38,$00,$fc,$66,$66,$7c,$60,$60,$f0,$00,$78,$cc,
-$cc,$cc,$dc,$78,$1c,$00,$fc,$66,$66,$7c,$78,$6c,$e6,$00,$78,$cc,$e0,$38,$1c,$cc,$78,$00,$fc,$b4,$30,$30,
-$30,$30,$78,$00,$cc,$cc,$cc,$cc,$cc,$cc,$fc,$00,$cc,$cc,$cc,$cc,$cc,$78,$30,$00,$c6,$c6,$c6,$d6,$fe,$ee,
-$c6,$00,$c6,$c6,$6c,$38,$6c,$c6,$c6,$00,$cc,$cc,$cc,$78,$30,$30,$78,$00,$fe,$cc,$98,$30,$62,$c6,$fe,$00,
-$78,$60,$60,$60,$60,$60,$78,$00,$c0,$60,$30,$18,$0c,$06,$02,$00,$78,$18,$18,$18,$18,$18,$78,$00,$10,$38,
-$6c,$c6,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$30,$30,$18,$00,$00,$00,$00,$00,$00,$00,$78,$0c,
-$7c,$cc,$76,$00,$e0,$60,$7c,$66,$66,$66,$bc,$00,$00,$00,$78,$cc,$c0,$cc,$78,$00,$1c,$0c,$0c,$7c,$cc,$cc,
-$76,$00,$00,$00,$78,$cc,$fc,$c0,$78,$00,$38,$6c,$60,$f0,$60,$60,$f0,$00,$00,$00,$76,$cc,$cc,$7c,$0c,$f8,
-$e0,$60,$6c,$76,$66,$66,$e6,$00,$30,$00,$70,$30,$30,$30,$78,$00,$18,$00,$78,$18,$18,$18,$d8,$70,$e0,$60,
-$66,$6c,$78,$6c,$e6,$00,$70,$30,$30,$30,$30,$30,$78,$00,$00,$00,$ec,$fe,$d6,$c6,$c6,$00,$00,$00,$f8,$cc,
-$cc,$cc,$cc,$00,$00,$00,$78,$cc,$cc,$cc,$78,$00,$00,$00,$dc,$66,$66,$7c,$60,$f0,$00,$00,$76,$cc,$cc,$7c,
-$0c,$1e,$00,$00,$d8,$6c,$6c,$60,$f0,$00,$00,$00,$7c,$c0,$78,$0c,$f8,$00,$10,$30,$7c,$30,$30,$34,$18,$00,
-$00,$00,$cc,$cc,$cc,$cc,$76,$00,$00,$00,$cc,$cc,$cc,$78,$30,$00,$00,$00,$c6,$c6,$d6,$fe,$6c,$00,$00,$00,
-$c6,$6c,$38,$6c,$c6,$00,$00,$00,$cc,$cc,$cc,$7c,$0c,$f8,$00,$00,$fc,$98,$30,$64,$fc,$00,$1c,$30,$30,$e0,
-$30,$30,$1c,$00,$18,$18,$18,$00,$18,$18,$18,$00,$e0,$30,$30,$1c,$30,$30,$e0,$00,$76,$dc,$00,$00,$00,$00,
-$00,$00,$10,$38,$6c,$c6,$c6,$c6,$fe,$00,$78,$cc,$c0,$cc,$78,$18,$0c,$78,$00,$cc,$00,$cc,$cc,$cc,$7e,$00,
-$1c,$00,$78,$cc,$fc,$c0,$78,$00,$7e,$c3,$3c,$06,$3e,$66,$3f,$00,$cc,$00,$78,$0c,$7c,$cc,$7e,$00,$e0,$00,
-$78,$0c,$7c,$cc,$7e,$00,$30,$30,$78,$0c,$7c,$cc,$7e,$00,$00,$00,$7c,$c0,$c0,$7c,$06,$3c,$7e,$c3,$3c,$66,
-$7e,$60,$3c,$00,$cc,$00,$78,$cc,$fc,$c0,$78,$00,$e0,$00,$78,$cc,$fc,$c0,$78,$00,$cc,$00,$70,$30,$30,$30,
-$78,$00,$7c,$c6,$38,$18,$18,$18,$3c,$00,$e0,$00,$70,$30,$30,$30,$78,$00,$cc,$30,$78,$cc,$cc,$fc,$cc,$00,
-$30,$30,$00,$78,$cc,$fc,$cc,$00,$1c,$00,$fc,$60,$78,$60,$fc,$00,$00,$00,$7f,$0c,$7f,$cc,$7f,$00,$3e,$6c,
-$cc,$fe,$cc,$cc,$ce,$00,$78,$cc,$00,$78,$cc,$cc,$78,$00,$00,$cc,$00,$78,$cc,$cc,$78,$00,$00,$e0,$00,$78,
-$cc,$cc,$78,$00,$78,$cc,$00,$cc,$cc,$cc,$7e,$00,$00,$e0,$00,$cc,$cc,$cc,$7e,$00,$00,$cc,$00,$cc,$cc,$fc,
-$0c,$f8,$c6,$38,$7c,$c6,$c6,$7c,$38,$00,$cc,$00,$cc,$cc,$cc,$cc,$78,$00,$18,$18,$7e,$c0,$c0,$7e,$18,$18,
-$38,$6c,$64,$f0,$60,$e6,$fc,$00,$cc,$cc,$78,$fc,$30,$fc,$30,$00,$f0,$d8,$d8,$f4,$cc,$de,$cc,$0e,$0e,$1b,
-$18,$7e,$18,$18,$d8,$70,$1c,$00,$78,$0c,$7c,$cc,$7e,$00,$38,$00,$70,$30,$30,$30,$78,$00,$00,$1c,$00,$78,
-$cc,$cc,$78,$00,$00,$1c,$00,$cc,$cc,$cc,$7e,$00,$00,$f8,$00,$f8,$cc,$cc,$cc,$00,$fc,$00,$cc,$ec,$fc,$dc,
-$cc,$00,$3c,$6c,$6c,$3e,$00,$7e,$00,$00,$3c,$66,$66,$3c,$00,$7e,$00,$00,$30,$00,$30,$60,$c0,$cc,$78,$00,
-$00,$00,$00,$fc,$c0,$c0,$00,$00,$00,$00,$00,$fc,$0c,$0c,$00,$00,$c6,$cc,$d8,$3e,$63,$ce,$98,$1f,$c6,$cc,
-$d8,$f3,$67,$cf,$9f,$03,$00,$18,$00,$18,$18,$3c,$3c,$18,$00,$33,$66,$cc,$66,$33,$00,$00,$00,$cc,$66,$33,
-$66,$cc,$00,$00,$22,$88,$22,$88,$22,$88,$22,$88,$55,$aa,$55,$aa,$55,$aa,$55,$aa,$dc,$76,$dc,$76,$dc,$76,
-$dc,$76,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$f8,$18,$18,$18,$18,$18,$f8,$18,$f8,$18,$18,$18,
-$36,$36,$36,$36,$f6,$36,$36,$36,$00,$00,$00,$00,$fe,$36,$36,$36,$00,$00,$f8,$18,$f8,$18,$18,$18,$36,$36,
-$f6,$06,$f6,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$00,$00,$fe,$06,$f6,$36,$36,$36,$36,$36,$f6,$06,
-$fe,$00,$00,$00,$36,$36,$36,$36,$fe,$00,$00,$00,$18,$18,$f8,$18,$f8,$00,$00,$00,$00,$00,$00,$00,$f8,$18,
-$18,$18,$18,$18,$18,$18,$1f,$00,$00,$00,$18,$18,$18,$18,$ff,$00,$00,$00,$00,$00,$00,$00,$ff,$18,$18,$18,
-$18,$18,$18,$18,$1f,$18,$18,$18,$00,$00,$00,$00,$ff,$00,$00,$00,$18,$18,$18,$18,$ff,$18,$18,$18,$18,$18,
-$1f,$18,$1f,$18,$18,$18,$36,$36,$36,$36,$37,$36,$36,$36,$36,$36,$37,$30,$3f,$00,$00,$00,$00,$00,$3f,$30,
-$37,$36,$36,$36,$36,$36,$f7,$00,$ff,$00,$00,$00,$00,$00,$ff,$00,$f7,$36,$36,$36,$36,$36,$37,$30,$37,$36,
-$36,$36,$00,$00,$ff,$00,$ff,$00,$00,$00,$36,$36,$f7,$00,$f7,$36,$36,$36,$18,$18,$ff,$00,$ff,$00,$00,$00,
-$36,$36,$36,$36,$ff,$00,$00,$00,$00,$00,$ff,$00,$ff,$18,$18,$18,$00,$00,$00,$00,$ff,$36,$36,$36,$36,$36,
-$36,$36,$3f,$00,$00,$00,$18,$18,$1f,$18,$1f,$00,$00,$00,$00,$00,$1f,$18,$1f,$18,$18,$18,$00,$00,$00,$00,
-$3f,$36,$36,$36,$36,$36,$36,$36,$f7,$36,$36,$36,$18,$18,$ff,$00,$ff,$18,$18,$18,$18,$18,$18,$18,$f8,$00,
-$00,$00,$00,$00,$00,$00,$1f,$18,$18,$18,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$ff,$ff,$ff,$ff,
-$f0,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$00,$00,
-$76,$dc,$c8,$dc,$76,$00,$00,$78,$cc,$f8,$cc,$f8,$c0,$c0,$00,$fe,$c6,$c0,$c0,$c0,$c0,$00,$00,$fe,$6c,$6c,
-$6c,$6c,$6c,$00,$fe,$66,$30,$18,$30,$66,$fe,$00,$00,$00,$7e,$cc,$cc,$cc,$78,$00,$00,$66,$66,$66,$66,$7c,
-$60,$c0,$00,$76,$dc,$18,$18,$18,$18,$00,$fc,$30,$78,$cc,$cc,$78,$30,$fc,$38,$6c,$c6,$fe,$c6,$6c,$38,$00,
-$38,$6c,$c6,$c6,$6c,$6c,$ee,$00,$1c,$30,$18,$7c,$cc,$cc,$78,$00,$00,$00,$7e,$db,$db,$7e,$00,$00,$06,$0c,
-$7e,$db,$db,$7e,$60,$c0,$3c,$60,$c0,$fc,$c0,$60,$3c,$00,$78,$cc,$cc,$cc,$cc,$cc,$cc,$00,$00,$fc,$00,$fc,
-$00,$fc,$00,$00,$30,$30,$fc,$30,$30,$00,$fc,$00,$60,$30,$18,$30,$60,$00,$fc,$00,$18,$30,$60,$30,$18,$00,
-$fc,$00,$0e,$1b,$1b,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$d8,$d8,$70,$30,$30,$00,$fc,$00,$30,$30,$00,
-$00,$72,$9c,$00,$72,$9c,$00,$00,$38,$6c,$6c,$38,$00,$00,$00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00,$00,
-$00,$00,$18,$00,$00,$00,$0f,$0c,$0c,$0c,$ec,$6c,$3c,$1c,$78,$6c,$6c,$6c,$6c,$00,$00,$00,$78,$0c,$38,$60,
-$7c,$00,$00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
-);
-
-const kgiFont6PropWidth: array[0..256-1] of Byte = (
-  $08,$08,$08,$07,$07,$07,$07,$04,$08,$07,$08,$08,$06,$06,$06,$07,
-  $06,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
-  $85,$21,$13,$05,$05,$05,$05,$13,$13,$13,$05,$05,$12,$14,$12,$05,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$21,$12,$05,$05,$05,$05,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$05,$05,$05,$05,$05,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$13,$05,$05,
-  $13,$05,$05,$05,$05,$05,$05,$05,$05,$13,$04,$14,$13,$05,$05,$05,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$14,$21,$04,$05,$08,
-  $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$04,
-  $44,$08,$08,$08,$08,$08,$08,$08,$05,$04,$05,$08,$08,$08,$08,$08,
-  $05,$05,$05,$05,$05,$05,$13,$13,$05,$05,$05,$04,$05,$05,$05,$05,
-  $05,$05,$05,$05,$05,$03,$04,$04,$06,$05,$04,$07,$04,$03,$05,$08,
-  $05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$04,$05,$05,$05,$05,
-  $14,$05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$05,$05,$14,$05,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05
-);
-
-const kgiFont8PropWidth: array[0..256-1] of Byte = (
-  $08,$08,$08,$07,$07,$07,$07,$06,$08,$07,$08,$08,$07,$08,$08,$08,
-  $07,$07,$07,$07,$08,$08,$07,$08,$07,$07,$07,$07,$07,$08,$08,$08,
-  $85,$14,$15,$07,$06,$07,$07,$03,$14,$14,$08,$06,$13,$06,$22,$07,
-  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$22,$13,$05,$06,$15,$06,
-  $07,$06,$07,$07,$07,$07,$07,$07,$06,$14,$07,$07,$07,$07,$07,$07,
-  $07,$06,$07,$06,$06,$06,$06,$07,$07,$06,$07,$14,$07,$14,$07,$08,
-  $23,$07,$07,$06,$07,$06,$06,$07,$07,$14,$05,$07,$14,$07,$06,$06,
-  $07,$07,$06,$06,$15,$07,$06,$07,$07,$06,$06,$06,$32,$06,$07,$07,
-  $06,$07,$06,$08,$07,$07,$07,$07,$08,$06,$06,$06,$07,$05,$06,$06,
-  $06,$08,$07,$06,$06,$06,$07,$07,$06,$07,$06,$07,$07,$06,$07,$08,
-  $07,$05,$06,$07,$06,$06,$16,$16,$06,$06,$06,$08,$08,$06,$08,$08,
-  $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
-  $38,$08,$08,$38,$08,$08,$38,$28,$28,$28,$08,$08,$28,$08,$08,$08,
-  $08,$08,$08,$28,$38,$38,$28,$08,$08,$08,$38,$08,$08,$08,$48,$08,
-  $07,$06,$07,$07,$07,$07,$07,$07,$06,$07,$07,$06,$08,$08,$06,$06,
-  $06,$06,$06,$06,$35,$05,$06,$07,$15,$32,$32,$08,$15,$15,$24,$08
-);
-
-
-function createFontTexture (constref font: array of Byte; constref fontwdt: array of Byte; prop: Boolean): GLuint;
-const
-  Width = 16*8;
-  Height = 16*8;
+// return width (not including last empty pixel)
+function TGxBmpFont.drawTextInternal (x, y: Integer; const s: AnsiString): Integer;
 var
-  tex, tpp: PByte;
-  b: Byte;
-  cc: Integer;
-  x, y, dx, dy: Integer;
+  ch: AnsiChar;
+  tx, ty: Integer;
 begin
-  GetMem(tex, Width*Height*4);
+  if (Length(s) = 0) then begin result := 0; exit; end;
+
+  result := -1;
+
+  glEnable(GL_ALPHA_TEST);
+  glAlphaFunc(GL_NOTEQUAL, 0.0);
+  glEnable(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, mTexId);
 
-  for cc := 0 to 255 do
+  for ch in s do
   begin
-    x := (cc mod 16)*8;
-    y := (cc div 16)*8;
-    for dy := 0 to 7 do
-    begin
-      b := font[cc*8+dy];
-      if prop then b := b shl (fontwdt[cc] shr 4);
-      tpp := tex+((y+dy)*(Width*4))+x*4;
-      for dx := 0 to 7 do
-      begin
-        if ((b and $80) <> 0) then
-        begin
-          tpp^ := 255; Inc(tpp);
-          tpp^ := 255; Inc(tpp);
-          tpp^ := 255; Inc(tpp);
-          tpp^ := 255; Inc(tpp);
-        end
-        else
-        begin
-          tpp^ := 0; Inc(tpp);
-          tpp^ := 0; Inc(tpp);
-          tpp^ := 0; Inc(tpp);
-          tpp^ := 0; Inc(tpp);
-        end;
-        b := (b and $7f) shl 1;
-      end;
-    end;
+    tx := (Integer(ch) mod 16)*8;
+    ty := (Integer(ch) div 16)*8;
+    glBegin(GL_QUADS);
+      glTexCoord2f((tx+0)/128.0, (ty+0)/128.0); glVertex2i(x+0, y+0); // top-left
+      glTexCoord2f((tx+8)/128.0, (ty+0)/128.0); glVertex2i(x+8, y+0); // top-right
+      glTexCoord2f((tx+8)/128.0, (ty+8)/128.0); glVertex2i(x+8, y+8); // bottom-right
+      glTexCoord2f((tx+0)/128.0, (ty+8)/128.0); glVertex2i(x+0, y+8); // bottom-left
+    glEnd();
+    x += (mFontWdt[Byte(ch)] and $0f)+1;
+    result += (mFontWdt[Byte(ch)] and $0f)+1;
   end;
 
-  glGenTextures(1, @result);
-  if (result = 0) then raise Exception.Create('can''t create Holmes font texture');
+  glDisable(GL_ALPHA_TEST);
+  glDisable(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, 0);
+end;
 
-  glBindTexture(GL_TEXTURE_2D, result);
-  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
-  //GLfloat[4] bclr = 0.0;
-  //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
+// ////////////////////////////////////////////////////////////////////////// //
+var
+  fontList: array of TGxBmpFont = nil;
+  defaultFontName: AnsiString = 'dos';
 
-  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
-  glFlush();
 
-  //FreeMem(tex);
+function strEquCI (const s0, s1: AnsiString): Boolean;
+var
+  f: Integer;
+  c0, c1: AnsiChar;
+begin
+  result := (Length(s0) = Length(s1));
+  if (result) then
+  begin
+    for f := 1 to Length(s0) do
+    begin
+      c0 := s0[f];
+      if (c0 >= 'a') and (c0 <= 'z') then Dec(c0, 32); // poor man's `toupper()`
+      c1 := s1[f];
+      if (c1 >= 'a') and (c1 <= 'z') then Dec(c1, 32); // poor man's `toupper()`
+      if (c0 <> c1) then begin result := false; exit; end;
+    end;
+  end;
 end;
 
 
+function getFontByName (const aname: AnsiString): TGxBmpFont;
 var
-  font6texid: GLuint = 0;
-  font8texid: GLuint = 0;
-  prfont6texid: GLuint = 0;
-  prfont8texid: GLuint = 0;
+  f: Integer;
+  fname: AnsiString;
+begin
+  if (Length(fontList) = 0) then raise Exception.Create('font subsystem not initialized');
+  if (Length(aname) = 0) or (strEquCI(aname, 'default')) then fname := defaultFontName else fname := aname;
+  for f := 0 to High(fontList) do
+  begin
+    result := fontList[f];
+    if (result = nil) then continue;
+    if (strEquCI(result.name, fname)) then exit;
+  end;
+  if (fontList[0] = nil) then raise Exception.Create('font subsystem not properly initialized');
+  result := fontList[0];
+end;
 
 
 procedure deleteFonts ();
+var
+  f: Integer;
 begin
-  if (font6texid <> 0) then glDeleteTextures(1, @font6texid);
-  if (font8texid <> 0) then glDeleteTextures(1, @font8texid);
-  if (prfont6texid <> 0) then glDeleteTextures(1, @prfont6texid);
-  if (prfont8texid <> 0) then glDeleteTextures(1, @prfont8texid);
-  font6texid := 0;
-  font8texid := 0;
-  prfont6texid := 0;
-  prfont8texid := 0;
+  for f := 0 to High(fontList) do freeAndNil(fontList[f]);
+  fontList := nil;
 end;
 
 
 procedure createFonts ();
 begin
-  if (font6texid = 0) then font6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, false);
-  if (font8texid = 0) then font8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, false);
-  if (prfont6texid = 0) then prfont6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, true);
-  if (prfont8texid = 0) then prfont8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, true);
+  deleteFonts();
+  SetLength(fontList, 4);
+  fontList[0] := TGxBmpFont.Create('dos', 8, 8, @kgiFont8[0], @kgiFont8PropWidth[0]);
+  fontList[1] := TGxBmpFont.Create('dos-prop', 0, 8, @kgiFont8[0], @kgiFont8PropWidth[0]);
+  fontList[2] := TGxBmpFont.Create('msx', 6, 8, @kgiFont6[0], @kgiFont6PropWidth[0]);
+  fontList[3] := TGxBmpFont.Create('msx-prop', 0, 8, @kgiFont6[0], @kgiFont6PropWidth[0]);
 end;
 
 
-// ////////////////////////////////////////////////////////////////////////// //
-procedure TScissorSave.save (enableScissoring: Boolean);
+procedure oglInitFonts ();
+var
+  f: Integer;
 begin
-  wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
-  if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
-  //conwritefln('(%d,%d)-(%d,%d)', [scxywh[0], scxywh[1], scxywh[2], scxywh[3]]);
-  if enableScissoring and (not wassc) then glEnable(GL_SCISSOR_TEST);
+  for f := 0 to High(fontList) do if (fontList[f] <> nil) then fontList[f].oglCreateTexture();
 end;
 
-procedure TScissorSave.restore ();
-begin
-  glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
-  if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
-end;
 
-procedure TScissorSave.combineRect (x, y, w, h: Integer);
-//var ox, oy, ow, oh: Integer;
+procedure oglDeinitFonts ();
+var
+  f: Integer;
 begin
-  if (w < 1) or (h < 1) then begin glScissor(0, 0, 0, 0); exit; end;
-  y := fuiScrHgt-(y+h);
-  //ox := x; oy := y; ow := w; oh := h;
-  if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then
-  begin
-    //writeln('oops: COMBINE: old=(', ox, ',', oy, ')-(', ox+ow-1, ',', oy+oh-1, '); sci: (', scxywh[0], ',', scxywh[1], ')-(', scxywh[0]+scxywh[2]-1, ',', scxywh[1]+scxywh[3]-1, ')');
-    //writeln('oops: COMBINE: oldx=<', ox, '-', ox+ow-1, '>; oldy=<', oy, ',', oy+oh-1, '> : scix=<', scxywh[0], '-', scxywh[0]+scxywh[2]-1, '>; sciy=<', scxywh[1], '-', scxywh[1]+scxywh[3]-1, '>');
-    glScissor(0, 0, 0, 0);
-  end
-  else
-  begin
-    glScissor(x, y, w, h);
-  end;
+  for f := 0 to High(fontList) do if (fontList[f] <> nil) then fontList[f].oglDestroyTexture();
 end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-// returns `false` if the color is transparent
-function setupGLColor (r, g, b, a: Integer): Boolean;
+procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
 begin
-  normRGBA(r, g, b, a);
-  if (a < 255) then
+  glViewport(0, 0, winWidth, winHeight);
+
+  glDisable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glDisable(GL_LINE_SMOOTH);
+  glDisable(GL_POLYGON_SMOOTH);
+  glDisable(GL_POINT_SMOOTH);
+  glDisable(GL_DEPTH_TEST);
+  glDisable(GL_TEXTURE_2D);
+  glDisable(GL_LIGHTING);
+  glDisable(GL_DITHER);
+  glDisable(GL_STENCIL_TEST);
+  glDisable(GL_SCISSOR_TEST);
+  glDisable(GL_CULL_FACE);
+  glDisable(GL_ALPHA_TEST);
+
+  glMatrixMode(GL_TEXTURE);
+  glLoadIdentity();
+
+  glMatrixMode(GL_COLOR);
+  glLoadIdentity();
+
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  if (upsideDown) then
   begin
-    if (a = 0) then begin result := false; exit; end;
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glOrtho(0, winWidth, 0, winHeight, -1, 1); // set origin to bottom left
   end
   else
   begin
-    glDisable(GL_BLEND);
+    glOrtho(0, winWidth, winHeight, 0, -1, 1); // set origin to top left
   end;
-  glColor4ub(Byte(r), Byte(g), Byte(b), Byte(a));
-  result := true;
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+
+  glClearColor(0, 0, 0, 0);
+  glColor4f(1, 1, 1, 1);
 end;
 
+
+// ////////////////////////////////////////////////////////////////////////// //
+{$INCLUDE fui_gfx_gl_cursor.inc}
+
+procedure oglDrawCursor (); begin oglDrawCursorAt(fuiMouseX, fuiMouseY); end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+// returns `false` if the color is transparent
 // returns `false` if the color is transparent
 function setupGLColor (constref clr: TGxRGBA): Boolean;
 begin
   if (clr.a < 255) then
   begin
-    if (clr.a = 0) then begin result := false; exit; end;
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   end
@@ -651,10 +573,10 @@ begin
     glDisable(GL_BLEND);
   end;
   glColor4ub(clr.r, clr.g, clr.b, clr.a);
-  result := true;
+  result := (clr.a <> 0);
 end;
 
-function isScaled (): Boolean;
+function mScaled (): Boolean;
 var
   mt: packed array [0..15] of Double;
 begin
@@ -664,133 +586,147 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-function textWidth6 (const s: AnsiString): Integer;
-var
-  f: Integer;
+constructor TGxContext.Create ();
 begin
-  result := 0;
-  for f := 1 to Length(s) do Inc(result, Integer(kgiFont6PropWidth[Integer(s[f])] and $0f)+1);
-  if (result > 0) then Dec(result); // don't count last empty pixel
+  mActive := false;
+  mColor := TGxRGBA.Create(255, 255, 255);
+  mFont := getFontByName('default');
+  mScaled := false;
+  mScale := 1.0;
+  mClipRect := TGxRect.Create(0, 0, 8192, 8192);
+  mClipOfs := TGxOfs.Create(0, 0);
 end;
 
 
-function textWidth8 (const s: AnsiString): Integer;
-var
-  f: Integer;
+destructor TGxContext.Destroy ();
 begin
-  result := 0;
-  for f := 1 to Length(s) do Inc(result, Integer(kgiFont8PropWidth[Integer(s[f])] and $0f)+1);
-  if (result > 0) then Dec(result); // don't count last empty pixel
+  if (mActive) then gxSetContext(nil);
+  inherited;
 end;
 
 
-// return width (including last empty pixel)
-function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; constref clr: TGxRGBA; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
-var
-  f, c: Integer;
-  tx, ty: Integer;
+function TGxContext.getFont (): AnsiString;
 begin
-  result := 0;
-  if (Length(s) = 0) then exit;
-  if not setupGLColor(clr) then exit;
+  result := mFont.name;
+end;
+
+procedure TGxContext.setFont (const aname: AnsiString);
+begin
+  mFont := getFontByName(aname);
+end;
+
+
+procedure TGxContext.onActivate ();
+begin
+  setupGLColor(mColor);
+  realizeClip();
+end;
+
+procedure TGxContext.onDeactivate ();
+begin
+end;
+
+
+procedure TGxContext.setColor (const clr: TGxRGBA);
+begin
+  mColor := clr;
+  if (mActive) then setupGLColor(mColor);
+end;
 
-  glEnable(GL_ALPHA_TEST);
-  glAlphaFunc(GL_NOTEQUAL, 0.0);
-  glEnable(GL_TEXTURE_2D);
-  glBindTexture(GL_TEXTURE_2D, tid);
 
-  for f := 1 to Length(s) do
+procedure TGxContext.realizeClip ();
+var
+  sx, sy, sw, sh: Integer;
+begin
+  if (not mActive) then exit; // just in case
+  if (mClipRect.w <= 0) or (mClipRect.h <= 0) then
   begin
-    c := Integer(s[f]) and $ff;
-    tx := (c mod 16)*8;
-    ty := (c div 16)*8;
-    glBegin(GL_QUADS);
-      glTexCoord2f((tx+0)/128.0, (ty+0)/128.0); glVertex2i(x+0, y+0); // top-left
-      glTexCoord2f((tx+8)/128.0, (ty+0)/128.0); glVertex2i(x+8, y+0); // top-right
-      glTexCoord2f((tx+8)/128.0, (ty+8)/128.0); glVertex2i(x+8, y+8); // bottom-right
-      glTexCoord2f((tx+0)/128.0, (ty+8)/128.0); glVertex2i(x+0, y+8); // bottom-left
-    glEnd();
-    if prop then
+    glEnable(GL_SCISSOR_TEST);
+    glScissor(0, 0, 0, 0);
+  end
+  else
+  begin
+    if (mScaled) then
+    begin
+      sx := trunc(mClipRect.x*mScale);
+      sy := trunc(mClipRect.y*mScale);
+      sw := trunc(mClipRect.w*mScale);
+      sh := trunc(mClipRect.h*mScale);
+    end
+    else
+    begin
+      sx := mClipRect.x;
+      sy := mClipRect.y;
+      sw := mClipRect.w;
+      sh := mClipRect.h;
+    end;
+    if (not intersectRect(sx, sy, sw, sh, 0, 0, fuiScrWdt, fuiScrHgt)) then
+    begin
+      glEnable(GL_SCISSOR_TEST);
+      glScissor(0, 0, 0, 0);
+    end
+    else if (sx = 0) and (sy = 0) and (sw = fuiScrWdt) and (sh = fuiScrHgt) then
     begin
-      x += Integer(fontwdt[c] and $0f)+1;
-      result += Integer(fontwdt[c] and $0f)+1;
+      glDisable(GL_SCISSOR_TEST);
     end
     else
     begin
-      x += wdt;
-      result += wdt;
+      glEnable(GL_SCISSOR_TEST);
+      sy := fuiScrHgt-(sy+sh);
+      glScissor(sx, sy, sw, sh);
     end;
   end;
+end;
 
-  glDisable(GL_ALPHA_TEST);
-  glDisable(GL_BLEND);
-  glDisable(GL_TEXTURE_2D);
-  glColor4f(1, 1, 1, 1);
-  glBindTexture(GL_TEXTURE_2D, 0);
+
+procedure TGxContext.resetClip ();
+begin
+  mClipRect := TGxRect.Create(0, 0, 8192, 8192);
+  if (mActive) then realizeClip();
 end;
 
 
-// ////////////////////////////////////////////////////////////////////////// //
-procedure drawHLine (x, y, len: Integer; constref clr: TGxRGBA);
+procedure TGxContext.setClipOfs (const aofs: TGxOfs);
 begin
-  if (len < 1) then exit;
-  if not setupGLColor(clr) then exit;
-  glDisable(GL_TEXTURE_2D);
-  if (not isScaled) then
-  begin
-    glLineWidth(1);
-    glBegin(GL_LINES);
-      glVertex2f(x+0.375, y+0.375);
-      glVertex2f(x+len+0.375, y+0.375);
-    glEnd();
-  end
-  else
-  begin
-    glBegin(GL_QUADS);
-      glVertex2i(x, y);
-      glVertex2i(x+len, y);
-      glVertex2i(x+len, y+1);
-      glVertex2i(x, y+1);
-    glEnd();
-  end;
+  mClipOfs := aofs;
 end;
 
 
-procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
+procedure TGxContext.setClipRect (const aclip: TGxRect);
 begin
-  if (len < 1) then exit;
-  if not setupGLColor(clr) then exit;
-  glDisable(GL_TEXTURE_2D);
-  if (not isScaled) then
-  begin
-    glLineWidth(1);
-    glBegin(GL_LINES);
-      glVertex2f(x+0.375, y+0.375);
-      glVertex2f(x+0.375, y+len+0.375);
-    glEnd();
-  end
-  else
-  begin
-    glBegin(GL_QUADS);
-      glVertex2i(x, y);
-      glVertex2i(x, y+len);
-      glVertex2i(x+1, y+len);
-      glVertex2i(x+1, y);
-    glEnd();
-  end;
+  mClipRect := aclip;
+  if (mActive) then realizeClip();
 end;
 
 
-procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
+function TGxContext.setOffset (constref aofs: TGxOfs): TGxOfs;
 begin
-  if not setupGLColor(clr) then exit;
+  result := mClipOfs;
+  mClipOfs := aofs;
+end;
 
-  glDisable(GL_TEXTURE_2D);
 
-  glLineWidth(1);
-  glPointSize(1);
+function TGxContext.setClip (constref aclip: TGxRect): TGxRect;
+begin
+  result := mClipRect;
+  mClipRect := aclip;
+  if (mActive) then realizeClip();
+end;
+
+
+function TGxContext.combineClip (constref aclip: TGxRect): TGxRect;
+begin
+  result := mClipRect;
+  mClipRect.intersect(aclip);
+  if (mActive) then realizeClip();
+end;
 
-  if (not isScaled) then
+
+procedure TGxContext.line (x1, y1, x2, y2: Integer);
+begin
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+
+  if (not mScaled) then
   begin
     glLineWidth(1);
     glBegin(GL_LINES);
@@ -800,6 +736,7 @@ begin
 
     if (x1 <> x2) or (y1 <> y2) then
     begin
+      glPointSize(1);
       glBegin(GL_POINTS);
         glVertex2f(x2+0.375, y2+0.375);
       glEnd();
@@ -816,44 +753,59 @@ begin
       glVertex2i(x2+1, y2+1);
     glEnd();
   end;
-
-  glColor4f(1, 1, 1, 1);
-  glDisable(GL_BLEND);
 end;
 
 
-procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
+procedure TGxContext.hline (x, y, len: Integer);
 begin
-  if (w < 0) or (h < 0) then exit;
-  if not setupGLColor(clr) then exit;
-  glDisable(GL_TEXTURE_2D);
-  glLineWidth(1);
-  glDisable(GL_LINE_SMOOTH);
-  glDisable(GL_POLYGON_SMOOTH);
-  if (w = 1) and (h = 1) then
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+  if (len < 1) then exit;
+  if (not mScaled) then
   begin
-    glBegin(GL_POINTS);
+    glLineWidth(1);
+    glBegin(GL_LINES);
       glVertex2f(x+0.375, y+0.375);
+      glVertex2f(x+len+0.375, y+0.375);
     glEnd();
   end
   else
+  begin
+    glBegin(GL_QUADS);
+      glVertex2i(x, y);
+      glVertex2i(x+len, y);
+      glVertex2i(x+len, y+1);
+      glVertex2i(x, y+1);
+    glEnd();
+  end;
+end;
+
+
+procedure TGxContext.vline (x, y, len: Integer);
+begin
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+  if (len < 1) then exit;
+  if (not mScaled) then
   begin
     glLineWidth(1);
     glBegin(GL_LINES);
-      glVertex2i(x, y); glVertex2i(x+w, y); // top
-      glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
-      glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
-      glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
+      glVertex2f(x+0.375, y+0.375);
+      glVertex2f(x+0.375, y+len+0.375);
+    glEnd();
+  end
+  else
+  begin
+    glBegin(GL_QUADS);
+      glVertex2i(x, y);
+      glVertex2i(x, y+len);
+      glVertex2i(x+1, y+len);
+      glVertex2i(x+1, y);
     glEnd();
   end;
-  //glRect(x, y, x+w, y+h);
-  glColor4f(1, 1, 1, 1);
-  glDisable(GL_BLEND);
 end;
 
 
-procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
-  procedure hline (x, y, len: Integer);
+procedure TGxContext.rect (x, y, w, h: Integer);
+  procedure hlinex (x, y, len: Integer);
   begin
     if (len < 1) then exit;
     glBegin(GL_QUADS);
@@ -864,7 +816,7 @@ procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
     glEnd();
   end;
 
-  procedure vline (x, y, len: Integer);
+  procedure vlinex (x, y, len: Integer);
   begin
     if (len < 1) then exit;
     glBegin(GL_QUADS);
@@ -875,25 +827,19 @@ procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
     glEnd();
   end;
 
-var
-  scaled: Boolean;
 begin
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
   if (w < 0) or (h < 0) then exit;
-  if not setupGLColor(clr) then exit;
-  glDisable(GL_TEXTURE_2D);
-  glLineWidth(1);
-  glDisable(GL_LINE_SMOOTH);
-  glDisable(GL_POLYGON_SMOOTH);
-  scaled := isScaled();
   if (w = 1) and (h = 1) then
   begin
+    glPointSize(1);
     glBegin(GL_POINTS);
-      if scaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
+      if mScaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
     glEnd();
   end
   else
   begin
-    if not scaled then
+    if (not mScaled) then
     begin
       glLineWidth(1);
       glBegin(GL_LINES);
@@ -905,28 +851,35 @@ begin
     end
     else
     begin
-      hline(x, y, w);
-      hline(x, y+h-1, w);
-      vline(x, y+1, h-2);
-      vline(x+w-1, y+1, h-2);
+      hlinex(x, y, w);
+      hlinex(x, y+h-1, w);
+      vlinex(x, y+1, h-2);
+      vlinex(x+w-1, y+1, h-2);
     end;
   end;
-  //glRect(x, y, x+w, y+h);
-  glColor4f(1, 1, 1, 1);
-  glDisable(GL_BLEND);
 end;
 
 
-procedure darkenRect (x, y, w, h: Integer; a: Integer);
+procedure TGxContext.fillRect (x, y, w, h: Integer);
+begin
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+  if (w < 0) or (h < 0) then exit;
+  glBegin(GL_QUADS);
+    glVertex2f(x, y);
+    glVertex2f(x+w, y);
+    glVertex2f(x+w, y+h);
+    glVertex2f(x, y+h);
+  glEnd();
+end;
+
+
+procedure TGxContext.darkenRect (x, y, w, h: Integer; a: Integer);
 begin
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (a >= 255) then exit;
   if (w < 0) or (h < 0) then exit;
   if (a < 0) then a := 0;
-  if (a >= 255) then exit;
   glEnable(GL_BLEND);
   glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
-  glDisable(GL_LINE_SMOOTH);
-  glDisable(GL_POLYGON_SMOOTH);
-  glDisable(GL_TEXTURE_2D);
   glColor4f(0.0, 0.0, 0.0, a/255.0);
   glBegin(GL_QUADS);
     glVertex2i(x, y);
@@ -934,93 +887,114 @@ begin
     glVertex2i(x+w, y+h);
     glVertex2i(x, y+h);
   glEnd();
-  //glRect(x, y, x+w, y+h);
-  glColor4f(1, 1, 1, 1);
-  glDisable(GL_BLEND);
-  //glBlendEquation(GL_FUNC_ADD);
+  setupGLColor(mColor);
 end;
 
 
-procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
+function TGxContext.charWidth (const ch: AnsiChar): Integer;
 begin
-  if (w < 0) or (h < 0) then exit;
-  if not setupGLColor(clr) then exit;
-  glDisable(GL_LINE_SMOOTH);
-  glDisable(GL_POLYGON_SMOOTH);
-  glDisable(GL_TEXTURE_2D);
-  glBegin(GL_QUADS);
-    glVertex2f(x, y);
-    glVertex2f(x+w, y);
-    glVertex2f(x+w, y+h);
-    glVertex2f(x, y+h);
-  glEnd();
-  glColor4f(1, 1, 1, 1);
-  glDisable(GL_BLEND);
+  result := mFont.charWidth(ch);
 end;
 
-
-// ////////////////////////////////////////////////////////////////////////// //
-function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+function TGxContext.charHeight (const ch: AnsiChar): Integer;
 begin
-  if (font6texid = 0) then createFonts();
-  drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
-  result := Length(s)*6;
+  result := mFont.height;
 end;
 
-function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
-begin
-  if (font8texid = 0) then createFonts();
-  drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
-  result := Length(s)*8;
-end;
 
-function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+function TGxContext.textWidth (const s: AnsiString): Integer;
 begin
-  if (prfont6texid = 0) then createFonts();
-  result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
+  result := mFont.textWidth(s);
 end;
 
-function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+function TGxContext.textHeight (const s: AnsiString): Integer;
 begin
-  if (prfont8texid = 0) then createFonts();
-  result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
+  result := mFont.height;
 end;
 
 
-// ////////////////////////////////////////////////////////////////////////// //
-// x-centered at `x`
-function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+function TGxContext.drawChar (x, y: Integer; const ch: AnsiChar): Integer; // returns char width
 begin
-  if (font6texid = 0) then createFonts();
-  x -= Length(s)*6 div 2;
-  drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
-  result := Length(s)*6;
+  result := mFont.charWidth(ch);
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+  TGxBmpFont(mFont).drawTextInternal(x, y, ch);
 end;
 
-function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+function TGxContext.drawText (x, y: Integer; const s: AnsiString): Integer; // returns text width
 begin
-  if (font8texid = 0) then createFonts();
-  x -= Length(s)*8 div 2;
-  drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
-  result := Length(s)*8;
+  result := mFont.textWidth(s);
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) or (Length(s) = 0) then exit;
+  TGxBmpFont(mFont).drawTextInternal(x, y, s);
 end;
 
-function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+
+function TGxContext.iconMarkWidth (ic: TMarkIcon): Integer; begin result := 11; end;
+function TGxContext.iconMarkHeight (ic: TMarkIcon): Integer; begin result := 8; end;
+
+procedure TGxContext.drawIconMark (ic: TMarkIcon; x, y: Integer; marked: Boolean);
+var
+  f: Integer;
 begin
-  if (prfont6texid = 0) then createFonts();
-  x -= textWidth6(s) div 2;
-  result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+  if (ic = TMarkIcon.Checkbox) then
+  begin
+    vline(x, y, 7);
+    vline(x+10, y, 7);
+    hline(x+1, y, 1);
+    hline(x+1, y+6, 1);
+    hline(x+9, y, 1);
+    hline(x+9, y+6, 1);
+  end
+  else
+  begin
+    vline(x, y+1, 5);
+    vline(x+10, y+1, 5);
+    hline(x+1, y, 1);
+    hline(x+1, y+6, 1);
+    hline(x+9, y, 1);
+    hline(x+9, y+6, 1);
+  end;
+  if (not marked) then exit;
+  case ic of
+    TMarkIcon.Checkbox:
+      begin
+        for f := 0 to 4 do
+        begin
+          vline(x+3+f, y+1+f, 1);
+          vline(x+7-f, y+1+f, 1);
+        end;
+      end;
+    TMarkIcon.Radiobox:
+      begin
+        hline(x+4, y+1, 3);
+        hline(x+3, y+2, 5);
+        hline(x+3, y+3, 5);
+        hline(x+3, y+4, 5);
+        hline(x+4, y+5, 3);
+      end;
+  end;
 end;
 
-function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
+
+function TGxContext.iconWinWidth (ic: TWinIcon): Integer; begin result := 9; end;
+function TGxContext.iconWinHeight (ic: TWinIcon): Integer; begin result := 8; end;
+
+procedure TGxContext.drawIconWin (ic: TWinIcon; x, y: Integer; pressed: Boolean);
+var
+  f: Integer;
 begin
-  if (prfont8texid = 0) then createFonts();
-  x -= textWidth8(s) div 2;
-  result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
+  if (not mActive) or (mClipRect.w < 1) or (mClipRect.h < 1) or (mColor.a = 0) then exit;
+  if pressed then rect(x, y, 9, 8);
+  for f := 1 to 5 do
+  begin
+    vline(x+1+f, y+f, 1);
+    vline(x+1+6-f, y+f, 1);
+  end;
 end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
+(*
 procedure oglRestoreMode (doClear: Boolean);
 begin
   oglSetup2D(fuiScrWdt, fuiScrHgt);
@@ -1051,21 +1025,20 @@ begin
   glLoadIdentity();
   //glScalef(4, 4, 1);
 end;
+*)
 
 
 //procedure onWinFocus (); begin end;
 //procedure onWinBlur (); begin fuiResetKMState(true); end;
 
-procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
-
-procedure onPostRender (); begin oglRestoreMode(false); oglDrawCursor(); end;
+//procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
+procedure onPostRender (); begin oglDrawCursor(); end;
 
 procedure onInit ();
 begin
-  oglSetup2D(fuiScrWdt, fuiScrHgt);
-
+  //oglSetup2D(fuiScrWdt, fuiScrHgt);
   createCursorTexture();
-  createFonts();
+  oglInitFonts();
 end;
 
 procedure onDeinit ();
@@ -1073,7 +1046,7 @@ begin
   fuiResetKMState(false);
   if (curtexid <> 0) then glDeleteTextures(1, @curtexid);
   curtexid := 0;
-  deleteFonts();
+  oglDeinitFonts();
   fuiSetButState(0);
   fuiSetModState(0);
   fuiSetMouseX(0);
@@ -1082,10 +1055,12 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-begin
+initialization
+  savedGLState := TSavedGLState.Create(false);
+  createFonts();
   //winFocusCB := onWinFocus;
   //winBlurCB := onWinBlur;
-  prerenderFrameCB := onPreRender;
+  //prerenderFrameCB := onPreRender;
   postrenderFrameCB := onPostRender;
   oglInitCB := onInit;
   oglDeinitCB := onDeinit;
diff --git a/src/flexui/fui_gfx_gl_cursor.inc b/src/flexui/fui_gfx_gl_cursor.inc
new file mode 100644 (file)
index 0000000..1884c9f
--- /dev/null
@@ -0,0 +1,171 @@
+(* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
+ * Understanding is not required. Only obedience.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *)
+// ////////////////////////////////////////////////////////////////////////// //
+// cursor (hi, Death Track!)
+const curTexWidth = 32;
+const curTexHeight = 32;
+const curWidth = 17;
+const curHeight = 23;
+
+const cursorImg: array[0..curWidth*curHeight-1] of Byte = (
+  2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,
+  3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,0,0,
+  3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,0,0,
+  3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,0,0,
+  3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,0,0,
+  3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,0,0,
+  3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,0,0,
+  3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,0,0,
+  3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,0,0,
+  3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,0,0,
+  3,3,5,6,3,3,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,6,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+);
+const cursorPal: array[0..9*4-1] of Byte = (
+    0,  0,  0,  0,
+    0,  0,  0, 92, // shadow
+   85,255,255,255,
+   85, 85,255,255,
+  255, 85, 85,255,
+  170,  0,170,255,
+   85, 85, 85,255,
+    0,  0,  0,255,
+    0,  0,170,255
+);
+
+
+var
+  curtexid: GLuint = 0;
+
+
+procedure createCursorTexture ();
+var
+  tex, tpp: PByte;
+  c: Integer;
+  x, y: Integer;
+begin
+  if (curtexid <> 0) then exit; //begin glDeleteTextures(1, @curtexid); curtexid := 0; end;
+
+  GetMem(tex, curTexWidth*curTexHeight*4);
+  try
+    FillChar(tex^, curTexWidth*curTexHeight*4, 0);
+
+    // draw shadow
+    for y := 0 to curHeight-1 do
+    begin
+      for x := 0 to curWidth-1 do
+      begin
+        if (cursorImg[y*curWidth+x] <> 0) then
+        begin
+          c := 1*4;
+          tpp := tex+((y+1)*(curTexWidth*4)+(x+3)*4);
+          tpp^ := cursorPal[c+0]; Inc(tpp);
+          tpp^ := cursorPal[c+1]; Inc(tpp);
+          tpp^ := cursorPal[c+2]; Inc(tpp);
+          tpp^ := cursorPal[c+3]; Inc(tpp);
+          tpp^ := cursorPal[c+0]; Inc(tpp);
+          tpp^ := cursorPal[c+1]; Inc(tpp);
+          tpp^ := cursorPal[c+2]; Inc(tpp);
+          tpp^ := cursorPal[c+3]; Inc(tpp);
+        end;
+      end;
+    end;
+
+    // draw cursor
+    for y := 0 to curHeight-1 do
+    begin
+      for x := 0 to curWidth-1 do
+      begin
+        c := cursorImg[y*curWidth+x]*4;
+        if (c <> 0) then
+        begin
+          tpp := tex+(y*(curTexWidth*4)+x*4);
+          tpp^ := cursorPal[c+0]; Inc(tpp);
+          tpp^ := cursorPal[c+1]; Inc(tpp);
+          tpp^ := cursorPal[c+2]; Inc(tpp);
+          tpp^ := cursorPal[c+3]; Inc(tpp);
+        end;
+      end;
+    end;
+
+    glGenTextures(1, @curtexid);
+    if (curtexid = 0) then raise Exception.Create('can''t create cursor texture');
+
+    glBindTexture(GL_TEXTURE_2D, curtexid);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    //GLfloat[4] bclr = 0.0;
+    //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, curTexWidth, curTexHeight, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
+    glFlush();
+  finally
+    FreeMem(tex);
+  end;
+end;
+
+
+procedure oglDrawCursorAt (msX, msY: Integer);
+var
+  sst: TSavedGLState;
+begin
+  //if (curtexid = 0) then createCursorTexture() else glBindTexture(GL_TEXTURE_2D, curtexid);
+  sst := TSavedGLState.Create(true);
+  try
+    oglSetup2D(fuiScrWdt, fuiScrHgt);
+    glBindTexture(GL_TEXTURE_2D, curtexid);
+    // blend it
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_TEXTURE_2D);
+    glDisable(GL_STENCIL_TEST);
+    glDisable(GL_SCISSOR_TEST);
+    glDisable(GL_LIGHTING);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+    // color and opacity
+    glColor4f(1, 1, 1, 1.0);
+    //Dec(msX, 2);
+    glBegin(GL_QUADS);
+      glTexCoord2f(0.0, 0.0); glVertex2i(msX, msY); // top-left
+      glTexCoord2f(1.0, 0.0); glVertex2i(msX+curTexWidth, msY); // top-right
+      glTexCoord2f(1.0, 1.0); glVertex2i(msX+curTexWidth, msY+curTexHeight); // bottom-right
+      glTexCoord2f(0.0, 1.0); glVertex2i(msX, msY+curTexHeight); // bottom-left
+    glEnd();
+    //Inc(msX, 2);
+    //glDisable(GL_BLEND);
+    //glDisable(GL_TEXTURE_2D);
+    //glColor4f(1, 1, 1, 1);
+    //glBindTexture(GL_TEXTURE_2D, 0);
+  finally
+    sst.restore();
+  end;
+end;
diff --git a/src/flexui/fui_gfx_gl_fonts.inc b/src/flexui/fui_gfx_gl_fonts.inc
new file mode 100644 (file)
index 0000000..c3372e2
--- /dev/null
@@ -0,0 +1,311 @@
+(* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
+ * Understanding is not required. Only obedience.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *)
+// ////////////////////////////////////////////////////////////////////////// //
+// fonts
+const kgiFont6: array[0..256*8-1] of Byte  = (
+$00,$00,$00,$00,$00,$00,$00,$00,$3c,$42,$a5,$81,$a5,$99,$42,$3c,$3c,$7e,$db,$ff,$ff,$db,$66,$3c,$6c,$fe,
+$fe,$fe,$7c,$38,$10,$00,$10,$38,$7c,$fe,$7c,$38,$10,$00,$10,$38,$54,$fe,$54,$10,$38,$00,$10,$38,$7c,$fe,
+$fe,$10,$38,$00,$00,$00,$00,$30,$30,$00,$00,$00,$ff,$ff,$ff,$e7,$e7,$ff,$ff,$ff,$38,$44,$82,$82,$82,$44,
+$38,$00,$c7,$bb,$7d,$7d,$7d,$bb,$c7,$ff,$0f,$03,$05,$79,$88,$88,$88,$70,$38,$44,$44,$44,$38,$10,$7c,$10,
+$30,$28,$24,$24,$28,$20,$e0,$c0,$3c,$24,$3c,$24,$24,$e4,$dc,$18,$10,$54,$38,$ee,$38,$54,$10,$00,$10,$10,
+$10,$7c,$10,$10,$10,$10,$10,$10,$10,$ff,$00,$00,$00,$00,$00,$00,$00,$ff,$10,$10,$10,$10,$10,$10,$10,$f0,
+$10,$10,$10,$10,$10,$10,$10,$1f,$10,$10,$10,$10,$10,$10,$10,$ff,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,
+$10,$10,$00,$00,$00,$ff,$00,$00,$00,$00,$00,$00,$00,$1f,$10,$10,$10,$10,$00,$00,$00,$f0,$10,$10,$10,$10,
+$10,$10,$10,$1f,$00,$00,$00,$00,$10,$10,$10,$f0,$00,$00,$00,$00,$81,$42,$24,$18,$18,$24,$42,$81,$01,$02,
+$04,$08,$10,$20,$40,$80,$80,$40,$20,$10,$08,$04,$02,$01,$00,$10,$10,$ff,$10,$10,$00,$00,$00,$00,$00,$00,
+$00,$00,$00,$00,$20,$20,$20,$20,$00,$00,$20,$00,$50,$50,$50,$00,$00,$00,$00,$00,$50,$50,$f8,$50,$f8,$50,
+$50,$00,$20,$78,$a0,$70,$28,$f0,$20,$00,$c0,$c8,$10,$20,$40,$98,$18,$00,$40,$a0,$40,$a8,$90,$98,$60,$00,
+$10,$20,$40,$00,$00,$00,$00,$00,$10,$20,$40,$40,$40,$20,$10,$00,$40,$20,$10,$10,$10,$20,$40,$00,$88,$50,
+$20,$f8,$20,$50,$88,$00,$00,$20,$20,$f8,$20,$20,$00,$00,$00,$00,$00,$00,$00,$20,$20,$40,$00,$00,$00,$78,
+$00,$00,$00,$00,$00,$00,$00,$00,$00,$60,$60,$00,$00,$00,$08,$10,$20,$40,$80,$00,$70,$88,$98,$a8,$c8,$88,
+$70,$00,$20,$60,$a0,$20,$20,$20,$f8,$00,$70,$88,$08,$10,$60,$80,$f8,$00,$70,$88,$08,$30,$08,$88,$70,$00,
+$10,$30,$50,$90,$f8,$10,$10,$00,$f8,$80,$e0,$10,$08,$10,$e0,$00,$30,$40,$80,$f0,$88,$88,$70,$00,$f8,$88,
+$10,$20,$20,$20,$20,$00,$70,$88,$88,$70,$88,$88,$70,$00,$70,$88,$88,$78,$08,$10,$60,$00,$00,$00,$20,$00,
+$00,$20,$00,$00,$00,$00,$20,$00,$00,$20,$20,$40,$18,$30,$60,$c0,$60,$30,$18,$00,$00,$00,$f8,$00,$f8,$00,
+$00,$00,$c0,$60,$30,$18,$30,$60,$c0,$00,$70,$88,$08,$10,$20,$00,$20,$00,$70,$88,$08,$68,$a8,$a8,$70,$00,
+$20,$50,$88,$88,$f8,$88,$88,$00,$f0,$48,$48,$70,$48,$48,$f0,$00,$30,$48,$80,$80,$80,$48,$30,$00,$e0,$50,
+$48,$48,$48,$50,$e0,$00,$f8,$80,$80,$f0,$80,$80,$f8,$00,$f8,$80,$80,$f0,$80,$80,$80,$00,$70,$88,$80,$b8,
+$88,$88,$70,$00,$88,$88,$88,$f8,$88,$88,$88,$00,$70,$20,$20,$20,$20,$20,$70,$00,$38,$10,$10,$10,$90,$90,
+$60,$00,$88,$90,$a0,$c0,$a0,$90,$88,$00,$80,$80,$80,$80,$80,$80,$f8,$00,$88,$d8,$a8,$a8,$88,$88,$88,$00,
+$88,$c8,$c8,$a8,$98,$98,$88,$00,$70,$88,$88,$88,$88,$88,$70,$00,$f0,$88,$88,$f0,$80,$80,$80,$00,$70,$88,
+$88,$88,$a8,$90,$68,$00,$f0,$88,$88,$f0,$a0,$90,$88,$00,$70,$88,$80,$70,$08,$88,$70,$00,$f8,$20,$20,$20,
+$20,$20,$20,$00,$88,$88,$88,$88,$88,$88,$70,$00,$88,$88,$88,$88,$50,$50,$20,$00,$88,$88,$88,$a8,$a8,$d8,
+$88,$00,$88,$88,$50,$20,$50,$88,$88,$00,$88,$88,$88,$70,$20,$20,$20,$00,$f8,$08,$10,$20,$40,$80,$f8,$00,
+$70,$40,$40,$40,$40,$40,$70,$00,$00,$00,$80,$40,$20,$10,$08,$00,$70,$10,$10,$10,$10,$10,$70,$00,$20,$50,
+$88,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$f8,$00,$40,$20,$10,$00,$00,$00,$00,$00,$00,$00,$70,$08,
+$78,$88,$78,$00,$80,$80,$b0,$c8,$88,$c8,$b0,$00,$00,$00,$70,$88,$80,$88,$70,$00,$08,$08,$68,$98,$88,$98,
+$68,$00,$00,$00,$70,$88,$f8,$80,$70,$00,$10,$28,$20,$f8,$20,$20,$20,$00,$00,$00,$68,$98,$98,$68,$08,$70,
+$80,$80,$f0,$88,$88,$88,$88,$00,$20,$00,$60,$20,$20,$20,$70,$00,$10,$00,$30,$10,$10,$10,$90,$60,$40,$40,
+$48,$50,$60,$50,$48,$00,$60,$20,$20,$20,$20,$20,$70,$00,$00,$00,$d0,$a8,$a8,$a8,$a8,$00,$00,$00,$b0,$c8,
+$88,$88,$88,$00,$00,$00,$70,$88,$88,$88,$70,$00,$00,$00,$b0,$c8,$c8,$b0,$80,$80,$00,$00,$68,$98,$98,$68,
+$08,$08,$00,$00,$b0,$c8,$80,$80,$80,$00,$00,$00,$78,$80,$f0,$08,$f0,$00,$40,$40,$f0,$40,$40,$48,$30,$00,
+$00,$00,$90,$90,$90,$90,$68,$00,$00,$00,$88,$88,$88,$50,$20,$00,$00,$00,$88,$a8,$a8,$a8,$50,$00,$00,$00,
+$88,$50,$20,$50,$88,$00,$00,$00,$88,$88,$98,$68,$08,$70,$00,$00,$f8,$10,$20,$40,$f8,$00,$18,$20,$20,$40,
+$20,$20,$18,$00,$20,$20,$20,$00,$20,$20,$20,$00,$c0,$20,$20,$10,$20,$20,$c0,$00,$40,$a8,$10,$00,$00,$00,
+$00,$00,$00,$00,$20,$50,$f8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$ff,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,
+$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$3c,$3c,$00,$00,$00,$ff,$ff,
+$ff,$ff,$ff,$ff,$00,$00,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$0f,$0f,$0f,$0f,$f0,$f0,$f0,$f0,$fc,$fc,$fc,$fc,
+$fc,$fc,$fc,$fc,$03,$03,$03,$03,$03,$03,$03,$03,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$11,$22,$44,$88,$11,$22,
+$44,$88,$88,$44,$22,$11,$88,$44,$22,$11,$fe,$7c,$38,$10,$00,$00,$00,$00,$00,$00,$00,$00,$10,$38,$7c,$fe,
+$80,$c0,$e0,$f0,$e0,$c0,$80,$00,$01,$03,$07,$0f,$07,$03,$01,$00,$ff,$7e,$3c,$18,$18,$3c,$7e,$ff,$81,$c3,
+$e7,$ff,$ff,$e7,$c3,$81,$f0,$f0,$f0,$f0,$00,$00,$00,$00,$00,$00,$00,$00,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,
+$00,$00,$00,$00,$00,$00,$00,$00,$f0,$f0,$f0,$f0,$33,$33,$cc,$cc,$33,$33,$cc,$cc,$00,$20,$20,$50,$50,$88,
+$f8,$00,$20,$20,$70,$20,$70,$20,$20,$00,$00,$00,$00,$50,$88,$a8,$50,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,
+$00,$00,$00,$00,$ff,$ff,$ff,$ff,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$ff,$ff,
+$ff,$ff,$00,$00,$00,$00,$00,$00,$68,$90,$90,$90,$68,$00,$30,$48,$48,$70,$48,$48,$70,$c0,$f8,$88,$80,$80,
+$80,$80,$80,$00,$00,$50,$70,$88,$f8,$80,$70,$00,$00,$00,$78,$80,$f0,$80,$78,$00,$00,$00,$78,$90,$90,$90,
+$60,$00,$20,$00,$60,$20,$20,$20,$70,$00,$50,$00,$70,$20,$20,$20,$70,$00,$f8,$20,$70,$a8,$a8,$70,$20,$f8,
+$20,$50,$88,$f8,$88,$50,$20,$00,$70,$88,$88,$88,$50,$50,$d8,$00,$30,$40,$40,$20,$50,$50,$50,$20,$00,$00,
+$00,$50,$a8,$a8,$50,$00,$08,$70,$a8,$a8,$a8,$70,$80,$00,$38,$40,$80,$f8,$80,$40,$38,$00,$70,$88,$88,$88,
+$88,$88,$88,$00,$00,$f8,$00,$f8,$00,$f8,$00,$00,$20,$20,$f8,$20,$20,$00,$f8,$00,$c0,$30,$08,$30,$c0,$00,
+$f8,$00,$50,$f8,$80,$f0,$80,$80,$f8,$00,$78,$80,$80,$f0,$80,$80,$78,$00,$20,$20,$20,$20,$20,$20,$a0,$40,
+$70,$20,$20,$20,$20,$20,$70,$00,$50,$70,$20,$20,$20,$20,$70,$00,$00,$18,$24,$24,$18,$00,$00,$00,$00,$30,
+$78,$78,$30,$00,$00,$00,$00,$00,$00,$00,$30,$00,$00,$00,$3e,$20,$20,$20,$a0,$60,$20,$00,$a0,$50,$50,$50,
+$00,$00,$00,$00,$40,$a0,$20,$40,$e0,$00,$00,$00,$00,$38,$38,$38,$38,$38,$38,$00,$3c,$42,$99,$a1,$a1,$99,
+$42,$3c,$00,$00,$90,$a8,$e8,$a8,$90,$00,$00,$00,$60,$10,$70,$90,$68,$00,$00,$00,$f0,$80,$f0,$88,$f0,$00,
+$00,$00,$90,$90,$90,$f8,$08,$00,$00,$00,$30,$50,$50,$70,$88,$00,$00,$00,$70,$88,$f8,$80,$70,$00,$00,$20,
+$70,$a8,$a8,$70,$20,$00,$00,$00,$78,$48,$40,$40,$40,$00,$00,$00,$88,$50,$20,$50,$88,$00,$00,$00,$88,$98,
+$a8,$c8,$88,$00,$00,$50,$20,$00,$98,$a8,$c8,$00,$00,$00,$90,$a0,$c0,$a0,$90,$00,$00,$00,$38,$28,$28,$48,
+$88,$00,$00,$00,$88,$d8,$a8,$88,$88,$00,$00,$00,$88,$88,$f8,$88,$88,$00,$00,$00,$70,$88,$88,$88,$70,$00,
+$00,$00,$78,$48,$48,$48,$48,$00,$00,$00,$78,$88,$78,$28,$48,$00,$00,$00,$f0,$88,$f0,$80,$80,$00,$00,$00,
+$78,$80,$80,$80,$78,$00,$00,$00,$f8,$20,$20,$20,$20,$00,$00,$00,$88,$50,$20,$40,$80,$00,$00,$00,$a8,$70,
+$20,$70,$a8,$00,$00,$00,$f0,$48,$70,$48,$f0,$00,$00,$00,$40,$40,$70,$48,$70,$00,$00,$00,$88,$88,$c8,$a8,
+$c8,$00,$00,$00,$f0,$08,$70,$08,$f0,$00,$00,$00,$a8,$a8,$a8,$a8,$f8,$00,$00,$00,$70,$88,$38,$88,$70,$00,
+$00,$00,$a8,$a8,$a8,$f8,$08,$00,$00,$00,$48,$48,$78,$08,$08,$00,$00,$00,$c0,$40,$70,$48,$70,$00,$90,$a8,
+$a8,$e8,$a8,$a8,$90,$00,$20,$50,$88,$88,$f8,$88,$88,$00,$f8,$88,$80,$f0,$88,$88,$f0,$00,$90,$90,$90,$90,
+$90,$f8,$08,$00,$38,$28,$28,$48,$48,$f8,$88,$00,$f8,$80,$80,$f0,$80,$80,$f8,$00,$20,$70,$a8,$a8,$a8,$70,
+$20,$00,$f8,$88,$88,$80,$80,$80,$80,$00,$88,$88,$50,$20,$50,$88,$88,$00,$88,$88,$98,$a8,$c8,$88,$88,$00,
+$50,$20,$88,$98,$a8,$c8,$88,$00,$88,$90,$a0,$c0,$a0,$90,$88,$00,$18,$28,$48,$48,$48,$48,$88,$00,$88,$d8,
+$a8,$a8,$88,$88,$88,$00,$88,$88,$88,$f8,$88,$88,$88,$00,$70,$88,$88,$88,$88,$88,$70,$00,$f8,$88,$88,$88,
+$88,$88,$88,$00,$78,$88,$88,$78,$28,$48,$88,$00,$f0,$88,$88,$f0,$80,$80,$80,$00,$70,$88,$80,$80,$80,$88,
+$70,$00,$f8,$20,$20,$20,$20,$20,$20,$00,$88,$88,$88,$50,$20,$40,$80,$00,$a8,$a8,$70,$20,$70,$a8,$a8,$00,
+$f0,$48,$48,$70,$48,$48,$f0,$00,$80,$80,$80,$f0,$88,$88,$f0,$00,$88,$88,$88,$c8,$a8,$a8,$c8,$00,$f0,$08,
+$08,$30,$08,$08,$f0,$00,$a8,$a8,$a8,$a8,$a8,$a8,$f8,$00,$70,$88,$08,$78,$08,$88,$70,$00,$a8,$a8,$a8,$a8,
+$a8,$f8,$08,$00,$88,$88,$88,$88,$78,$08,$08,$00,$c0,$40,$40,$70,$48,$48,$70,$00
+);
+
+const kgiFont8: array[0..256*8-1] of Byte  = (
+$00,$00,$00,$00,$00,$00,$00,$00,$7e,$81,$a5,$81,$bd,$99,$81,$7e,$7e,$ff,$db,$ff,$c3,$e7,$ff,$7e,$6c,$fe,
+$fe,$fe,$7c,$38,$10,$00,$10,$38,$7c,$fe,$7c,$38,$10,$00,$38,$7c,$38,$fe,$fe,$d6,$10,$38,$10,$10,$38,$7c,
+$fe,$7c,$10,$38,$00,$00,$18,$3c,$3c,$18,$00,$00,$ff,$ff,$e7,$c3,$c3,$e7,$ff,$ff,$00,$3c,$66,$42,$42,$66,
+$3c,$00,$ff,$c3,$99,$bd,$bd,$99,$c3,$ff,$0f,$07,$0f,$7d,$cc,$cc,$cc,$78,$3c,$66,$66,$66,$3c,$18,$7e,$18,
+$3f,$33,$3f,$30,$30,$70,$f0,$e0,$7f,$63,$7f,$63,$63,$67,$e6,$c0,$99,$5a,$3c,$e7,$e7,$3c,$5a,$99,$80,$e0,
+$f8,$fe,$f8,$e0,$80,$00,$02,$0e,$3e,$fe,$3e,$0e,$02,$00,$18,$3c,$7e,$18,$18,$7e,$3c,$18,$66,$66,$66,$66,
+$66,$00,$66,$00,$7f,$db,$db,$7b,$1b,$1b,$1b,$00,$7e,$c3,$78,$cc,$cc,$78,$8c,$f8,$00,$00,$00,$00,$7e,$7e,
+$7e,$00,$18,$3c,$7e,$18,$7e,$3c,$18,$ff,$18,$3c,$7e,$18,$18,$18,$18,$00,$18,$18,$18,$18,$7e,$3c,$18,$00,
+$00,$18,$0c,$fe,$0c,$18,$00,$00,$00,$30,$60,$fe,$60,$30,$00,$00,$00,$00,$c0,$c0,$c0,$fe,$00,$00,$00,$24,
+$66,$ff,$66,$24,$00,$00,$00,$18,$3c,$7e,$ff,$ff,$00,$00,$00,$ff,$ff,$7e,$3c,$18,$00,$00,$00,$00,$00,$00,
+$00,$00,$00,$00,$30,$78,$78,$30,$30,$00,$30,$00,$6c,$6c,$6c,$00,$00,$00,$00,$00,$6c,$6c,$fe,$6c,$fe,$6c,
+$6c,$00,$30,$7c,$c0,$78,$0c,$f8,$30,$00,$00,$c6,$cc,$18,$30,$66,$c6,$00,$38,$6c,$38,$76,$dc,$cc,$76,$00,
+$60,$60,$c0,$00,$00,$00,$00,$00,$18,$30,$60,$60,$60,$30,$18,$00,$60,$30,$18,$18,$18,$30,$60,$00,$00,$66,
+$3c,$ff,$3c,$66,$00,$00,$00,$30,$30,$fc,$30,$30,$00,$00,$00,$00,$00,$00,$00,$70,$30,$60,$00,$00,$00,$fc,
+$00,$00,$00,$00,$00,$00,$00,$00,$00,$30,$30,$00,$06,$0c,$18,$30,$60,$c0,$80,$00,$78,$cc,$dc,$fc,$ec,$cc,
+$78,$00,$30,$f0,$30,$30,$30,$30,$fc,$00,$78,$cc,$0c,$38,$60,$cc,$fc,$00,$78,$cc,$0c,$38,$0c,$cc,$78,$00,
+$1c,$3c,$6c,$cc,$fe,$0c,$0c,$00,$fc,$c0,$f8,$0c,$0c,$cc,$78,$00,$38,$60,$c0,$f8,$cc,$cc,$78,$00,$fc,$cc,
+$0c,$18,$30,$60,$60,$00,$78,$cc,$cc,$78,$cc,$cc,$78,$00,$78,$cc,$cc,$7c,$0c,$18,$70,$00,$00,$00,$30,$30,
+$00,$30,$30,$00,$00,$00,$30,$30,$00,$70,$30,$60,$18,$30,$60,$c0,$60,$30,$18,$00,$00,$00,$fc,$00,$fc,$00,
+$00,$00,$60,$30,$18,$0c,$18,$30,$60,$00,$78,$cc,$0c,$18,$30,$00,$30,$00,$7c,$c6,$de,$de,$de,$c0,$78,$00,
+$30,$78,$cc,$cc,$fc,$cc,$cc,$00,$fc,$66,$66,$7c,$66,$66,$fc,$00,$3c,$66,$c0,$c0,$c0,$66,$3c,$00,$fc,$6c,
+$66,$66,$66,$6c,$fc,$00,$fe,$62,$68,$78,$68,$62,$fe,$00,$fe,$62,$68,$78,$68,$60,$f0,$00,$3c,$66,$c0,$c0,
+$ce,$66,$3e,$00,$cc,$cc,$cc,$fc,$cc,$cc,$cc,$00,$78,$30,$30,$30,$30,$30,$78,$00,$1e,$0c,$0c,$0c,$cc,$cc,
+$78,$00,$e6,$66,$6c,$78,$6c,$66,$e6,$00,$f0,$60,$60,$60,$62,$66,$fe,$00,$c6,$ee,$fe,$d6,$c6,$c6,$c6,$00,
+$c6,$e6,$f6,$de,$ce,$c6,$c6,$00,$38,$6c,$c6,$c6,$c6,$6c,$38,$00,$fc,$66,$66,$7c,$60,$60,$f0,$00,$78,$cc,
+$cc,$cc,$dc,$78,$1c,$00,$fc,$66,$66,$7c,$78,$6c,$e6,$00,$78,$cc,$e0,$38,$1c,$cc,$78,$00,$fc,$b4,$30,$30,
+$30,$30,$78,$00,$cc,$cc,$cc,$cc,$cc,$cc,$fc,$00,$cc,$cc,$cc,$cc,$cc,$78,$30,$00,$c6,$c6,$c6,$d6,$fe,$ee,
+$c6,$00,$c6,$c6,$6c,$38,$6c,$c6,$c6,$00,$cc,$cc,$cc,$78,$30,$30,$78,$00,$fe,$cc,$98,$30,$62,$c6,$fe,$00,
+$78,$60,$60,$60,$60,$60,$78,$00,$c0,$60,$30,$18,$0c,$06,$02,$00,$78,$18,$18,$18,$18,$18,$78,$00,$10,$38,
+$6c,$c6,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$30,$30,$18,$00,$00,$00,$00,$00,$00,$00,$78,$0c,
+$7c,$cc,$76,$00,$e0,$60,$7c,$66,$66,$66,$bc,$00,$00,$00,$78,$cc,$c0,$cc,$78,$00,$1c,$0c,$0c,$7c,$cc,$cc,
+$76,$00,$00,$00,$78,$cc,$fc,$c0,$78,$00,$38,$6c,$60,$f0,$60,$60,$f0,$00,$00,$00,$76,$cc,$cc,$7c,$0c,$f8,
+$e0,$60,$6c,$76,$66,$66,$e6,$00,$30,$00,$70,$30,$30,$30,$78,$00,$18,$00,$78,$18,$18,$18,$d8,$70,$e0,$60,
+$66,$6c,$78,$6c,$e6,$00,$70,$30,$30,$30,$30,$30,$78,$00,$00,$00,$ec,$fe,$d6,$c6,$c6,$00,$00,$00,$f8,$cc,
+$cc,$cc,$cc,$00,$00,$00,$78,$cc,$cc,$cc,$78,$00,$00,$00,$dc,$66,$66,$7c,$60,$f0,$00,$00,$76,$cc,$cc,$7c,
+$0c,$1e,$00,$00,$d8,$6c,$6c,$60,$f0,$00,$00,$00,$7c,$c0,$78,$0c,$f8,$00,$10,$30,$7c,$30,$30,$34,$18,$00,
+$00,$00,$cc,$cc,$cc,$cc,$76,$00,$00,$00,$cc,$cc,$cc,$78,$30,$00,$00,$00,$c6,$c6,$d6,$fe,$6c,$00,$00,$00,
+$c6,$6c,$38,$6c,$c6,$00,$00,$00,$cc,$cc,$cc,$7c,$0c,$f8,$00,$00,$fc,$98,$30,$64,$fc,$00,$1c,$30,$30,$e0,
+$30,$30,$1c,$00,$18,$18,$18,$00,$18,$18,$18,$00,$e0,$30,$30,$1c,$30,$30,$e0,$00,$76,$dc,$00,$00,$00,$00,
+$00,$00,$10,$38,$6c,$c6,$c6,$c6,$fe,$00,$78,$cc,$c0,$cc,$78,$18,$0c,$78,$00,$cc,$00,$cc,$cc,$cc,$7e,$00,
+$1c,$00,$78,$cc,$fc,$c0,$78,$00,$7e,$c3,$3c,$06,$3e,$66,$3f,$00,$cc,$00,$78,$0c,$7c,$cc,$7e,$00,$e0,$00,
+$78,$0c,$7c,$cc,$7e,$00,$30,$30,$78,$0c,$7c,$cc,$7e,$00,$00,$00,$7c,$c0,$c0,$7c,$06,$3c,$7e,$c3,$3c,$66,
+$7e,$60,$3c,$00,$cc,$00,$78,$cc,$fc,$c0,$78,$00,$e0,$00,$78,$cc,$fc,$c0,$78,$00,$cc,$00,$70,$30,$30,$30,
+$78,$00,$7c,$c6,$38,$18,$18,$18,$3c,$00,$e0,$00,$70,$30,$30,$30,$78,$00,$cc,$30,$78,$cc,$cc,$fc,$cc,$00,
+$30,$30,$00,$78,$cc,$fc,$cc,$00,$1c,$00,$fc,$60,$78,$60,$fc,$00,$00,$00,$7f,$0c,$7f,$cc,$7f,$00,$3e,$6c,
+$cc,$fe,$cc,$cc,$ce,$00,$78,$cc,$00,$78,$cc,$cc,$78,$00,$00,$cc,$00,$78,$cc,$cc,$78,$00,$00,$e0,$00,$78,
+$cc,$cc,$78,$00,$78,$cc,$00,$cc,$cc,$cc,$7e,$00,$00,$e0,$00,$cc,$cc,$cc,$7e,$00,$00,$cc,$00,$cc,$cc,$fc,
+$0c,$f8,$c6,$38,$7c,$c6,$c6,$7c,$38,$00,$cc,$00,$cc,$cc,$cc,$cc,$78,$00,$18,$18,$7e,$c0,$c0,$7e,$18,$18,
+$38,$6c,$64,$f0,$60,$e6,$fc,$00,$cc,$cc,$78,$fc,$30,$fc,$30,$00,$f0,$d8,$d8,$f4,$cc,$de,$cc,$0e,$0e,$1b,
+$18,$7e,$18,$18,$d8,$70,$1c,$00,$78,$0c,$7c,$cc,$7e,$00,$38,$00,$70,$30,$30,$30,$78,$00,$00,$1c,$00,$78,
+$cc,$cc,$78,$00,$00,$1c,$00,$cc,$cc,$cc,$7e,$00,$00,$f8,$00,$f8,$cc,$cc,$cc,$00,$fc,$00,$cc,$ec,$fc,$dc,
+$cc,$00,$3c,$6c,$6c,$3e,$00,$7e,$00,$00,$3c,$66,$66,$3c,$00,$7e,$00,$00,$30,$00,$30,$60,$c0,$cc,$78,$00,
+$00,$00,$00,$fc,$c0,$c0,$00,$00,$00,$00,$00,$fc,$0c,$0c,$00,$00,$c6,$cc,$d8,$3e,$63,$ce,$98,$1f,$c6,$cc,
+$d8,$f3,$67,$cf,$9f,$03,$00,$18,$00,$18,$18,$3c,$3c,$18,$00,$33,$66,$cc,$66,$33,$00,$00,$00,$cc,$66,$33,
+$66,$cc,$00,$00,$22,$88,$22,$88,$22,$88,$22,$88,$55,$aa,$55,$aa,$55,$aa,$55,$aa,$dc,$76,$dc,$76,$dc,$76,
+$dc,$76,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$f8,$18,$18,$18,$18,$18,$f8,$18,$f8,$18,$18,$18,
+$36,$36,$36,$36,$f6,$36,$36,$36,$00,$00,$00,$00,$fe,$36,$36,$36,$00,$00,$f8,$18,$f8,$18,$18,$18,$36,$36,
+$f6,$06,$f6,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$00,$00,$fe,$06,$f6,$36,$36,$36,$36,$36,$f6,$06,
+$fe,$00,$00,$00,$36,$36,$36,$36,$fe,$00,$00,$00,$18,$18,$f8,$18,$f8,$00,$00,$00,$00,$00,$00,$00,$f8,$18,
+$18,$18,$18,$18,$18,$18,$1f,$00,$00,$00,$18,$18,$18,$18,$ff,$00,$00,$00,$00,$00,$00,$00,$ff,$18,$18,$18,
+$18,$18,$18,$18,$1f,$18,$18,$18,$00,$00,$00,$00,$ff,$00,$00,$00,$18,$18,$18,$18,$ff,$18,$18,$18,$18,$18,
+$1f,$18,$1f,$18,$18,$18,$36,$36,$36,$36,$37,$36,$36,$36,$36,$36,$37,$30,$3f,$00,$00,$00,$00,$00,$3f,$30,
+$37,$36,$36,$36,$36,$36,$f7,$00,$ff,$00,$00,$00,$00,$00,$ff,$00,$f7,$36,$36,$36,$36,$36,$37,$30,$37,$36,
+$36,$36,$00,$00,$ff,$00,$ff,$00,$00,$00,$36,$36,$f7,$00,$f7,$36,$36,$36,$18,$18,$ff,$00,$ff,$00,$00,$00,
+$36,$36,$36,$36,$ff,$00,$00,$00,$00,$00,$ff,$00,$ff,$18,$18,$18,$00,$00,$00,$00,$ff,$36,$36,$36,$36,$36,
+$36,$36,$3f,$00,$00,$00,$18,$18,$1f,$18,$1f,$00,$00,$00,$00,$00,$1f,$18,$1f,$18,$18,$18,$00,$00,$00,$00,
+$3f,$36,$36,$36,$36,$36,$36,$36,$f7,$36,$36,$36,$18,$18,$ff,$00,$ff,$18,$18,$18,$18,$18,$18,$18,$f8,$00,
+$00,$00,$00,$00,$00,$00,$1f,$18,$18,$18,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$ff,$ff,$ff,$ff,
+$f0,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$00,$00,
+$76,$dc,$c8,$dc,$76,$00,$00,$78,$cc,$f8,$cc,$f8,$c0,$c0,$00,$fe,$c6,$c0,$c0,$c0,$c0,$00,$00,$fe,$6c,$6c,
+$6c,$6c,$6c,$00,$fe,$66,$30,$18,$30,$66,$fe,$00,$00,$00,$7e,$cc,$cc,$cc,$78,$00,$00,$66,$66,$66,$66,$7c,
+$60,$c0,$00,$76,$dc,$18,$18,$18,$18,$00,$fc,$30,$78,$cc,$cc,$78,$30,$fc,$38,$6c,$c6,$fe,$c6,$6c,$38,$00,
+$38,$6c,$c6,$c6,$6c,$6c,$ee,$00,$1c,$30,$18,$7c,$cc,$cc,$78,$00,$00,$00,$7e,$db,$db,$7e,$00,$00,$06,$0c,
+$7e,$db,$db,$7e,$60,$c0,$3c,$60,$c0,$fc,$c0,$60,$3c,$00,$78,$cc,$cc,$cc,$cc,$cc,$cc,$00,$00,$fc,$00,$fc,
+$00,$fc,$00,$00,$30,$30,$fc,$30,$30,$00,$fc,$00,$60,$30,$18,$30,$60,$00,$fc,$00,$18,$30,$60,$30,$18,$00,
+$fc,$00,$0e,$1b,$1b,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$d8,$d8,$70,$30,$30,$00,$fc,$00,$30,$30,$00,
+$00,$72,$9c,$00,$72,$9c,$00,$00,$38,$6c,$6c,$38,$00,$00,$00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00,$00,
+$00,$00,$18,$00,$00,$00,$0f,$0c,$0c,$0c,$ec,$6c,$3c,$1c,$78,$6c,$6c,$6c,$6c,$00,$00,$00,$78,$0c,$38,$60,
+$7c,$00,$00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
+);
+
+const kgiFont6PropWidth: array[0..256-1] of Byte = (
+  $08,$08,$08,$07,$07,$07,$07,$04,$08,$07,$08,$08,$06,$06,$06,$07,
+  $06,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
+  $85,$21,$13,$05,$05,$05,$05,$13,$13,$13,$05,$05,$12,$14,$12,$05,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$21,$12,$05,$05,$05,$05,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$05,$05,$05,$05,$05,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$13,$05,$05,
+  $13,$05,$05,$05,$05,$05,$05,$05,$05,$13,$04,$14,$13,$05,$05,$05,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$14,$21,$04,$05,$08,
+  $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$04,
+  $44,$08,$08,$08,$08,$08,$08,$08,$05,$04,$05,$08,$08,$08,$08,$08,
+  $05,$05,$05,$05,$05,$05,$13,$13,$05,$05,$05,$04,$05,$05,$05,$05,
+  $05,$05,$05,$05,$05,$03,$04,$04,$06,$05,$04,$07,$04,$03,$05,$08,
+  $05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$04,$05,$05,$05,$05,
+  $14,$05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$05,$05,$14,$05,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05
+);
+
+const kgiFont8PropWidth: array[0..256-1] of Byte = (
+  $08,$08,$08,$07,$07,$07,$07,$06,$08,$07,$08,$08,$07,$08,$08,$08,
+  $07,$07,$07,$07,$08,$08,$07,$08,$07,$07,$07,$07,$07,$08,$08,$08,
+  $85,$14,$15,$07,$06,$07,$07,$03,$14,$14,$08,$06,$13,$06,$22,$07,
+  $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$22,$13,$05,$06,$15,$06,
+  $07,$06,$07,$07,$07,$07,$07,$07,$06,$14,$07,$07,$07,$07,$07,$07,
+  $07,$06,$07,$06,$06,$06,$06,$07,$07,$06,$07,$14,$07,$14,$07,$08,
+  $23,$07,$07,$06,$07,$06,$06,$07,$07,$14,$05,$07,$14,$07,$06,$06,
+  $07,$07,$06,$06,$15,$07,$06,$07,$07,$06,$06,$06,$32,$06,$07,$07,
+  $06,$07,$06,$08,$07,$07,$07,$07,$08,$06,$06,$06,$07,$05,$06,$06,
+  $06,$08,$07,$06,$06,$06,$07,$07,$06,$07,$06,$07,$07,$06,$07,$08,
+  $07,$05,$06,$07,$06,$06,$16,$16,$06,$06,$06,$08,$08,$06,$08,$08,
+  $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
+  $38,$08,$08,$38,$08,$08,$38,$28,$28,$28,$08,$08,$28,$08,$08,$08,
+  $08,$08,$08,$28,$38,$38,$28,$08,$08,$08,$38,$08,$08,$08,$48,$08,
+  $07,$06,$07,$07,$07,$07,$07,$07,$06,$07,$07,$06,$08,$08,$06,$06,
+  $06,$06,$06,$06,$35,$05,$06,$07,$15,$32,$32,$08,$15,$15,$24,$08
+);
+
+
+function createFontTexture (const font: PByte; const fontwdt: PByte; prop: Boolean): GLuint;
+const
+  Width = 16*8;
+  Height = 16*8;
+var
+  tex, tpp: PByte;
+  b: Byte;
+  cc: Integer;
+  x, y, dx, dy: Integer;
+begin
+  GetMem(tex, Width*Height*4);
+
+  for cc := 0 to 255 do
+  begin
+    x := (cc mod 16)*8;
+    y := (cc div 16)*8;
+    for dy := 0 to 7 do
+    begin
+      b := font[cc*8+dy];
+      if prop then b := b shl (fontwdt[cc] shr 4);
+      tpp := tex+((y+dy)*(Width*4))+x*4;
+      for dx := 0 to 7 do
+      begin
+        if ((b and $80) <> 0) then
+        begin
+          tpp^ := 255; Inc(tpp);
+          tpp^ := 255; Inc(tpp);
+          tpp^ := 255; Inc(tpp);
+          tpp^ := 255; Inc(tpp);
+        end
+        else
+        begin
+          tpp^ := 0; Inc(tpp);
+          tpp^ := 0; Inc(tpp);
+          tpp^ := 0; Inc(tpp);
+          tpp^ := 0; Inc(tpp);
+        end;
+        b := (b and $7f) shl 1;
+      end;
+    end;
+  end;
+
+  glGenTextures(1, @result);
+  if (result = 0) then raise Exception.Create('can''t create Holmes font texture');
+
+  glBindTexture(GL_TEXTURE_2D, result);
+  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+  //GLfloat[4] bclr = 0.0;
+  //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
+
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
+  glFlush();
+
+  //FreeMem(tex);
+end;
+
+
+{
+var
+  font6texid: GLuint = 0;
+  font8texid: GLuint = 0;
+  prfont6texid: GLuint = 0;
+  prfont8texid: GLuint = 0;
+
+
+procedure deleteFonts ();
+begin
+  if (font6texid <> 0) then glDeleteTextures(1, @font6texid);
+  if (font8texid <> 0) then glDeleteTextures(1, @font8texid);
+  if (prfont6texid <> 0) then glDeleteTextures(1, @prfont6texid);
+  if (prfont8texid <> 0) then glDeleteTextures(1, @prfont8texid);
+  font6texid := 0;
+  font8texid := 0;
+  prfont6texid := 0;
+  prfont8texid := 0;
+end;
+
+
+procedure createFonts ();
+begin
+  if (font6texid = 0) then font6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, false);
+  if (font8texid = 0) then font8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, false);
+  if (prfont6texid = 0) then prfont6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, true);
+  if (prfont8texid = 0) then prfont8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, true);
+end;
+}