DEADSOFTWARE

mempool is optional now
[d2df-sdl.git] / src / gx / glgfx.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit glgfx;
19 interface
21 uses
22 SysUtils, Classes,
23 GL, GLExt, SDL2,
24 sdlcarcass;
27 // ////////////////////////////////////////////////////////////////////////// //
28 type
29 TGxRGBA = packed record
30 public
31 r, g, b, a: Byte;
33 public
34 constructor Create (ar, ag, ab: Integer; aa: Integer=255);
36 function asUInt (): LongWord; inline;
37 function isOpaque (): Boolean; inline;
38 function isTransparent (): Boolean; inline;
40 // WARNING! This function does blending in RGB space, and RGB space is not linear!
41 // alpha value of `self` doesn't matter
42 // `aa` means: 255 for replace color, 0 for keep `self`
43 function blend (ar, ag, ab, aa: Integer): TGxRGBA; inline;
44 end;
47 // ////////////////////////////////////////////////////////////////////////// //
48 type
49 THMouseEvent = record
50 public
51 const
52 // both for but and for bstate
53 None = 0;
54 Left = $0001;
55 Right = $0002;
56 Middle = $0004;
57 WheelUp = $0008;
58 WheelDown = $0010;
60 // event types
61 type
62 TKind = (Release, Press, Motion);
64 public
65 kind: TKind; // motion, press, release
66 x, y: Integer; // current mouse position
67 dx, dy: Integer; // for wheel this is wheel motion, otherwise this is relative mouse motion
68 but: Word; // current pressed/released button, or 0 for motion
69 bstate: Word; // button state BEFORE event (i.e. press/release modifications aren't done yet)
70 kstate: Word; // keyboard state (see THKeyEvent);
72 public
73 function press (): Boolean; inline;
74 function release (): Boolean; inline;
75 function motion (): Boolean; inline;
76 end;
78 THKeyEvent = record
79 public
80 const
81 // modifiers
82 ModCtrl = $0001;
83 ModAlt = $0002;
84 ModShift = $0004;
85 ModHyper = $0008;
87 // event types
88 type
89 TKind = (Release, Press);
91 public
92 kind: TKind;
93 scan: Word; // SDL_SCANCODE_XXX
94 sym: LongWord; // SDLK_XXX
95 x, y: Integer; // current mouse position
96 bstate: Word; // button state
97 kstate: Word; // keyboard state BEFORE event (i.e. press/release modifications aren't done yet)
99 public
100 function press (): Boolean; inline;
101 function release (): Boolean; inline;
102 end;
105 // ////////////////////////////////////////////////////////////////////////// //
106 // setup 2D OpenGL mode; will be called automatically in `glInit()`
107 procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
109 type
110 TScissorSave = record
111 public
112 wassc: Boolean;
113 scxywh: packed array[0..3] of GLint;
115 public
117 public
118 procedure save (enableScissoring: Boolean);
119 procedure restore ();
121 // set new scissor rect, bounded by the saved scissor rect
122 procedure setRect (x, y, w, h: Integer);
123 end;
126 procedure oglDrawCursor ();
127 procedure oglDrawCursorAt (msX, msY: Integer);
129 // return `false` if destination rect is empty
130 // modifies rect0
131 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean;
133 procedure normRGBA (var r, g, b, a: Integer); inline;
134 function setupGLColor (r, g, b, a: Integer): Boolean;
135 function isScaled (): Boolean;
137 function textWidth6 (const s: AnsiString): Integer;
138 function textWidth8 (const s: AnsiString): Integer;
139 // return width (including last empty pixel)
140 function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
141 procedure drawLine (x1, y1, x2, y2: Integer; r, g, b: Integer; a: Integer=255);
142 procedure drawVLine (x, y, len: Integer; r, g, b: Integer; a: Integer=255);
143 procedure drawHLine (x, y, len: Integer; r, g, b: Integer; a: Integer=255);
144 procedure drawRect (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
145 procedure drawRectUI (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
146 procedure darkenRect (x, y, w, h: Integer; a: Integer);
147 procedure fillRect (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
148 function drawText6 (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
149 function drawText8 (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
150 function drawText6Prop (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
151 function drawText8Prop (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
152 // x-centered at `x`
153 function drawText6XC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
154 function drawText8XC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
155 function drawText6PropXC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
156 function drawText8PropXC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
159 // ////////////////////////////////////////////////////////////////////////// //
160 // event handlers
161 var
162 evMouseCB: function (var ev: THMouseEvent): Boolean = nil; // `true`: event eaten
163 evKeyCB: function (var ev: THKeyEvent): Boolean = nil; // `true`: event eaten
166 // ////////////////////////////////////////////////////////////////////////// //
167 function getMouseX (): Integer; inline;
168 function getMouseY (): Integer; inline;
169 function getButState (): Word; inline;
170 function getModState (): Word; inline;
173 // ////////////////////////////////////////////////////////////////////////// //
174 property
175 gMouseX: Integer read getMouseX;
176 gMouseY: Integer read getMouseY;
177 gButState: Word read getButState;
178 gModState: Word read getModState;
180 var
181 gGfxDoClear: Boolean = true;
184 // ////////////////////////////////////////////////////////////////////////// //
185 // any mods = 255: nothing was defined
186 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
188 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
189 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
191 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
192 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
195 implementation
198 var
199 curButState: Word = 0;
200 curModState: Word = 0;
201 curMsX: Integer = 0;
202 curMsY: Integer = 0;
205 // ////////////////////////////////////////////////////////////////////////// //
206 function strEquCI (const s0, s1: AnsiString): Boolean;
207 var
208 f: Integer;
209 c0, c1: AnsiChar;
210 begin
211 result := (Length(s0) = Length(s1));
212 if result then
213 begin
214 for f := 1 to Length(s0) do
215 begin
216 c0 := s0[f];
217 if (c0 >= 'a') and (c0 <= 'z') then Dec(c0, 32); // poor man's `toupper()`
218 c1 := s1[f];
219 if (c1 >= 'a') and (c1 <= 'z') then Dec(c1, 32); // poor man's `toupper()`
220 if (c0 <> c1) then begin result := false; exit; end;
221 end;
222 end;
223 end;
226 // ////////////////////////////////////////////////////////////////////////// //
227 function getMouseX (): Integer; inline; begin result := curMsX; end;
228 function getMouseY (): Integer; inline; begin result := curMsY; end;
229 function getButState (): Word; inline; begin result := curButState; end;
230 function getModState (): Word; inline; begin result := curModState; end;
233 // ////////////////////////////////////////////////////////////////////////// //
234 function THMouseEvent.press (): Boolean; inline; begin result := (kind = TKind.Press); end;
235 function THMouseEvent.release (): Boolean; inline; begin result := (kind = TKind.Release); end;
236 function THMouseEvent.motion (): Boolean; inline; begin result := (kind = TKind.Motion); end;
238 function THKeyEvent.press (): Boolean; inline; begin result := (kind = TKind.Press); end;
239 function THKeyEvent.release (): Boolean; inline; begin result := (kind = TKind.Release); end;
242 // ////////////////////////////////////////////////////////////////////////// //
243 constructor TGxRGBA.Create (ar, ag, ab: Integer; aa: Integer=255);
244 begin
245 if (ar < 0) then r := 0 else if (ar > 255) then r := 255 else r := Byte(ar);
246 if (ag < 0) then g := 0 else if (ag > 255) then g := 255 else g := Byte(ag);
247 if (ab < 0) then b := 0 else if (ab > 255) then b := 255 else b := Byte(ab);
248 if (aa < 0) then a := 0 else if (aa > 255) then a := 255 else a := Byte(aa);
249 end;
251 function TGxRGBA.asUInt (): LongWord; inline; begin result := LongWord(r) or (LongWord(g) shl 8) or (LongWord(b) shl 16) or (LongWord(a) shl 24); end;
253 function TGxRGBA.isOpaque (): Boolean; inline; begin result := (a = 255); end;
254 function TGxRGBA.isTransparent (): Boolean; inline; begin result := (a = 0); end;
256 function TGxRGBA.blend (ar, ag, ab, aa: Integer): TGxRGBA; inline;
257 var
258 me, it, a_tmp_, dc_tmp_, srb_tmp_, sg_tmp_, drb_tmp_, dg_tmp_, orb_tmp_, og_tmp_: LongWord;
259 begin
260 if (aa <= 0) then begin result := self; exit; end;
261 result := TGxRGBA.Create(ar, ag, ab, aa);
262 if (aa >= 255) then begin result.a := a; exit; end;
263 me := asUInt;
264 it := result.asUInt;
265 a_tmp_ := (256-(255-(it shr 24))) and (-(1-(((255-(it shr 24))+1) shr 8))); // to not loose bits, but 255 should become 0
266 dc_tmp_ := me and $ffffff;
267 srb_tmp_ := (it and $ff00ff);
268 sg_tmp_ := (it and $00ff00);
269 drb_tmp_ := (dc_tmp_ and $ff00ff);
270 dg_tmp_ := (dc_tmp_ and $00ff00);
271 orb_tmp_ := (drb_tmp_+(((srb_tmp_-drb_tmp_)*a_tmp_+$800080) shr 8)) and $ff00ff;
272 og_tmp_ := (dg_tmp_+(((sg_tmp_-dg_tmp_)*a_tmp_+$008000) shr 8)) and $00ff00;
273 me := (orb_tmp_ or og_tmp_); // or $ff000000; /* and $ffffff;*/
274 result.r := Byte(me and $ff);
275 result.g := Byte((me shr 8) and $ff);
276 result.b := Byte((me shr 16) and $ff);
277 result.a := a;
278 end;
281 // ////////////////////////////////////////////////////////////////////////// //
282 // any mods = 255: nothing was defined
283 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
284 var
285 pos, epos: Integer;
286 begin
287 kmods := 255;
288 mbuts := 255;
289 pos := 1;
290 //while (pos <= Length(s)) and (s[pos] <= ' ') do Inc(pos);
291 if (pos < Length(s)) and ((s[pos] = '+') or (s[pos] = '-') or (s[pos] = '*')) then Inc(pos);
292 while (pos < Length(s)) do
293 begin
294 if (Length(s)-pos >= 2) and (s[pos+1] = '-') then
295 begin
296 case s[pos] of
297 'C', 'c': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModCtrl; Inc(pos, 2); continue; end;
298 'M', 'm': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModAlt; Inc(pos, 2); continue; end;
299 'S', 's': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModShift; Inc(pos, 2); continue; end;
300 end;
301 break;
302 end;
303 if (Length(s)-pos >= 4) and ((s[pos+1] = 'M') or (s[pos+1] = 'm')) and ((s[pos+2] = 'B') or (s[pos+1] = 'b')) and (s[pos+3] = '-') then
304 begin
305 case s[pos] of
306 'L', 'l': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Left; Inc(pos, 4); continue; end;
307 'R', 'r': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Right; Inc(pos, 4); continue; end;
308 'M', 'm': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Middle; Inc(pos, 4); continue; end;
309 end;
310 break;
311 end;
312 break;
313 end;
314 epos := Length(s)+1;
315 while (epos > pos) and (s[epos-1] <= ' ') do Dec(epos);
316 if (epos > pos) then result := Copy(s, pos, epos-pos) else result := '';
317 end;
320 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
321 var
322 f: Integer;
323 kmods: Byte = 255;
324 mbuts: Byte = 255;
325 kname: AnsiString;
326 begin
327 result := false;
328 if (Length(s) > 0) then
329 begin
330 if (s[1] = '+') then begin if (not ev.press) then exit; end
331 else if (s[1] = '-') then begin if (not ev.release) then exit; end
332 else if (s[1] = '*') then begin end
333 else if (not ev.press) then exit;
334 end;
335 kname := parseModKeys(s, kmods, mbuts);
336 if (kmods = 255) then kmods := 0;
337 if (ev.kstate <> kmods) then exit;
338 if (mbuts <> 255) and (ev.bstate <> mbuts) then exit;
340 if (strEquCI(kname, 'Enter')) then kname := 'RETURN';
342 for f := 0 to SDL_NUM_SCANCODES-1 do
343 begin
344 if strEquCI(kname, SDL_GetScancodeName(f)) then
345 begin
346 result := (ev.scan = f);
347 exit;
348 end;
349 end;
350 end;
353 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
354 begin
355 result := (ev = s);
356 end;
359 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
360 var
361 kmods: Byte = 255;
362 mbuts: Byte = 255;
363 kname: AnsiString;
364 but: Integer = -1;
365 begin
366 result := false;
368 if (Length(s) > 0) then
369 begin
370 if (s[1] = '+') then begin if (not ev.press) then exit; end
371 else if (s[1] = '-') then begin if (not ev.release) then exit; end
372 else if (s[1] = '*') then begin if (not ev.motion) then exit; end
373 else if (not ev.press) then exit;
374 end;
376 kname := parseModKeys(s, kmods, mbuts);
377 if strEquCI(kname, 'LMB') then but := THMouseEvent.Left
378 else if strEquCI(kname, 'RMB') then but := THMouseEvent.Right
379 else if strEquCI(kname, 'MMB') then but := THMouseEvent.Middle
380 else if strEquCI(kname, 'WheelUp') or strEquCI(kname, 'WUP') then but := THMouseEvent.WheelUp
381 else if strEquCI(kname, 'WheelDown') or strEquCI(kname, 'WDN') or strEquCI(kname, 'WDOWN') then but := THMouseEvent.WheelDown
382 else if strEquCI(kname, 'None') then but := 0
383 else exit;
385 if (mbuts = 255) then mbuts := 0;
386 if (kmods = 255) then kmods := 0;
387 if (ev.kstate <> kmods) then exit;
389 result := (ev.bstate = mbuts) and (ev.but = but);
390 end;
393 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
394 begin
395 result := (ev = s);
396 end;
399 // ////////////////////////////////////////////////////////////////////////// //
400 procedure resetKMState (sendEvents: Boolean=true);
401 var
402 mask: Word;
403 mev: THMouseEvent;
404 kev: THKeyEvent;
405 begin
406 // generate mouse release events
407 if (curButState <> 0) then
408 begin
409 if sendEvents then
410 begin
411 mask := 1;
412 while (mask <> 0) do
413 begin
414 // checked each time, 'cause `evMouseCB` can be changed from the handler
415 if ((curButState and mask) <> 0) and assigned(evMouseCB) then
416 begin
417 mev.kind := mev.TKind.Release;
418 mev.x := curMsX;
419 mev.y := curMsY;
420 mev.dx := 0;
421 mev.dy := 0;
422 mev.but := mask;
423 mev.bstate := curButState;
424 mev.kstate := curModState;
425 curButState := curButState and (not mask);
426 evMouseCB(mev);
427 end;
428 mask := mask shl 1;
429 end;
430 end;
431 curButState := 0;
432 end;
434 // generate modifier release events
435 if (curModState <> 0) then
436 begin
437 if sendEvents then
438 begin
439 mask := 1;
440 while (mask <= 8) do
441 begin
442 // checked each time, 'cause `evMouseCB` can be changed from the handler
443 if ((curModState and mask) <> 0) and assigned(evKeyCB) then
444 begin
445 kev.kind := kev.TKind.Release;
446 case mask of
447 THKeyEvent.ModCtrl: begin kev.scan := SDL_SCANCODE_LCTRL; kev.sym := SDLK_LCTRL;{arbitrary} end;
448 THKeyEvent.ModAlt: begin kev.scan := SDL_SCANCODE_LALT; kev.sym := SDLK_LALT;{arbitrary} end;
449 THKeyEvent.ModShift: begin kev.scan := SDL_SCANCODE_LSHIFT; kev.sym := SDLK_LSHIFT;{arbitrary} end;
450 THKeyEvent.ModHyper: begin kev.scan := SDL_SCANCODE_LGUI; kev.sym := SDLK_LGUI;{arbitrary} end;
451 else assert(false);
452 end;
453 kev.x := curMsX;
454 kev.y := curMsY;
455 mev.bstate := 0{curMsButState}; // anyway
456 mev.kstate := curModState;
457 curModState := curModState and (not mask);
458 evKeyCB(kev);
459 end;
460 mask := mask shl 1;
461 end;
462 end;
463 curModState := 0;
464 end;
465 end;
468 function onSDLEvent (var ev: TSDL_Event): Boolean;
469 var
470 mev: THMouseEvent;
471 kev: THKeyEvent;
473 function buildBut (b: Byte): Word;
474 begin
475 result := 0;
476 case b of
477 SDL_BUTTON_LEFT: result := result or THMouseEvent.Left;
478 SDL_BUTTON_MIDDLE: result := result or THMouseEvent.Middle;
479 SDL_BUTTON_RIGHT: result := result or THMouseEvent.Right;
480 end;
481 end;
483 begin
484 result := false;
486 case ev.type_ of
487 SDL_KEYDOWN, SDL_KEYUP:
488 begin
489 // fix left/right modifiers
490 if (ev.type_ = SDL_KEYDOWN) then kev.kind := THKeyEvent.TKind.Press else kev.kind := THKeyEvent.TKind.Release;
491 kev.scan := ev.key.keysym.scancode;
492 kev.sym := ev.key.keysym.sym;
494 if (kev.scan = SDL_SCANCODE_RCTRL) then kev.scan := SDL_SCANCODE_LCTRL;
495 if (kev.scan = SDL_SCANCODE_RALT) then kev.scan := SDL_SCANCODE_LALT;
496 if (kev.scan = SDL_SCANCODE_RSHIFT) then kev.scan := SDL_SCANCODE_LSHIFT;
497 if (kev.scan = SDL_SCANCODE_RGUI) then kev.scan := SDL_SCANCODE_LGUI;
499 if (kev.sym = SDLK_RCTRL) then kev.sym := SDLK_LCTRL;
500 if (kev.sym = SDLK_RALT) then kev.sym := SDLK_LALT;
501 if (kev.sym = SDLK_RSHIFT) then kev.sym := SDLK_LSHIFT;
502 if (kev.sym = SDLK_RGUI) then kev.sym := SDLK_LGUI;
504 kev.x := curMsX;
505 kev.y := curMsY;
506 kev.bstate := curButState;
507 kev.kstate := curModState;
509 case kev.scan of
510 SDL_SCANCODE_LCTRL: if (kev.press) then curModState := curModState or THKeyEvent.ModCtrl else curModState := curModState and (not THKeyEvent.ModCtrl);
511 SDL_SCANCODE_LALT: if (kev.press) then curModState := curModState or THKeyEvent.ModAlt else curModState := curModState and (not THKeyEvent.ModAlt);
512 SDL_SCANCODE_LSHIFT: if (kev.press) then curModState := curModState or THKeyEvent.ModShift else curModState := curModState and (not THKeyEvent.ModShift);
513 end;
515 if assigned(evKeyCB) then result := evKeyCB(kev);
516 end;
518 SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP:
519 begin
520 mev.dx := ev.button.x-curMsX;
521 mev.dy := ev.button.y-curMsY;
522 curMsX := ev.button.x;
523 curMsY := ev.button.y;
524 if (ev.type_ = SDL_MOUSEBUTTONDOWN) then mev.kind := THMouseEvent.TKind.Press else mev.kind := THMouseEvent.TKind.Release;
525 mev.but := buildBut(ev.button.button);
526 mev.x := curMsX;
527 mev.y := curMsY;
528 mev.bstate := curButState;
529 mev.kstate := curModState;
530 if (mev.but <> 0) then
531 begin
532 // ev.button.clicks: Byte
533 if (ev.type_ = SDL_MOUSEBUTTONDOWN) then curButState := curButState or mev.but else curButState := curButState and (not mev.but);
534 if assigned(evMouseCB) then result := evMouseCB(mev);
535 end;
536 end;
537 SDL_MOUSEWHEEL:
538 begin
539 if (ev.wheel.y <> 0) then
540 begin
541 mev.dx := 0;
542 mev.dy := ev.wheel.y;
543 mev.kind := THMouseEvent.TKind.Press;
544 if (ev.wheel.y < 0) then mev.but := THMouseEvent.WheelUp else mev.but := THMouseEvent.WheelDown;
545 mev.x := curMsX;
546 mev.y := curMsY;
547 mev.bstate := curButState;
548 mev.kstate := curModState;
549 if assigned(evMouseCB) then result := evMouseCB(mev);
550 end;
551 end;
552 SDL_MOUSEMOTION:
553 begin
554 mev.dx := ev.button.x-curMsX;
555 mev.dy := ev.button.y-curMsY;
556 curMsX := ev.button.x;
557 curMsY := ev.button.y;
558 mev.kind := THMouseEvent.TKind.Motion;
559 mev.but := 0;
560 mev.x := curMsX;
561 mev.y := curMsY;
562 mev.bstate := curButState;
563 mev.kstate := curModState;
564 if assigned(evMouseCB) then result := evMouseCB(mev);
565 end;
568 SDL_TEXTINPUT:
569 begin
570 Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
571 keychr := Word(uc);
572 if (keychr > 127) then keychr := Word(wchar2win(WideChar(keychr)));
573 CharPress(AnsiChar(keychr));
574 end;
576 end;
577 end;
580 // ////////////////////////////////////////////////////////////////////////// //
581 procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
582 begin
583 glViewport(0, 0, winWidth, winHeight);
585 glDisable(GL_BLEND);
586 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
587 glDisable(GL_LINE_SMOOTH);
588 glDisable(GL_POINT_SMOOTH);
589 glDisable(GL_DEPTH_TEST);
590 glDisable(GL_TEXTURE_2D);
591 glDisable(GL_LIGHTING);
592 glDisable(GL_DITHER);
593 glDisable(GL_STENCIL_TEST);
594 glDisable(GL_SCISSOR_TEST);
595 glDisable(GL_CULL_FACE);
597 glMatrixMode(GL_PROJECTION);
598 glLoadIdentity();
599 if (upsideDown) then
600 begin
601 glOrtho(0, winWidth, 0, winHeight, -1, 1); // set origin to bottom left
602 end
603 else
604 begin
605 glOrtho(0, winWidth, winHeight, 0, -1, 1); // set origin to top left
606 end;
608 glMatrixMode(GL_MODELVIEW);
609 glLoadIdentity();
611 glClearColor(0, 0, 0, 0);
612 glColor4f(1, 1, 1, 1);
613 end;
616 // ////////////////////////////////////////////////////////////////////////// //
617 // cursor (hi, Death Track!)
618 const curTexWidth = 32;
619 const curTexHeight = 32;
620 const curWidth = 17;
621 const curHeight = 23;
623 const cursorImg: array[0..curWidth*curHeight-1] of Byte = (
624 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
625 3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
626 3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
627 3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
628 3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
629 3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,
630 3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,
631 3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,0,0,
632 3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,0,0,
633 3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,0,0,
634 3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,0,0,
635 3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,0,0,
636 3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,0,0,
637 3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,0,0,
638 3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,0,0,
639 3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,0,0,
640 3,3,5,6,3,3,0,0,0,0,0,0,0,0,0,0,0,
641 3,3,6,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
642 3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
643 3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
644 3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
645 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
646 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
647 );
648 const cursorPal: array[0..9*4-1] of Byte = (
649 0, 0, 0, 0,
650 0, 0, 0,163,
651 85,255,255,255,
652 85, 85,255,255,
653 255, 85, 85,255,
654 170, 0,170,255,
655 85, 85, 85,255,
656 0, 0, 0,255,
657 0, 0,170,255
658 );
661 var
662 curtexid: GLuint = 0;
664 procedure createCursorTexture ();
665 var
666 tex, tpp: PByte;
667 c: Integer;
668 x, y: Integer;
669 begin
670 if (curtexid <> 0) then exit; //begin glDeleteTextures(1, @curtexid); curtexid := 0; end;
672 GetMem(tex, curTexWidth*curTexHeight*4);
673 try
674 FillChar(tex^, curTexWidth*curTexHeight*4, 0);
676 // draw shadow
677 for y := 0 to curHeight-1 do
678 begin
679 for x := 0 to curWidth-1 do
680 begin
681 if (cursorImg[y*curWidth+x] <> 0) then
682 begin
683 c := 1*4;
684 tpp := tex+((y+1)*(curTexWidth*4)+(x+3)*4);
685 tpp^ := cursorPal[c+0]; Inc(tpp);
686 tpp^ := cursorPal[c+1]; Inc(tpp);
687 tpp^ := cursorPal[c+2]; Inc(tpp);
688 tpp^ := cursorPal[c+3]; Inc(tpp);
689 tpp^ := cursorPal[c+0]; Inc(tpp);
690 tpp^ := cursorPal[c+1]; Inc(tpp);
691 tpp^ := cursorPal[c+2]; Inc(tpp);
692 tpp^ := cursorPal[c+3]; Inc(tpp);
693 end;
694 end;
695 end;
697 // draw cursor
698 for y := 0 to curHeight-1 do
699 begin
700 for x := 0 to curWidth-1 do
701 begin
702 c := cursorImg[y*curWidth+x]*4;
703 if (c <> 0) then
704 begin
705 tpp := tex+(y*(curTexWidth*4)+x*4);
706 tpp^ := cursorPal[c+0]; Inc(tpp);
707 tpp^ := cursorPal[c+1]; Inc(tpp);
708 tpp^ := cursorPal[c+2]; Inc(tpp);
709 tpp^ := cursorPal[c+3]; Inc(tpp);
710 end;
711 end;
712 end;
714 glGenTextures(1, @curtexid);
715 if (curtexid = 0) then raise Exception.Create('can''t create cursor texture');
717 glBindTexture(GL_TEXTURE_2D, curtexid);
718 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
719 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
720 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
721 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
723 //GLfloat[4] bclr = 0.0;
724 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
726 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, curTexWidth, curTexHeight, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
727 glFlush();
728 finally
729 FreeMem(tex);
730 end;
731 end;
733 procedure oglDrawCursorAt (msX, msY: Integer);
734 begin
735 //if (curtexid = 0) then createCursorTexture() else glBindTexture(GL_TEXTURE_2D, curtexid);
736 glBindTexture(GL_TEXTURE_2D, curtexid);
737 // blend it
738 glEnable(GL_BLEND);
739 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
740 glEnable(GL_TEXTURE_2D);
741 glDisable(GL_STENCIL_TEST);
742 glDisable(GL_SCISSOR_TEST);
743 glDisable(GL_LIGHTING);
744 glDisable(GL_DEPTH_TEST);
745 glDisable(GL_CULL_FACE);
746 // color and opacity
747 glColor4f(1, 1, 1, 0.9);
748 //Dec(msX, 2);
749 glBegin(GL_QUADS);
750 glTexCoord2f(0.0, 0.0); glVertex2i(msX, msY); // top-left
751 glTexCoord2f(1.0, 0.0); glVertex2i(msX+curTexWidth, msY); // top-right
752 glTexCoord2f(1.0, 1.0); glVertex2i(msX+curTexWidth, msY+curTexHeight); // bottom-right
753 glTexCoord2f(0.0, 1.0); glVertex2i(msX, msY+curTexHeight); // bottom-left
754 glEnd();
755 //Inc(msX, 2);
756 glDisable(GL_BLEND);
757 glDisable(GL_TEXTURE_2D);
758 glColor4f(1, 1, 1, 1);
759 glBindTexture(GL_TEXTURE_2D, 0);
760 end;
762 procedure oglDrawCursor (); begin oglDrawCursorAt(curMsX, curMsY); end;
765 // ////////////////////////////////////////////////////////////////////////// //
766 // fonts
767 const kgiFont6: array[0..256*8-1] of Byte = (
768 $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,
769 $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,
770 $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,
771 $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,
772 $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,
773 $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,
774 $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,
775 $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,
776 $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,
777 $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,
778 $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,
779 $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,
780 $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,
781 $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,
782 $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,
783 $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,
784 $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,
785 $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,
786 $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,
787 $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,
788 $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,
789 $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,
790 $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,
791 $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,
792 $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,
793 $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,
794 $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,
795 $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,
796 $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,
797 $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,
798 $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,
799 $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,
800 $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,
801 $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,
802 $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,
803 $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,
804 $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,
805 $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,
806 $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,
807 $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,
808 $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,
809 $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,
810 $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,
811 $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,
812 $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,
813 $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,
814 $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,
815 $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,
816 $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,
817 $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,
818 $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,
819 $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,
820 $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,
821 $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,
822 $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,
823 $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,
824 $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,
825 $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,
826 $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,
827 $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,
828 $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,
829 $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,
830 $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,
831 $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,
832 $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,
833 $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,
834 $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,
835 $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,
836 $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,
837 $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,
838 $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,
839 $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,
840 $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,
841 $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,
842 $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,
843 $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,
844 $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,
845 $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,
846 $a8,$f8,$08,$00,$88,$88,$88,$88,$78,$08,$08,$00,$c0,$40,$40,$70,$48,$48,$70,$00
847 );
849 const kgiFont8: array[0..256*8-1] of Byte = (
850 $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,
851 $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,
852 $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,
853 $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,
854 $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,
855 $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,
856 $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,
857 $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,
858 $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,
859 $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,
860 $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,
861 $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,
862 $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,
863 $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,
864 $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,
865 $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,
866 $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,
867 $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,
868 $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,
869 $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,
870 $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,
871 $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,
872 $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,
873 $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,
874 $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,
875 $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,
876 $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,
877 $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,
878 $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,
879 $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,
880 $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,
881 $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,
882 $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,
883 $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,
884 $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,
885 $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,
886 $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,
887 $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,
888 $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,
889 $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,
890 $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,
891 $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,
892 $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,
893 $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,
894 $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,
895 $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,
896 $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,
897 $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,
898 $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,
899 $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,
900 $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,
901 $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,
902 $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,
903 $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,
904 $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,
905 $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,
906 $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,
907 $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,
908 $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,
909 $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,
910 $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,
911 $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,
912 $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,
913 $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,
914 $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,
915 $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,
916 $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,
917 $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,
918 $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,
919 $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,
920 $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,
921 $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,
922 $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,
923 $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,
924 $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,
925 $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,
926 $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,
927 $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,
928 $7c,$00,$00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
929 );
931 const kgiFont6PropWidth: array[0..256-1] of Byte = (
932 $08,$08,$08,$07,$07,$07,$07,$04,$08,$07,$08,$08,$06,$06,$06,$07,
933 $06,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
934 $85,$21,$13,$05,$05,$05,$05,$13,$13,$13,$05,$05,$12,$14,$12,$05,
935 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$21,$12,$05,$05,$05,$05,
936 $05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$05,$05,$05,$05,$05,
937 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$13,$05,$05,
938 $13,$05,$05,$05,$05,$05,$05,$05,$05,$13,$04,$14,$13,$05,$05,$05,
939 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$14,$21,$04,$05,$08,
940 $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$04,
941 $44,$08,$08,$08,$08,$08,$08,$08,$05,$04,$05,$08,$08,$08,$08,$08,
942 $05,$05,$05,$05,$05,$05,$13,$13,$05,$05,$05,$04,$05,$05,$05,$05,
943 $05,$05,$05,$05,$05,$03,$04,$04,$06,$05,$04,$07,$04,$03,$05,$08,
944 $05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$04,$05,$05,$05,$05,
945 $14,$05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$05,$05,$14,$05,
946 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,
947 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05
948 );
950 const kgiFont8PropWidth: array[0..256-1] of Byte = (
951 $08,$08,$08,$07,$07,$07,$07,$06,$08,$07,$08,$08,$07,$08,$08,$08,
952 $07,$07,$07,$07,$08,$08,$07,$08,$07,$07,$07,$07,$07,$08,$08,$08,
953 $85,$14,$15,$07,$06,$07,$07,$03,$14,$14,$08,$06,$13,$06,$22,$07,
954 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$22,$13,$05,$06,$15,$06,
955 $07,$06,$07,$07,$07,$07,$07,$07,$06,$14,$07,$07,$07,$07,$07,$07,
956 $07,$06,$07,$06,$06,$06,$06,$07,$07,$06,$07,$14,$07,$14,$07,$08,
957 $23,$07,$07,$06,$07,$06,$06,$07,$07,$14,$05,$07,$14,$07,$06,$06,
958 $07,$07,$06,$06,$15,$07,$06,$07,$07,$06,$06,$06,$32,$06,$07,$07,
959 $06,$07,$06,$08,$07,$07,$07,$07,$08,$06,$06,$06,$07,$05,$06,$06,
960 $06,$08,$07,$06,$06,$06,$07,$07,$06,$07,$06,$07,$07,$06,$07,$08,
961 $07,$05,$06,$07,$06,$06,$16,$16,$06,$06,$06,$08,$08,$06,$08,$08,
962 $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
963 $38,$08,$08,$38,$08,$08,$38,$28,$28,$28,$08,$08,$28,$08,$08,$08,
964 $08,$08,$08,$28,$38,$38,$28,$08,$08,$08,$38,$08,$08,$08,$48,$08,
965 $07,$06,$07,$07,$07,$07,$07,$07,$06,$07,$07,$06,$08,$08,$06,$06,
966 $06,$06,$06,$06,$35,$05,$06,$07,$15,$32,$32,$08,$15,$15,$24,$08
967 );
970 function createFontTexture (constref font: array of Byte; constref fontwdt: array of Byte; prop: Boolean): GLuint;
971 const
972 Width = 16*8;
973 Height = 16*8;
974 var
975 tex, tpp: PByte;
976 b: Byte;
977 cc: Integer;
978 x, y, dx, dy: Integer;
979 begin
980 GetMem(tex, Width*Height*4);
982 for cc := 0 to 255 do
983 begin
984 x := (cc mod 16)*8;
985 y := (cc div 16)*8;
986 for dy := 0 to 7 do
987 begin
988 b := font[cc*8+dy];
989 if prop then b := b shl (fontwdt[cc] shr 4);
990 tpp := tex+((y+dy)*(Width*4))+x*4;
991 for dx := 0 to 7 do
992 begin
993 if ((b and $80) <> 0) then
994 begin
995 tpp^ := 255; Inc(tpp);
996 tpp^ := 255; Inc(tpp);
997 tpp^ := 255; Inc(tpp);
998 tpp^ := 255; Inc(tpp);
999 end
1000 else
1001 begin
1002 tpp^ := 0; Inc(tpp);
1003 tpp^ := 0; Inc(tpp);
1004 tpp^ := 0; Inc(tpp);
1005 tpp^ := 0; Inc(tpp);
1006 end;
1007 b := (b and $7f) shl 1;
1008 end;
1009 end;
1010 end;
1012 glGenTextures(1, @result);
1013 if (result = 0) then raise Exception.Create('can''t create Holmes font texture');
1015 glBindTexture(GL_TEXTURE_2D, result);
1016 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1017 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1018 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1019 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1021 //GLfloat[4] bclr = 0.0;
1022 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
1024 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
1025 glFlush();
1027 //FreeMem(tex);
1028 end;
1031 var
1032 font6texid: GLuint = 0;
1033 font8texid: GLuint = 0;
1034 prfont6texid: GLuint = 0;
1035 prfont8texid: GLuint = 0;
1038 procedure deleteFonts ();
1039 begin
1040 if (font6texid <> 0) then glDeleteTextures(1, @font6texid);
1041 if (font8texid <> 0) then glDeleteTextures(1, @font8texid);
1042 if (prfont6texid <> 0) then glDeleteTextures(1, @prfont6texid);
1043 if (prfont8texid <> 0) then glDeleteTextures(1, @prfont8texid);
1044 font6texid := 0;
1045 font8texid := 0;
1046 prfont6texid := 0;
1047 prfont8texid := 0;
1048 end;
1051 procedure createFonts ();
1052 begin
1053 if (font6texid = 0) then font6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, false);
1054 if (font8texid = 0) then font8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, false);
1055 if (prfont6texid = 0) then prfont6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, true);
1056 if (prfont8texid = 0) then prfont8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, true);
1057 end;
1060 // ////////////////////////////////////////////////////////////////////////// //
1061 procedure TScissorSave.save (enableScissoring: Boolean);
1062 begin
1063 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
1064 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
1065 //conwritefln('(%d,%d)-(%d,%d)', [scxywh[0], scxywh[1], scxywh[2], scxywh[3]]);
1066 if enableScissoring then glEnable(GL_SCISSOR_TEST);
1067 end;
1069 procedure TScissorSave.restore ();
1070 begin
1071 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
1072 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
1073 end;
1075 procedure TScissorSave.setRect (x, y, w, h: Integer);
1076 begin
1077 if (w < 1) or (h < 1) then begin glScissor(0, 0, 0, 0); exit; end;
1078 y := gScrHeight-(y+h);
1079 if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then glScissor(0, 0, 0, 0) else glScissor(x, y, w, h);
1080 end;
1082 //TODO: overflow checks
1083 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean;
1084 var
1085 ex0, ey0: Integer;
1086 begin
1087 result := false;
1088 if (w0 < 1) or (h0 < 1) or (w1 < 1) or (h1 < 1) then exit;
1089 // check for intersection
1090 if (x0+w0 <= x1) or (y0+h0 <= y1) or (x1+w1 <= x0) or (y1+h1 <= y0) then exit;
1091 if (x0 >= x1+w1) or (y0 >= y1+h1) or (x1 >= x0+h0) or (y1 >= y0+h0) then exit;
1092 // ok, intersects
1093 ex0 := x0+w0;
1094 ey0 := y0+h0;
1095 if (x0 < x1) then x0 := x1;
1096 if (y0 < y1) then y0 := y1;
1097 if (ex0 > x1+w1) then ex0 := x1+w1;
1098 if (ey0 > y1+h1) then ey0 := y1+h1;
1099 w0 := ex0-x0;
1100 h0 := ey0-y0;
1101 result := (w0 > 0) and (h0 > 0);
1102 end;
1105 // ////////////////////////////////////////////////////////////////////////// //
1106 procedure normRGBA (var r, g, b, a: Integer); inline;
1107 begin
1108 if (a < 0) then a := 0 else if (a > 255) then a := 255;
1109 if (r < 0) then r := 0 else if (r > 255) then r := 255;
1110 if (g < 0) then g := 0 else if (g > 255) then g := 255;
1111 if (b < 0) then b := 0 else if (b > 255) then b := 255;
1112 end;
1114 // returns `false` if the color is transparent
1115 function setupGLColor (r, g, b, a: Integer): Boolean;
1116 begin
1117 normRGBA(r, g, b, a);
1118 if (a < 255) then
1119 begin
1120 if (a = 0) then begin result := false; exit; end;
1121 glEnable(GL_BLEND);
1122 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1123 end
1124 else
1125 begin
1126 glDisable(GL_BLEND);
1127 end;
1128 glColor4ub(Byte(r), Byte(g), Byte(b), Byte(a));
1129 result := true;
1130 end;
1132 function isScaled (): Boolean;
1133 var
1134 mt: packed array [0..15] of Double;
1135 begin
1136 glGetDoublev(GL_MODELVIEW_MATRIX, @mt[0]);
1137 result := (mt[0] <> 1.0) or (mt[1*4+1] <> 1.0);
1138 end;
1141 // ////////////////////////////////////////////////////////////////////////// //
1142 function textWidth6 (const s: AnsiString): Integer;
1143 var
1144 f: Integer;
1145 begin
1146 result := 0;
1147 for f := 1 to Length(s) do Inc(result, Integer(kgiFont6PropWidth[Integer(s[f])] and $0f)+1);
1148 if (result > 0) then Dec(result); // don't count last empty pixel
1149 end;
1152 function textWidth8 (const s: AnsiString): Integer;
1153 var
1154 f: Integer;
1155 begin
1156 result := 0;
1157 for f := 1 to Length(s) do Inc(result, Integer(kgiFont8PropWidth[Integer(s[f])] and $0f)+1);
1158 if (result > 0) then Dec(result); // don't count last empty pixel
1159 end;
1162 // return width (including last empty pixel)
1163 function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
1164 var
1165 f, c: Integer;
1166 tx, ty: Integer;
1167 begin
1168 result := 0;
1169 if (Length(s) = 0) then exit;
1170 if not setupGLColor(r, g, b, a) then exit;
1172 glEnable(GL_ALPHA_TEST);
1173 glAlphaFunc(GL_NOTEQUAL, 0.0);
1174 glEnable(GL_TEXTURE_2D);
1175 glBindTexture(GL_TEXTURE_2D, tid);
1177 for f := 1 to Length(s) do
1178 begin
1179 c := Integer(s[f]) and $ff;
1180 tx := (c mod 16)*8;
1181 ty := (c div 16)*8;
1182 glBegin(GL_QUADS);
1183 glTexCoord2f((tx+0)/128.0, (ty+0)/128.0); glVertex2i(x+0, y+0); // top-left
1184 glTexCoord2f((tx+8)/128.0, (ty+0)/128.0); glVertex2i(x+8, y+0); // top-right
1185 glTexCoord2f((tx+8)/128.0, (ty+8)/128.0); glVertex2i(x+8, y+8); // bottom-right
1186 glTexCoord2f((tx+0)/128.0, (ty+8)/128.0); glVertex2i(x+0, y+8); // bottom-left
1187 glEnd();
1188 if prop then
1189 begin
1190 x += Integer(fontwdt[c] and $0f)+1;
1191 result += Integer(fontwdt[c] and $0f)+1;
1192 end
1193 else
1194 begin
1195 x += wdt;
1196 result += wdt;
1197 end;
1198 end;
1200 glDisable(GL_ALPHA_TEST);
1201 glDisable(GL_BLEND);
1202 glDisable(GL_TEXTURE_2D);
1203 glColor4f(1, 1, 1, 1);
1204 glBindTexture(GL_TEXTURE_2D, 0);
1205 end;
1208 // ////////////////////////////////////////////////////////////////////////// //
1209 procedure drawHLine (x, y, len: Integer; r, g, b: Integer; a: Integer=255);
1210 begin
1211 if (len < 1) then exit;
1212 if not setupGLColor(r, g, b, a) then exit;
1213 glDisable(GL_TEXTURE_2D);
1214 if (not isScaled) then
1215 begin
1216 glBegin(GL_LINES);
1217 glVertex2f(x+0.375, y+0.375);
1218 glVertex2f(x+len+0.375, y+0.375);
1219 glEnd();
1220 end
1221 else
1222 begin
1223 glBegin(GL_QUADS);
1224 glVertex2i(x, y);
1225 glVertex2i(x+len, y);
1226 glVertex2i(x+len, y+1);
1227 glVertex2i(x, y+1);
1228 glEnd();
1229 end;
1230 end;
1233 procedure drawVLine (x, y, len: Integer; r, g, b: Integer; a: Integer=255);
1234 begin
1235 if (len < 1) then exit;
1236 if not setupGLColor(r, g, b, a) then exit;
1237 glDisable(GL_TEXTURE_2D);
1238 if (not isScaled) then
1239 begin
1240 glBegin(GL_LINES);
1241 glVertex2f(x+0.375, y+0.375);
1242 glVertex2f(x+0.375, y+len+0.375);
1243 glEnd();
1244 end
1245 else
1246 begin
1247 glBegin(GL_QUADS);
1248 glVertex2i(x, y);
1249 glVertex2i(x, y+len);
1250 glVertex2i(x+1, y+len);
1251 glVertex2i(x+1, y);
1252 glEnd();
1253 end;
1254 end;
1257 procedure drawLine (x1, y1, x2, y2: Integer; r, g, b: Integer; a: Integer=255);
1258 begin
1259 if not setupGLColor(r, g, b, a) then exit;
1261 glDisable(GL_TEXTURE_2D);
1263 glLineWidth(1);
1264 glPointSize(1);
1266 if (not isScaled) then
1267 begin
1268 glBegin(GL_LINES);
1269 glVertex2f(x1+0.375, y1+0.375);
1270 glVertex2f(x2+0.375, y2+0.375);
1271 glEnd();
1273 if (x1 <> x2) or (y1 <> y2) then
1274 begin
1275 glBegin(GL_POINTS);
1276 glVertex2f(x2+0.375, y2+0.375);
1277 glEnd();
1278 end;
1279 end
1280 else
1281 begin
1282 glBegin(GL_LINES);
1283 glVertex2i(x1, y1);
1284 glVertex2i(x2, y2);
1285 // draw last point
1286 glVertex2i(x2, y2);
1287 glVertex2i(x2+1, y2+1);
1288 glEnd();
1289 end;
1291 glColor4f(1, 1, 1, 1);
1292 glDisable(GL_BLEND);
1293 end;
1296 procedure drawRect (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
1297 begin
1298 if (w < 0) or (h < 0) then exit;
1299 if not setupGLColor(r, g, b, a) then exit;
1300 glDisable(GL_TEXTURE_2D);
1301 glLineWidth(1);
1302 glDisable(GL_LINE_SMOOTH);
1303 glDisable(GL_POLYGON_SMOOTH);
1304 if (w = 1) and (h = 1) then
1305 begin
1306 glBegin(GL_POINTS);
1307 glVertex2f(x+0.375, y+0.375);
1308 glEnd();
1309 end
1310 else
1311 begin
1312 glBegin(GL_LINES);
1313 glVertex2i(x, y); glVertex2i(x+w, y); // top
1314 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1315 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1316 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1317 glEnd();
1318 end;
1319 //glRect(x, y, x+w, y+h);
1320 glColor4f(1, 1, 1, 1);
1321 glDisable(GL_BLEND);
1322 end;
1325 procedure drawRectUI (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
1326 procedure hline (x, y, len: Integer);
1327 begin
1328 if (len < 1) then exit;
1329 glBegin(GL_QUADS);
1330 glVertex2i(x, y);
1331 glVertex2i(x+len, y);
1332 glVertex2i(x+len, y+1);
1333 glVertex2i(x, y+1);
1334 glEnd();
1335 end;
1337 procedure vline (x, y, len: Integer);
1338 begin
1339 if (len < 1) then exit;
1340 glBegin(GL_QUADS);
1341 glVertex2i(x, y);
1342 glVertex2i(x, y+len);
1343 glVertex2i(x+1, y+len);
1344 glVertex2i(x+1, y);
1345 glEnd();
1346 end;
1348 var
1349 scaled: Boolean;
1350 begin
1351 if (w < 0) or (h < 0) then exit;
1352 if not setupGLColor(r, g, b, a) then exit;
1353 glDisable(GL_TEXTURE_2D);
1354 glLineWidth(1);
1355 glDisable(GL_LINE_SMOOTH);
1356 glDisable(GL_POLYGON_SMOOTH);
1357 scaled := isScaled();
1358 if (w = 1) and (h = 1) then
1359 begin
1360 glBegin(GL_POINTS);
1361 if scaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
1362 glEnd();
1363 end
1364 else
1365 begin
1366 if not scaled then
1367 begin
1368 glBegin(GL_LINES);
1369 glVertex2i(x, y); glVertex2i(x+w, y); // top
1370 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1371 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1372 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1373 glEnd();
1374 end
1375 else
1376 begin
1377 hline(x, y, w);
1378 hline(x, y+h-1, w);
1379 vline(x, y+1, h-2);
1380 vline(x+w-1, y+1, h-2);
1381 end;
1382 end;
1383 //glRect(x, y, x+w, y+h);
1384 glColor4f(1, 1, 1, 1);
1385 glDisable(GL_BLEND);
1386 end;
1389 procedure darkenRect (x, y, w, h: Integer; a: Integer);
1390 begin
1391 if (w < 0) or (h < 0) then exit;
1392 if (a < 0) then a := 0;
1393 if (a >= 255) then exit;
1394 glEnable(GL_BLEND);
1395 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
1396 glDisable(GL_LINE_SMOOTH);
1397 glDisable(GL_POLYGON_SMOOTH);
1398 glDisable(GL_TEXTURE_2D);
1399 glColor4f(0.0, 0.0, 0.0, a/255.0);
1400 glBegin(GL_QUADS);
1401 glVertex2i(x, y);
1402 glVertex2i(x+w, y);
1403 glVertex2i(x+w, y+h);
1404 glVertex2i(x, y+h);
1405 glEnd();
1406 //glRect(x, y, x+w, y+h);
1407 glColor4f(1, 1, 1, 1);
1408 glDisable(GL_BLEND);
1409 //glBlendEquation(GL_FUNC_ADD);
1410 end;
1413 procedure fillRect (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
1414 begin
1415 if (w < 0) or (h < 0) then exit;
1416 if not setupGLColor(r, g, b, a) then exit;
1417 glDisable(GL_LINE_SMOOTH);
1418 glDisable(GL_POLYGON_SMOOTH);
1419 glDisable(GL_TEXTURE_2D);
1420 glBegin(GL_QUADS);
1421 glVertex2f(x, y);
1422 glVertex2f(x+w, y);
1423 glVertex2f(x+w, y+h);
1424 glVertex2f(x, y+h);
1425 glEnd();
1426 glColor4f(1, 1, 1, 1);
1427 glDisable(GL_BLEND);
1428 end;
1431 // ////////////////////////////////////////////////////////////////////////// //
1432 function drawText6 (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1433 begin
1434 if (font6texid = 0) then createFonts();
1435 drawTextInternal(6, x, y, s, r, g, b, a, font6texid, kgiFont6PropWidth, false);
1436 result := Length(s)*6;
1437 end;
1439 function drawText8 (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1440 begin
1441 if (font8texid = 0) then createFonts();
1442 drawTextInternal(8, x, y, s, r, g, b, a, font8texid, kgiFont8PropWidth, false);
1443 result := Length(s)*8;
1444 end;
1446 function drawText6Prop (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1447 begin
1448 if (prfont6texid = 0) then createFonts();
1449 result := drawTextInternal(6, x, y, s, r, g, b, a, prfont6texid, kgiFont6PropWidth, true);
1450 end;
1452 function drawText8Prop (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1453 begin
1454 if (prfont8texid = 0) then createFonts();
1455 result := drawTextInternal(8, x, y, s, r, g, b, a, prfont8texid, kgiFont8PropWidth, true);
1456 end;
1459 // ////////////////////////////////////////////////////////////////////////// //
1460 // x-centered at `x`
1461 function drawText6XC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1462 begin
1463 if (font6texid = 0) then createFonts();
1464 x -= Length(s)*6 div 2;
1465 drawTextInternal(6, x, y, s, r, g, b, a, font6texid, kgiFont6PropWidth, false);
1466 result := Length(s)*6;
1467 end;
1469 function drawText8XC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1470 begin
1471 if (font8texid = 0) then createFonts();
1472 x -= Length(s)*8 div 2;
1473 drawTextInternal(8, x, y, s, r, g, b, a, font8texid, kgiFont8PropWidth, false);
1474 result := Length(s)*8;
1475 end;
1477 function drawText6PropXC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1478 begin
1479 if (prfont6texid = 0) then createFonts();
1480 x -= textWidth6(s) div 2;
1481 result := drawTextInternal(6, x, y, s, r, g, b, a, prfont6texid, kgiFont6PropWidth, true);
1482 end;
1484 function drawText8PropXC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1485 begin
1486 if (prfont8texid = 0) then createFonts();
1487 x -= textWidth8(s) div 2;
1488 result := drawTextInternal(8, x, y, s, r, g, b, a, prfont8texid, kgiFont8PropWidth, true);
1489 end;
1492 // ////////////////////////////////////////////////////////////////////////// //
1493 procedure oglRestoreMode (doClear: Boolean);
1494 begin
1495 oglSetup2D(gScrWidth, gScrHeight);
1496 glScissor(0, 0, gScrWidth, gScrHeight);
1498 glBindTexture(GL_TEXTURE_2D, 0);
1499 glDisable(GL_BLEND);
1500 glDisable(GL_TEXTURE_2D);
1501 glDisable(GL_STENCIL_TEST);
1502 glDisable(GL_SCISSOR_TEST);
1503 glDisable(GL_LIGHTING);
1504 glDisable(GL_DEPTH_TEST);
1505 glDisable(GL_CULL_FACE);
1506 glDisable(GL_LINE_SMOOTH);
1507 glDisable(GL_POINT_SMOOTH);
1508 glLineWidth(1);
1509 glPointSize(1);
1510 glColor4f(1, 1, 1, 1);
1512 if doClear then
1513 begin
1514 glClearColor(0, 0, 0, 0);
1515 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_ACCUM_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
1516 end;
1518 // scale everything
1519 glMatrixMode(GL_MODELVIEW);
1520 glLoadIdentity();
1521 //glScalef(4, 4, 1);
1522 end;
1525 procedure onWinFocus (); begin end;
1527 procedure onWinBlur (); begin resetKMState(true); end;
1529 procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
1531 procedure onPostRender (); begin oglRestoreMode(false); oglDrawCursor(); end;
1533 procedure onInit ();
1534 begin
1535 oglSetup2D(gScrWidth, gScrHeight);
1537 createCursorTexture();
1538 createFonts();
1539 end;
1541 procedure onDeinit ();
1542 begin
1543 resetKMState(false);
1544 if (curtexid <> 0) then glDeleteTextures(1, @curtexid);
1545 curtexid := 0;
1546 deleteFonts();
1547 curButState := 0;
1548 curModState := 0;
1549 curMsX := 0;
1550 curMsY := 0;
1551 end;
1554 // ////////////////////////////////////////////////////////////////////////// //
1555 begin
1556 evSDLCB := onSDLEvent;
1557 winFocusCB := onWinFocus;
1558 winBlurCB := onWinBlur;
1559 prerenderFrameCB := onPreRender;
1560 postrenderFrameCB := onPostRender;
1561 oglInitCB := onInit;
1562 oglDeinitCB := onDeinit;
1563 end.