DEADSOFTWARE

Holmes: gxlib API change
[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 combineRect (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 setupGLColor (constref clr: TGxRGBA): Boolean;
136 function isScaled (): Boolean;
138 function textWidth6 (const s: AnsiString): Integer;
139 function textWidth8 (const s: AnsiString): Integer;
140 // return width (including last empty pixel)
141 function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; constref clr: TGxRGBA; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
142 procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
143 procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
144 procedure drawHLine (x, y, len: Integer; constref clr: TGxRGBA);
145 procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
146 procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
147 procedure darkenRect (x, y, w, h: Integer; a: Integer);
148 procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
149 function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
150 function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
151 function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
152 function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
153 // x-centered at `x`
154 function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
155 function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
156 function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
157 function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
160 // ////////////////////////////////////////////////////////////////////////// //
161 // event handlers
162 var
163 evMouseCB: function (var ev: THMouseEvent): Boolean = nil; // `true`: event eaten
164 evKeyCB: function (var ev: THKeyEvent): Boolean = nil; // `true`: event eaten
167 // ////////////////////////////////////////////////////////////////////////// //
168 function getMouseX (): Integer; inline;
169 function getMouseY (): Integer; inline;
170 function getButState (): Word; inline;
171 function getModState (): Word; inline;
174 // ////////////////////////////////////////////////////////////////////////// //
175 property
176 gMouseX: Integer read getMouseX;
177 gMouseY: Integer read getMouseY;
178 gButState: Word read getButState;
179 gModState: Word read getModState;
181 var
182 gGfxDoClear: Boolean = true;
185 // ////////////////////////////////////////////////////////////////////////// //
186 // any mods = 255: nothing was defined
187 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
189 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
190 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
192 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
193 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
196 implementation
199 var
200 curButState: Word = 0;
201 curModState: Word = 0;
202 curMsX: Integer = 0;
203 curMsY: Integer = 0;
206 // ////////////////////////////////////////////////////////////////////////// //
207 function strEquCI (const s0, s1: AnsiString): Boolean;
208 var
209 f: Integer;
210 c0, c1: AnsiChar;
211 begin
212 result := (Length(s0) = Length(s1));
213 if result then
214 begin
215 for f := 1 to Length(s0) do
216 begin
217 c0 := s0[f];
218 if (c0 >= 'a') and (c0 <= 'z') then Dec(c0, 32); // poor man's `toupper()`
219 c1 := s1[f];
220 if (c1 >= 'a') and (c1 <= 'z') then Dec(c1, 32); // poor man's `toupper()`
221 if (c0 <> c1) then begin result := false; exit; end;
222 end;
223 end;
224 end;
227 // ////////////////////////////////////////////////////////////////////////// //
228 function getMouseX (): Integer; inline; begin result := curMsX; end;
229 function getMouseY (): Integer; inline; begin result := curMsY; end;
230 function getButState (): Word; inline; begin result := curButState; end;
231 function getModState (): Word; inline; begin result := curModState; end;
234 // ////////////////////////////////////////////////////////////////////////// //
235 function THMouseEvent.press (): Boolean; inline; begin result := (kind = TKind.Press); end;
236 function THMouseEvent.release (): Boolean; inline; begin result := (kind = TKind.Release); end;
237 function THMouseEvent.motion (): Boolean; inline; begin result := (kind = TKind.Motion); end;
239 function THKeyEvent.press (): Boolean; inline; begin result := (kind = TKind.Press); end;
240 function THKeyEvent.release (): Boolean; inline; begin result := (kind = TKind.Release); end;
243 // ////////////////////////////////////////////////////////////////////////// //
244 constructor TGxRGBA.Create (ar, ag, ab: Integer; aa: Integer=255);
245 begin
246 if (ar < 0) then r := 0 else if (ar > 255) then r := 255 else r := Byte(ar);
247 if (ag < 0) then g := 0 else if (ag > 255) then g := 255 else g := Byte(ag);
248 if (ab < 0) then b := 0 else if (ab > 255) then b := 255 else b := Byte(ab);
249 if (aa < 0) then a := 0 else if (aa > 255) then a := 255 else a := Byte(aa);
250 end;
252 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;
254 function TGxRGBA.isOpaque (): Boolean; inline; begin result := (a = 255); end;
255 function TGxRGBA.isTransparent (): Boolean; inline; begin result := (a = 0); end;
257 function TGxRGBA.blend (ar, ag, ab, aa: Integer): TGxRGBA; inline;
258 var
259 me, it, a_tmp_, dc_tmp_, srb_tmp_, sg_tmp_, drb_tmp_, dg_tmp_, orb_tmp_, og_tmp_: LongWord;
260 begin
261 if (aa <= 0) then begin result := self; exit; end;
262 result := TGxRGBA.Create(ar, ag, ab, aa);
263 if (aa >= 255) then begin result.a := a; exit; end;
264 me := asUInt;
265 it := result.asUInt;
266 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
267 dc_tmp_ := me and $ffffff;
268 srb_tmp_ := (it and $ff00ff);
269 sg_tmp_ := (it and $00ff00);
270 drb_tmp_ := (dc_tmp_ and $ff00ff);
271 dg_tmp_ := (dc_tmp_ and $00ff00);
272 orb_tmp_ := (drb_tmp_+(((srb_tmp_-drb_tmp_)*a_tmp_+$800080) shr 8)) and $ff00ff;
273 og_tmp_ := (dg_tmp_+(((sg_tmp_-dg_tmp_)*a_tmp_+$008000) shr 8)) and $00ff00;
274 me := (orb_tmp_ or og_tmp_); // or $ff000000; /* and $ffffff;*/
275 result.r := Byte(me and $ff);
276 result.g := Byte((me shr 8) and $ff);
277 result.b := Byte((me shr 16) and $ff);
278 result.a := a;
279 end;
282 // ////////////////////////////////////////////////////////////////////////// //
283 // any mods = 255: nothing was defined
284 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
285 var
286 pos, epos: Integer;
287 begin
288 kmods := 255;
289 mbuts := 255;
290 pos := 1;
291 //while (pos <= Length(s)) and (s[pos] <= ' ') do Inc(pos);
292 if (pos < Length(s)) and ((s[pos] = '+') or (s[pos] = '-') or (s[pos] = '*')) then Inc(pos);
293 while (pos < Length(s)) do
294 begin
295 if (Length(s)-pos >= 2) and (s[pos+1] = '-') then
296 begin
297 case s[pos] of
298 'C', 'c': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModCtrl; Inc(pos, 2); continue; end;
299 'M', 'm': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModAlt; Inc(pos, 2); continue; end;
300 'S', 's': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModShift; Inc(pos, 2); continue; end;
301 end;
302 break;
303 end;
304 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
305 begin
306 case s[pos] of
307 'L', 'l': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Left; Inc(pos, 4); continue; end;
308 'R', 'r': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Right; Inc(pos, 4); continue; end;
309 'M', 'm': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Middle; Inc(pos, 4); continue; end;
310 end;
311 break;
312 end;
313 break;
314 end;
315 epos := Length(s)+1;
316 while (epos > pos) and (s[epos-1] <= ' ') do Dec(epos);
317 if (epos > pos) then result := Copy(s, pos, epos-pos) else result := '';
318 end;
321 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
322 var
323 f: Integer;
324 kmods: Byte = 255;
325 mbuts: Byte = 255;
326 kname: AnsiString;
327 begin
328 result := false;
329 if (Length(s) > 0) then
330 begin
331 if (s[1] = '+') then begin if (not ev.press) then exit; end
332 else if (s[1] = '-') then begin if (not ev.release) then exit; end
333 else if (s[1] = '*') then begin end
334 else if (not ev.press) then exit;
335 end;
336 kname := parseModKeys(s, kmods, mbuts);
337 if (kmods = 255) then kmods := 0;
338 if (ev.kstate <> kmods) then exit;
339 if (mbuts <> 255) and (ev.bstate <> mbuts) then exit;
341 if (strEquCI(kname, 'Enter')) then kname := 'RETURN';
343 for f := 0 to SDL_NUM_SCANCODES-1 do
344 begin
345 if strEquCI(kname, SDL_GetScancodeName(f)) then
346 begin
347 result := (ev.scan = f);
348 exit;
349 end;
350 end;
351 end;
354 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
355 begin
356 result := (ev = s);
357 end;
360 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
361 var
362 kmods: Byte = 255;
363 mbuts: Byte = 255;
364 kname: AnsiString;
365 but: Integer = -1;
366 begin
367 result := false;
369 if (Length(s) > 0) then
370 begin
371 if (s[1] = '+') then begin if (not ev.press) then exit; end
372 else if (s[1] = '-') then begin if (not ev.release) then exit; end
373 else if (s[1] = '*') then begin if (not ev.motion) then exit; end
374 else if (not ev.press) then exit;
375 end;
377 kname := parseModKeys(s, kmods, mbuts);
378 if strEquCI(kname, 'LMB') then but := THMouseEvent.Left
379 else if strEquCI(kname, 'RMB') then but := THMouseEvent.Right
380 else if strEquCI(kname, 'MMB') then but := THMouseEvent.Middle
381 else if strEquCI(kname, 'WheelUp') or strEquCI(kname, 'WUP') then but := THMouseEvent.WheelUp
382 else if strEquCI(kname, 'WheelDown') or strEquCI(kname, 'WDN') or strEquCI(kname, 'WDOWN') then but := THMouseEvent.WheelDown
383 else if strEquCI(kname, 'None') then but := 0
384 else exit;
386 if (mbuts = 255) then mbuts := 0;
387 if (kmods = 255) then kmods := 0;
388 if (ev.kstate <> kmods) then exit;
390 result := (ev.bstate = mbuts) and (ev.but = but);
391 end;
394 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
395 begin
396 result := (ev = s);
397 end;
400 // ////////////////////////////////////////////////////////////////////////// //
401 procedure resetKMState (sendEvents: Boolean=true);
402 var
403 mask: Word;
404 mev: THMouseEvent;
405 kev: THKeyEvent;
406 begin
407 // generate mouse release events
408 if (curButState <> 0) then
409 begin
410 if sendEvents then
411 begin
412 mask := 1;
413 while (mask <> 0) do
414 begin
415 // checked each time, 'cause `evMouseCB` can be changed from the handler
416 if ((curButState and mask) <> 0) and assigned(evMouseCB) then
417 begin
418 mev.kind := mev.TKind.Release;
419 mev.x := curMsX;
420 mev.y := curMsY;
421 mev.dx := 0;
422 mev.dy := 0;
423 mev.but := mask;
424 mev.bstate := curButState;
425 mev.kstate := curModState;
426 curButState := curButState and (not mask);
427 evMouseCB(mev);
428 end;
429 mask := mask shl 1;
430 end;
431 end;
432 curButState := 0;
433 end;
435 // generate modifier release events
436 if (curModState <> 0) then
437 begin
438 if sendEvents then
439 begin
440 mask := 1;
441 while (mask <= 8) do
442 begin
443 // checked each time, 'cause `evMouseCB` can be changed from the handler
444 if ((curModState and mask) <> 0) and assigned(evKeyCB) then
445 begin
446 kev.kind := kev.TKind.Release;
447 case mask of
448 THKeyEvent.ModCtrl: begin kev.scan := SDL_SCANCODE_LCTRL; kev.sym := SDLK_LCTRL;{arbitrary} end;
449 THKeyEvent.ModAlt: begin kev.scan := SDL_SCANCODE_LALT; kev.sym := SDLK_LALT;{arbitrary} end;
450 THKeyEvent.ModShift: begin kev.scan := SDL_SCANCODE_LSHIFT; kev.sym := SDLK_LSHIFT;{arbitrary} end;
451 THKeyEvent.ModHyper: begin kev.scan := SDL_SCANCODE_LGUI; kev.sym := SDLK_LGUI;{arbitrary} end;
452 else assert(false);
453 end;
454 kev.x := curMsX;
455 kev.y := curMsY;
456 mev.bstate := 0{curMsButState}; // anyway
457 mev.kstate := curModState;
458 curModState := curModState and (not mask);
459 evKeyCB(kev);
460 end;
461 mask := mask shl 1;
462 end;
463 end;
464 curModState := 0;
465 end;
466 end;
469 function onSDLEvent (var ev: TSDL_Event): Boolean;
470 var
471 mev: THMouseEvent;
472 kev: THKeyEvent;
474 function buildBut (b: Byte): Word;
475 begin
476 result := 0;
477 case b of
478 SDL_BUTTON_LEFT: result := result or THMouseEvent.Left;
479 SDL_BUTTON_MIDDLE: result := result or THMouseEvent.Middle;
480 SDL_BUTTON_RIGHT: result := result or THMouseEvent.Right;
481 end;
482 end;
484 begin
485 result := false;
487 case ev.type_ of
488 SDL_KEYDOWN, SDL_KEYUP:
489 begin
490 // fix left/right modifiers
491 if (ev.type_ = SDL_KEYDOWN) then kev.kind := THKeyEvent.TKind.Press else kev.kind := THKeyEvent.TKind.Release;
492 kev.scan := ev.key.keysym.scancode;
493 kev.sym := ev.key.keysym.sym;
495 if (kev.scan = SDL_SCANCODE_RCTRL) then kev.scan := SDL_SCANCODE_LCTRL;
496 if (kev.scan = SDL_SCANCODE_RALT) then kev.scan := SDL_SCANCODE_LALT;
497 if (kev.scan = SDL_SCANCODE_RSHIFT) then kev.scan := SDL_SCANCODE_LSHIFT;
498 if (kev.scan = SDL_SCANCODE_RGUI) then kev.scan := SDL_SCANCODE_LGUI;
500 if (kev.sym = SDLK_RCTRL) then kev.sym := SDLK_LCTRL;
501 if (kev.sym = SDLK_RALT) then kev.sym := SDLK_LALT;
502 if (kev.sym = SDLK_RSHIFT) then kev.sym := SDLK_LSHIFT;
503 if (kev.sym = SDLK_RGUI) then kev.sym := SDLK_LGUI;
505 kev.x := curMsX;
506 kev.y := curMsY;
507 kev.bstate := curButState;
508 kev.kstate := curModState;
510 case kev.scan of
511 SDL_SCANCODE_LCTRL: if (kev.press) then curModState := curModState or THKeyEvent.ModCtrl else curModState := curModState and (not THKeyEvent.ModCtrl);
512 SDL_SCANCODE_LALT: if (kev.press) then curModState := curModState or THKeyEvent.ModAlt else curModState := curModState and (not THKeyEvent.ModAlt);
513 SDL_SCANCODE_LSHIFT: if (kev.press) then curModState := curModState or THKeyEvent.ModShift else curModState := curModState and (not THKeyEvent.ModShift);
514 end;
516 if assigned(evKeyCB) then result := evKeyCB(kev);
517 end;
519 SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP:
520 begin
521 mev.dx := ev.button.x-curMsX;
522 mev.dy := ev.button.y-curMsY;
523 curMsX := ev.button.x;
524 curMsY := ev.button.y;
525 if (ev.type_ = SDL_MOUSEBUTTONDOWN) then mev.kind := THMouseEvent.TKind.Press else mev.kind := THMouseEvent.TKind.Release;
526 mev.but := buildBut(ev.button.button);
527 mev.x := curMsX;
528 mev.y := curMsY;
529 mev.bstate := curButState;
530 mev.kstate := curModState;
531 if (mev.but <> 0) then
532 begin
533 // ev.button.clicks: Byte
534 if (ev.type_ = SDL_MOUSEBUTTONDOWN) then curButState := curButState or mev.but else curButState := curButState and (not mev.but);
535 if assigned(evMouseCB) then result := evMouseCB(mev);
536 end;
537 end;
538 SDL_MOUSEWHEEL:
539 begin
540 if (ev.wheel.y <> 0) then
541 begin
542 mev.dx := 0;
543 mev.dy := ev.wheel.y;
544 mev.kind := THMouseEvent.TKind.Press;
545 if (ev.wheel.y < 0) then mev.but := THMouseEvent.WheelUp else mev.but := THMouseEvent.WheelDown;
546 mev.x := curMsX;
547 mev.y := curMsY;
548 mev.bstate := curButState;
549 mev.kstate := curModState;
550 if assigned(evMouseCB) then result := evMouseCB(mev);
551 end;
552 end;
553 SDL_MOUSEMOTION:
554 begin
555 mev.dx := ev.button.x-curMsX;
556 mev.dy := ev.button.y-curMsY;
557 curMsX := ev.button.x;
558 curMsY := ev.button.y;
559 mev.kind := THMouseEvent.TKind.Motion;
560 mev.but := 0;
561 mev.x := curMsX;
562 mev.y := curMsY;
563 mev.bstate := curButState;
564 mev.kstate := curModState;
565 if assigned(evMouseCB) then result := evMouseCB(mev);
566 end;
569 SDL_TEXTINPUT:
570 begin
571 Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
572 keychr := Word(uc);
573 if (keychr > 127) then keychr := Word(wchar2win(WideChar(keychr)));
574 CharPress(AnsiChar(keychr));
575 end;
577 end;
578 end;
581 // ////////////////////////////////////////////////////////////////////////// //
582 procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
583 begin
584 glViewport(0, 0, winWidth, winHeight);
586 glDisable(GL_BLEND);
587 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
588 glDisable(GL_LINE_SMOOTH);
589 glDisable(GL_POINT_SMOOTH);
590 glDisable(GL_DEPTH_TEST);
591 glDisable(GL_TEXTURE_2D);
592 glDisable(GL_LIGHTING);
593 glDisable(GL_DITHER);
594 glDisable(GL_STENCIL_TEST);
595 glDisable(GL_SCISSOR_TEST);
596 glDisable(GL_CULL_FACE);
598 glMatrixMode(GL_PROJECTION);
599 glLoadIdentity();
600 if (upsideDown) then
601 begin
602 glOrtho(0, winWidth, 0, winHeight, -1, 1); // set origin to bottom left
603 end
604 else
605 begin
606 glOrtho(0, winWidth, winHeight, 0, -1, 1); // set origin to top left
607 end;
609 glMatrixMode(GL_MODELVIEW);
610 glLoadIdentity();
612 glClearColor(0, 0, 0, 0);
613 glColor4f(1, 1, 1, 1);
614 end;
617 // ////////////////////////////////////////////////////////////////////////// //
618 // cursor (hi, Death Track!)
619 const curTexWidth = 32;
620 const curTexHeight = 32;
621 const curWidth = 17;
622 const curHeight = 23;
624 const cursorImg: array[0..curWidth*curHeight-1] of Byte = (
625 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
626 3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
627 3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
628 3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
629 3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
630 3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,
631 3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,
632 3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,0,0,
633 3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,0,0,
634 3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,0,0,
635 3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,0,0,
636 3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,0,0,
637 3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,0,0,
638 3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,0,0,
639 3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,0,0,
640 3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,0,0,
641 3,3,5,6,3,3,0,0,0,0,0,0,0,0,0,0,0,
642 3,3,6,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
643 3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
644 3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
645 3,3,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 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
648 );
649 const cursorPal: array[0..9*4-1] of Byte = (
650 0, 0, 0, 0,
651 0, 0, 0,163,
652 85,255,255,255,
653 85, 85,255,255,
654 255, 85, 85,255,
655 170, 0,170,255,
656 85, 85, 85,255,
657 0, 0, 0,255,
658 0, 0,170,255
659 );
662 var
663 curtexid: GLuint = 0;
665 procedure createCursorTexture ();
666 var
667 tex, tpp: PByte;
668 c: Integer;
669 x, y: Integer;
670 begin
671 if (curtexid <> 0) then exit; //begin glDeleteTextures(1, @curtexid); curtexid := 0; end;
673 GetMem(tex, curTexWidth*curTexHeight*4);
674 try
675 FillChar(tex^, curTexWidth*curTexHeight*4, 0);
677 // draw shadow
678 for y := 0 to curHeight-1 do
679 begin
680 for x := 0 to curWidth-1 do
681 begin
682 if (cursorImg[y*curWidth+x] <> 0) then
683 begin
684 c := 1*4;
685 tpp := tex+((y+1)*(curTexWidth*4)+(x+3)*4);
686 tpp^ := cursorPal[c+0]; Inc(tpp);
687 tpp^ := cursorPal[c+1]; Inc(tpp);
688 tpp^ := cursorPal[c+2]; Inc(tpp);
689 tpp^ := cursorPal[c+3]; Inc(tpp);
690 tpp^ := cursorPal[c+0]; Inc(tpp);
691 tpp^ := cursorPal[c+1]; Inc(tpp);
692 tpp^ := cursorPal[c+2]; Inc(tpp);
693 tpp^ := cursorPal[c+3]; Inc(tpp);
694 end;
695 end;
696 end;
698 // draw cursor
699 for y := 0 to curHeight-1 do
700 begin
701 for x := 0 to curWidth-1 do
702 begin
703 c := cursorImg[y*curWidth+x]*4;
704 if (c <> 0) then
705 begin
706 tpp := tex+(y*(curTexWidth*4)+x*4);
707 tpp^ := cursorPal[c+0]; Inc(tpp);
708 tpp^ := cursorPal[c+1]; Inc(tpp);
709 tpp^ := cursorPal[c+2]; Inc(tpp);
710 tpp^ := cursorPal[c+3]; Inc(tpp);
711 end;
712 end;
713 end;
715 glGenTextures(1, @curtexid);
716 if (curtexid = 0) then raise Exception.Create('can''t create cursor texture');
718 glBindTexture(GL_TEXTURE_2D, curtexid);
719 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
720 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
721 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
722 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
724 //GLfloat[4] bclr = 0.0;
725 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
727 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, curTexWidth, curTexHeight, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
728 glFlush();
729 finally
730 FreeMem(tex);
731 end;
732 end;
734 procedure oglDrawCursorAt (msX, msY: Integer);
735 begin
736 //if (curtexid = 0) then createCursorTexture() else glBindTexture(GL_TEXTURE_2D, curtexid);
737 glBindTexture(GL_TEXTURE_2D, curtexid);
738 // blend it
739 glEnable(GL_BLEND);
740 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
741 glEnable(GL_TEXTURE_2D);
742 glDisable(GL_STENCIL_TEST);
743 glDisable(GL_SCISSOR_TEST);
744 glDisable(GL_LIGHTING);
745 glDisable(GL_DEPTH_TEST);
746 glDisable(GL_CULL_FACE);
747 // color and opacity
748 glColor4f(1, 1, 1, 0.9);
749 //Dec(msX, 2);
750 glBegin(GL_QUADS);
751 glTexCoord2f(0.0, 0.0); glVertex2i(msX, msY); // top-left
752 glTexCoord2f(1.0, 0.0); glVertex2i(msX+curTexWidth, msY); // top-right
753 glTexCoord2f(1.0, 1.0); glVertex2i(msX+curTexWidth, msY+curTexHeight); // bottom-right
754 glTexCoord2f(0.0, 1.0); glVertex2i(msX, msY+curTexHeight); // bottom-left
755 glEnd();
756 //Inc(msX, 2);
757 glDisable(GL_BLEND);
758 glDisable(GL_TEXTURE_2D);
759 glColor4f(1, 1, 1, 1);
760 glBindTexture(GL_TEXTURE_2D, 0);
761 end;
763 procedure oglDrawCursor (); begin oglDrawCursorAt(curMsX, curMsY); end;
766 // ////////////////////////////////////////////////////////////////////////// //
767 // fonts
768 const kgiFont6: array[0..256*8-1] of Byte = (
769 $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,
770 $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,
771 $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,
772 $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,
773 $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,
774 $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,
775 $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,
776 $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,
777 $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,
778 $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,
779 $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,
780 $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,
781 $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,
782 $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,
783 $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,
784 $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,
785 $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,
786 $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,
787 $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,
788 $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,
789 $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,
790 $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,
791 $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,
792 $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,
793 $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,
794 $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,
795 $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,
796 $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,
797 $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,
798 $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,
799 $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,
800 $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,
801 $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,
802 $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,
803 $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,
804 $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,
805 $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,
806 $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,
807 $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,
808 $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,
809 $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,
810 $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,
811 $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,
812 $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,
813 $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,
814 $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,
815 $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,
816 $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,
817 $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,
818 $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,
819 $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,
820 $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,
821 $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,
822 $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,
823 $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,
824 $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,
825 $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,
826 $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,
827 $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,
828 $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,
829 $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,
830 $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,
831 $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,
832 $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,
833 $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,
834 $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,
835 $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,
836 $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,
837 $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,
838 $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,
839 $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,
840 $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,
841 $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,
842 $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,
843 $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,
844 $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,
845 $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,
846 $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,
847 $a8,$f8,$08,$00,$88,$88,$88,$88,$78,$08,$08,$00,$c0,$40,$40,$70,$48,$48,$70,$00
848 );
850 const kgiFont8: array[0..256*8-1] of Byte = (
851 $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,
852 $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,
853 $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,
854 $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,
855 $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,
856 $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,
857 $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,
858 $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,
859 $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,
860 $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,
861 $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,
862 $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,
863 $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,
864 $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,
865 $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,
866 $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,
867 $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,
868 $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,
869 $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,
870 $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,
871 $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,
872 $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,
873 $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,
874 $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,
875 $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,
876 $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,
877 $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,
878 $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,
879 $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,
880 $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,
881 $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,
882 $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,
883 $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,
884 $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,
885 $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,
886 $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,
887 $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,
888 $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,
889 $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,
890 $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,
891 $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,
892 $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,
893 $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,
894 $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,
895 $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,
896 $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,
897 $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,
898 $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,
899 $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,
900 $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,
901 $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,
902 $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,
903 $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,
904 $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,
905 $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,
906 $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,
907 $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,
908 $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,
909 $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,
910 $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,
911 $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,
912 $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,
913 $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,
914 $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,
915 $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,
916 $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,
917 $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,
918 $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,
919 $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,
920 $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,
921 $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,
922 $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,
923 $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,
924 $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,
925 $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,
926 $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,
927 $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,
928 $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,
929 $7c,$00,$00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
930 );
932 const kgiFont6PropWidth: array[0..256-1] of Byte = (
933 $08,$08,$08,$07,$07,$07,$07,$04,$08,$07,$08,$08,$06,$06,$06,$07,
934 $06,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
935 $85,$21,$13,$05,$05,$05,$05,$13,$13,$13,$05,$05,$12,$14,$12,$05,
936 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$21,$12,$05,$05,$05,$05,
937 $05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$05,$05,$05,$05,$05,
938 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$13,$05,$05,
939 $13,$05,$05,$05,$05,$05,$05,$05,$05,$13,$04,$14,$13,$05,$05,$05,
940 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$14,$21,$04,$05,$08,
941 $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$04,
942 $44,$08,$08,$08,$08,$08,$08,$08,$05,$04,$05,$08,$08,$08,$08,$08,
943 $05,$05,$05,$05,$05,$05,$13,$13,$05,$05,$05,$04,$05,$05,$05,$05,
944 $05,$05,$05,$05,$05,$03,$04,$04,$06,$05,$04,$07,$04,$03,$05,$08,
945 $05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$04,$05,$05,$05,$05,
946 $14,$05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$05,$05,$14,$05,
947 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,
948 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05
949 );
951 const kgiFont8PropWidth: array[0..256-1] of Byte = (
952 $08,$08,$08,$07,$07,$07,$07,$06,$08,$07,$08,$08,$07,$08,$08,$08,
953 $07,$07,$07,$07,$08,$08,$07,$08,$07,$07,$07,$07,$07,$08,$08,$08,
954 $85,$14,$15,$07,$06,$07,$07,$03,$14,$14,$08,$06,$13,$06,$22,$07,
955 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$22,$13,$05,$06,$15,$06,
956 $07,$06,$07,$07,$07,$07,$07,$07,$06,$14,$07,$07,$07,$07,$07,$07,
957 $07,$06,$07,$06,$06,$06,$06,$07,$07,$06,$07,$14,$07,$14,$07,$08,
958 $23,$07,$07,$06,$07,$06,$06,$07,$07,$14,$05,$07,$14,$07,$06,$06,
959 $07,$07,$06,$06,$15,$07,$06,$07,$07,$06,$06,$06,$32,$06,$07,$07,
960 $06,$07,$06,$08,$07,$07,$07,$07,$08,$06,$06,$06,$07,$05,$06,$06,
961 $06,$08,$07,$06,$06,$06,$07,$07,$06,$07,$06,$07,$07,$06,$07,$08,
962 $07,$05,$06,$07,$06,$06,$16,$16,$06,$06,$06,$08,$08,$06,$08,$08,
963 $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
964 $38,$08,$08,$38,$08,$08,$38,$28,$28,$28,$08,$08,$28,$08,$08,$08,
965 $08,$08,$08,$28,$38,$38,$28,$08,$08,$08,$38,$08,$08,$08,$48,$08,
966 $07,$06,$07,$07,$07,$07,$07,$07,$06,$07,$07,$06,$08,$08,$06,$06,
967 $06,$06,$06,$06,$35,$05,$06,$07,$15,$32,$32,$08,$15,$15,$24,$08
968 );
971 function createFontTexture (constref font: array of Byte; constref fontwdt: array of Byte; prop: Boolean): GLuint;
972 const
973 Width = 16*8;
974 Height = 16*8;
975 var
976 tex, tpp: PByte;
977 b: Byte;
978 cc: Integer;
979 x, y, dx, dy: Integer;
980 begin
981 GetMem(tex, Width*Height*4);
983 for cc := 0 to 255 do
984 begin
985 x := (cc mod 16)*8;
986 y := (cc div 16)*8;
987 for dy := 0 to 7 do
988 begin
989 b := font[cc*8+dy];
990 if prop then b := b shl (fontwdt[cc] shr 4);
991 tpp := tex+((y+dy)*(Width*4))+x*4;
992 for dx := 0 to 7 do
993 begin
994 if ((b and $80) <> 0) then
995 begin
996 tpp^ := 255; Inc(tpp);
997 tpp^ := 255; Inc(tpp);
998 tpp^ := 255; Inc(tpp);
999 tpp^ := 255; Inc(tpp);
1000 end
1001 else
1002 begin
1003 tpp^ := 0; Inc(tpp);
1004 tpp^ := 0; Inc(tpp);
1005 tpp^ := 0; Inc(tpp);
1006 tpp^ := 0; Inc(tpp);
1007 end;
1008 b := (b and $7f) shl 1;
1009 end;
1010 end;
1011 end;
1013 glGenTextures(1, @result);
1014 if (result = 0) then raise Exception.Create('can''t create Holmes font texture');
1016 glBindTexture(GL_TEXTURE_2D, result);
1017 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1018 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1019 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1020 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1022 //GLfloat[4] bclr = 0.0;
1023 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
1025 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
1026 glFlush();
1028 //FreeMem(tex);
1029 end;
1032 var
1033 font6texid: GLuint = 0;
1034 font8texid: GLuint = 0;
1035 prfont6texid: GLuint = 0;
1036 prfont8texid: GLuint = 0;
1039 procedure deleteFonts ();
1040 begin
1041 if (font6texid <> 0) then glDeleteTextures(1, @font6texid);
1042 if (font8texid <> 0) then glDeleteTextures(1, @font8texid);
1043 if (prfont6texid <> 0) then glDeleteTextures(1, @prfont6texid);
1044 if (prfont8texid <> 0) then glDeleteTextures(1, @prfont8texid);
1045 font6texid := 0;
1046 font8texid := 0;
1047 prfont6texid := 0;
1048 prfont8texid := 0;
1049 end;
1052 procedure createFonts ();
1053 begin
1054 if (font6texid = 0) then font6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, false);
1055 if (font8texid = 0) then font8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, false);
1056 if (prfont6texid = 0) then prfont6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, true);
1057 if (prfont8texid = 0) then prfont8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, true);
1058 end;
1061 // ////////////////////////////////////////////////////////////////////////// //
1062 procedure TScissorSave.save (enableScissoring: Boolean);
1063 begin
1064 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
1065 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
1066 //conwritefln('(%d,%d)-(%d,%d)', [scxywh[0], scxywh[1], scxywh[2], scxywh[3]]);
1067 if enableScissoring and (not wassc) then glEnable(GL_SCISSOR_TEST);
1068 end;
1070 procedure TScissorSave.restore ();
1071 begin
1072 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
1073 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
1074 end;
1076 procedure TScissorSave.combineRect (x, y, w, h: Integer);
1077 //var ox, oy, ow, oh: Integer;
1078 begin
1079 if (w < 1) or (h < 1) then begin glScissor(0, 0, 0, 0); exit; end;
1080 y := gScrHeight-(y+h);
1081 //ox := x; oy := y; ow := w; oh := h;
1082 if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then
1083 begin
1084 //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, ')');
1085 //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, '>');
1086 glScissor(0, 0, 0, 0);
1087 end
1088 else
1089 begin
1090 glScissor(x, y, w, h);
1091 end;
1092 end;
1094 //TODO: overflow checks
1095 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean;
1096 var
1097 ex0, ey0: Integer;
1098 ex1, ey1: Integer;
1099 begin
1100 result := false;
1101 if (w0 < 1) or (h0 < 1) or (w1 < 1) or (h1 < 1) then exit; // at least one rect is null
1102 // check for intersection
1103 ex0 := x0+w0;
1104 ey0 := y0+h0;
1105 ex1 := x1+w1;
1106 ey1 := y1+h1;
1107 if (ex0 <= x1) or (ey0 <= y1) or (ex1 <= x0) or (ey1 <= y0) then exit;
1108 if (x0 >= ex1) or (y0 >= ey1) or (x1 >= ex0) or (y1 >= ey0) then exit;
1109 // ok, intersects
1110 if (x0 < x1) then x0 := x1;
1111 if (y0 < y1) then y0 := y1;
1112 if (ex0 > ex1) then ex0 := ex1;
1113 if (ey0 > ey1) then ey0 := ey1;
1114 w0 := ex0-x0;
1115 h0 := ey0-y0;
1116 result := (w0 > 0) and (h0 > 0);
1117 end;
1120 // ////////////////////////////////////////////////////////////////////////// //
1121 procedure normRGBA (var r, g, b, a: Integer); inline;
1122 begin
1123 if (a < 0) then a := 0 else if (a > 255) then a := 255;
1124 if (r < 0) then r := 0 else if (r > 255) then r := 255;
1125 if (g < 0) then g := 0 else if (g > 255) then g := 255;
1126 if (b < 0) then b := 0 else if (b > 255) then b := 255;
1127 end;
1129 // returns `false` if the color is transparent
1130 function setupGLColor (r, g, b, a: Integer): Boolean;
1131 begin
1132 normRGBA(r, g, b, a);
1133 if (a < 255) then
1134 begin
1135 if (a = 0) then begin result := false; exit; end;
1136 glEnable(GL_BLEND);
1137 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1138 end
1139 else
1140 begin
1141 glDisable(GL_BLEND);
1142 end;
1143 glColor4ub(Byte(r), Byte(g), Byte(b), Byte(a));
1144 result := true;
1145 end;
1147 // returns `false` if the color is transparent
1148 function setupGLColor (constref clr: TGxRGBA): Boolean;
1149 begin
1150 if (clr.a < 255) then
1151 begin
1152 if (clr.a = 0) then begin result := false; exit; end;
1153 glEnable(GL_BLEND);
1154 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1155 end
1156 else
1157 begin
1158 glDisable(GL_BLEND);
1159 end;
1160 glColor4ub(clr.r, clr.g, clr.b, clr.a);
1161 result := true;
1162 end;
1164 function isScaled (): Boolean;
1165 var
1166 mt: packed array [0..15] of Double;
1167 begin
1168 glGetDoublev(GL_MODELVIEW_MATRIX, @mt[0]);
1169 result := (mt[0] <> 1.0) or (mt[1*4+1] <> 1.0);
1170 end;
1173 // ////////////////////////////////////////////////////////////////////////// //
1174 function textWidth6 (const s: AnsiString): Integer;
1175 var
1176 f: Integer;
1177 begin
1178 result := 0;
1179 for f := 1 to Length(s) do Inc(result, Integer(kgiFont6PropWidth[Integer(s[f])] and $0f)+1);
1180 if (result > 0) then Dec(result); // don't count last empty pixel
1181 end;
1184 function textWidth8 (const s: AnsiString): Integer;
1185 var
1186 f: Integer;
1187 begin
1188 result := 0;
1189 for f := 1 to Length(s) do Inc(result, Integer(kgiFont8PropWidth[Integer(s[f])] and $0f)+1);
1190 if (result > 0) then Dec(result); // don't count last empty pixel
1191 end;
1194 // return width (including last empty pixel)
1195 function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; constref clr: TGxRGBA; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
1196 var
1197 f, c: Integer;
1198 tx, ty: Integer;
1199 begin
1200 result := 0;
1201 if (Length(s) = 0) then exit;
1202 if not setupGLColor(clr) then exit;
1204 glEnable(GL_ALPHA_TEST);
1205 glAlphaFunc(GL_NOTEQUAL, 0.0);
1206 glEnable(GL_TEXTURE_2D);
1207 glBindTexture(GL_TEXTURE_2D, tid);
1209 for f := 1 to Length(s) do
1210 begin
1211 c := Integer(s[f]) and $ff;
1212 tx := (c mod 16)*8;
1213 ty := (c div 16)*8;
1214 glBegin(GL_QUADS);
1215 glTexCoord2f((tx+0)/128.0, (ty+0)/128.0); glVertex2i(x+0, y+0); // top-left
1216 glTexCoord2f((tx+8)/128.0, (ty+0)/128.0); glVertex2i(x+8, y+0); // top-right
1217 glTexCoord2f((tx+8)/128.0, (ty+8)/128.0); glVertex2i(x+8, y+8); // bottom-right
1218 glTexCoord2f((tx+0)/128.0, (ty+8)/128.0); glVertex2i(x+0, y+8); // bottom-left
1219 glEnd();
1220 if prop then
1221 begin
1222 x += Integer(fontwdt[c] and $0f)+1;
1223 result += Integer(fontwdt[c] and $0f)+1;
1224 end
1225 else
1226 begin
1227 x += wdt;
1228 result += wdt;
1229 end;
1230 end;
1232 glDisable(GL_ALPHA_TEST);
1233 glDisable(GL_BLEND);
1234 glDisable(GL_TEXTURE_2D);
1235 glColor4f(1, 1, 1, 1);
1236 glBindTexture(GL_TEXTURE_2D, 0);
1237 end;
1240 // ////////////////////////////////////////////////////////////////////////// //
1241 procedure drawHLine (x, y, len: Integer; constref clr: TGxRGBA);
1242 begin
1243 if (len < 1) then exit;
1244 if not setupGLColor(clr) then exit;
1245 glDisable(GL_TEXTURE_2D);
1246 if (not isScaled) then
1247 begin
1248 glBegin(GL_LINES);
1249 glVertex2f(x+0.375, y+0.375);
1250 glVertex2f(x+len+0.375, y+0.375);
1251 glEnd();
1252 end
1253 else
1254 begin
1255 glBegin(GL_QUADS);
1256 glVertex2i(x, y);
1257 glVertex2i(x+len, y);
1258 glVertex2i(x+len, y+1);
1259 glVertex2i(x, y+1);
1260 glEnd();
1261 end;
1262 end;
1265 procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
1266 begin
1267 if (len < 1) then exit;
1268 if not setupGLColor(clr) then exit;
1269 glDisable(GL_TEXTURE_2D);
1270 if (not isScaled) then
1271 begin
1272 glBegin(GL_LINES);
1273 glVertex2f(x+0.375, y+0.375);
1274 glVertex2f(x+0.375, y+len+0.375);
1275 glEnd();
1276 end
1277 else
1278 begin
1279 glBegin(GL_QUADS);
1280 glVertex2i(x, y);
1281 glVertex2i(x, y+len);
1282 glVertex2i(x+1, y+len);
1283 glVertex2i(x+1, y);
1284 glEnd();
1285 end;
1286 end;
1289 procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
1290 begin
1291 if not setupGLColor(clr) then exit;
1293 glDisable(GL_TEXTURE_2D);
1295 glLineWidth(1);
1296 glPointSize(1);
1298 if (not isScaled) then
1299 begin
1300 glBegin(GL_LINES);
1301 glVertex2f(x1+0.375, y1+0.375);
1302 glVertex2f(x2+0.375, y2+0.375);
1303 glEnd();
1305 if (x1 <> x2) or (y1 <> y2) then
1306 begin
1307 glBegin(GL_POINTS);
1308 glVertex2f(x2+0.375, y2+0.375);
1309 glEnd();
1310 end;
1311 end
1312 else
1313 begin
1314 glBegin(GL_LINES);
1315 glVertex2i(x1, y1);
1316 glVertex2i(x2, y2);
1317 // draw last point
1318 glVertex2i(x2, y2);
1319 glVertex2i(x2+1, y2+1);
1320 glEnd();
1321 end;
1323 glColor4f(1, 1, 1, 1);
1324 glDisable(GL_BLEND);
1325 end;
1328 procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
1329 begin
1330 if (w < 0) or (h < 0) then exit;
1331 if not setupGLColor(clr) then exit;
1332 glDisable(GL_TEXTURE_2D);
1333 glLineWidth(1);
1334 glDisable(GL_LINE_SMOOTH);
1335 glDisable(GL_POLYGON_SMOOTH);
1336 if (w = 1) and (h = 1) then
1337 begin
1338 glBegin(GL_POINTS);
1339 glVertex2f(x+0.375, y+0.375);
1340 glEnd();
1341 end
1342 else
1343 begin
1344 glBegin(GL_LINES);
1345 glVertex2i(x, y); glVertex2i(x+w, y); // top
1346 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1347 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1348 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1349 glEnd();
1350 end;
1351 //glRect(x, y, x+w, y+h);
1352 glColor4f(1, 1, 1, 1);
1353 glDisable(GL_BLEND);
1354 end;
1357 procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
1358 procedure hline (x, y, len: Integer);
1359 begin
1360 if (len < 1) then exit;
1361 glBegin(GL_QUADS);
1362 glVertex2i(x, y);
1363 glVertex2i(x+len, y);
1364 glVertex2i(x+len, y+1);
1365 glVertex2i(x, y+1);
1366 glEnd();
1367 end;
1369 procedure vline (x, y, len: Integer);
1370 begin
1371 if (len < 1) then exit;
1372 glBegin(GL_QUADS);
1373 glVertex2i(x, y);
1374 glVertex2i(x, y+len);
1375 glVertex2i(x+1, y+len);
1376 glVertex2i(x+1, y);
1377 glEnd();
1378 end;
1380 var
1381 scaled: Boolean;
1382 begin
1383 if (w < 0) or (h < 0) then exit;
1384 if not setupGLColor(clr) then exit;
1385 glDisable(GL_TEXTURE_2D);
1386 glLineWidth(1);
1387 glDisable(GL_LINE_SMOOTH);
1388 glDisable(GL_POLYGON_SMOOTH);
1389 scaled := isScaled();
1390 if (w = 1) and (h = 1) then
1391 begin
1392 glBegin(GL_POINTS);
1393 if scaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
1394 glEnd();
1395 end
1396 else
1397 begin
1398 if not scaled then
1399 begin
1400 glBegin(GL_LINES);
1401 glVertex2i(x, y); glVertex2i(x+w, y); // top
1402 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1403 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1404 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1405 glEnd();
1406 end
1407 else
1408 begin
1409 hline(x, y, w);
1410 hline(x, y+h-1, w);
1411 vline(x, y+1, h-2);
1412 vline(x+w-1, y+1, h-2);
1413 end;
1414 end;
1415 //glRect(x, y, x+w, y+h);
1416 glColor4f(1, 1, 1, 1);
1417 glDisable(GL_BLEND);
1418 end;
1421 procedure darkenRect (x, y, w, h: Integer; a: Integer);
1422 begin
1423 if (w < 0) or (h < 0) then exit;
1424 if (a < 0) then a := 0;
1425 if (a >= 255) then exit;
1426 glEnable(GL_BLEND);
1427 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
1428 glDisable(GL_LINE_SMOOTH);
1429 glDisable(GL_POLYGON_SMOOTH);
1430 glDisable(GL_TEXTURE_2D);
1431 glColor4f(0.0, 0.0, 0.0, a/255.0);
1432 glBegin(GL_QUADS);
1433 glVertex2i(x, y);
1434 glVertex2i(x+w, y);
1435 glVertex2i(x+w, y+h);
1436 glVertex2i(x, y+h);
1437 glEnd();
1438 //glRect(x, y, x+w, y+h);
1439 glColor4f(1, 1, 1, 1);
1440 glDisable(GL_BLEND);
1441 //glBlendEquation(GL_FUNC_ADD);
1442 end;
1445 procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
1446 begin
1447 if (w < 0) or (h < 0) then exit;
1448 if not setupGLColor(clr) then exit;
1449 glDisable(GL_LINE_SMOOTH);
1450 glDisable(GL_POLYGON_SMOOTH);
1451 glDisable(GL_TEXTURE_2D);
1452 glBegin(GL_QUADS);
1453 glVertex2f(x, y);
1454 glVertex2f(x+w, y);
1455 glVertex2f(x+w, y+h);
1456 glVertex2f(x, y+h);
1457 glEnd();
1458 glColor4f(1, 1, 1, 1);
1459 glDisable(GL_BLEND);
1460 end;
1463 // ////////////////////////////////////////////////////////////////////////// //
1464 function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1465 begin
1466 if (font6texid = 0) then createFonts();
1467 drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
1468 result := Length(s)*6;
1469 end;
1471 function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1472 begin
1473 if (font8texid = 0) then createFonts();
1474 drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
1475 result := Length(s)*8;
1476 end;
1478 function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1479 begin
1480 if (prfont6texid = 0) then createFonts();
1481 result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
1482 end;
1484 function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1485 begin
1486 if (prfont8texid = 0) then createFonts();
1487 result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
1488 end;
1491 // ////////////////////////////////////////////////////////////////////////// //
1492 // x-centered at `x`
1493 function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1494 begin
1495 if (font6texid = 0) then createFonts();
1496 x -= Length(s)*6 div 2;
1497 drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
1498 result := Length(s)*6;
1499 end;
1501 function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1502 begin
1503 if (font8texid = 0) then createFonts();
1504 x -= Length(s)*8 div 2;
1505 drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
1506 result := Length(s)*8;
1507 end;
1509 function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1510 begin
1511 if (prfont6texid = 0) then createFonts();
1512 x -= textWidth6(s) div 2;
1513 result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
1514 end;
1516 function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1517 begin
1518 if (prfont8texid = 0) then createFonts();
1519 x -= textWidth8(s) div 2;
1520 result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
1521 end;
1524 // ////////////////////////////////////////////////////////////////////////// //
1525 procedure oglRestoreMode (doClear: Boolean);
1526 begin
1527 oglSetup2D(gScrWidth, gScrHeight);
1528 glScissor(0, 0, gScrWidth, gScrHeight);
1530 glBindTexture(GL_TEXTURE_2D, 0);
1531 glDisable(GL_BLEND);
1532 glDisable(GL_TEXTURE_2D);
1533 glDisable(GL_STENCIL_TEST);
1534 glDisable(GL_SCISSOR_TEST);
1535 glDisable(GL_LIGHTING);
1536 glDisable(GL_DEPTH_TEST);
1537 glDisable(GL_CULL_FACE);
1538 glDisable(GL_LINE_SMOOTH);
1539 glDisable(GL_POINT_SMOOTH);
1540 glLineWidth(1);
1541 glPointSize(1);
1542 glColor4f(1, 1, 1, 1);
1544 if doClear then
1545 begin
1546 glClearColor(0, 0, 0, 0);
1547 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_ACCUM_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
1548 end;
1550 // scale everything
1551 glMatrixMode(GL_MODELVIEW);
1552 glLoadIdentity();
1553 //glScalef(4, 4, 1);
1554 end;
1557 procedure onWinFocus (); begin end;
1559 procedure onWinBlur (); begin resetKMState(true); end;
1561 procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
1563 procedure onPostRender (); begin oglRestoreMode(false); oglDrawCursor(); end;
1565 procedure onInit ();
1566 begin
1567 oglSetup2D(gScrWidth, gScrHeight);
1569 createCursorTexture();
1570 createFonts();
1571 end;
1573 procedure onDeinit ();
1574 begin
1575 resetKMState(false);
1576 if (curtexid <> 0) then glDeleteTextures(1, @curtexid);
1577 curtexid := 0;
1578 deleteFonts();
1579 curButState := 0;
1580 curModState := 0;
1581 curMsX := 0;
1582 curMsY := 0;
1583 end;
1586 // ////////////////////////////////////////////////////////////////////////// //
1587 begin
1588 evSDLCB := onSDLEvent;
1589 winFocusCB := onWinFocus;
1590 winBlurCB := onWinBlur;
1591 prerenderFrameCB := onPreRender;
1592 postrenderFrameCB := onPostRender;
1593 oglInitCB := onInit;
1594 oglDeinitCB := onDeinit;
1595 end.