DEADSOFTWARE

5fa57e737cd43880e78b30966723f54301b9acbc
[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 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 and (not wassc) 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.combineRect (x, y, w, h: Integer);
1076 //var ox, oy, ow, oh: Integer;
1077 begin
1078 if (w < 1) or (h < 1) then begin glScissor(0, 0, 0, 0); exit; end;
1079 y := gScrHeight-(y+h);
1080 //ox := x; oy := y; ow := w; oh := h;
1081 if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then
1082 begin
1083 //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, ')');
1084 //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, '>');
1085 glScissor(0, 0, 0, 0);
1086 end
1087 else
1088 begin
1089 glScissor(x, y, w, h);
1090 end;
1091 end;
1093 //TODO: overflow checks
1094 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean;
1095 var
1096 ex0, ey0: Integer;
1097 ex1, ey1: Integer;
1098 begin
1099 result := false;
1100 if (w0 < 1) or (h0 < 1) or (w1 < 1) or (h1 < 1) then exit; // at least one rect is null
1101 // check for intersection
1102 ex0 := x0+w0;
1103 ey0 := y0+h0;
1104 ex1 := x1+w1;
1105 ey1 := y1+h1;
1106 if (ex0 <= x1) or (ey0 <= y1) or (ex1 <= x0) or (ey1 <= y0) then exit;
1107 if (x0 >= ex1) or (y0 >= ey1) or (x1 >= ex0) or (y1 >= ey0) then exit;
1108 // ok, intersects
1109 if (x0 < x1) then x0 := x1;
1110 if (y0 < y1) then y0 := y1;
1111 if (ex0 > ex1) then ex0 := ex1;
1112 if (ey0 > ey1) then ey0 := ey1;
1113 w0 := ex0-x0;
1114 h0 := ey0-y0;
1115 result := (w0 > 0) and (h0 > 0);
1116 end;
1119 // ////////////////////////////////////////////////////////////////////////// //
1120 procedure normRGBA (var r, g, b, a: Integer); inline;
1121 begin
1122 if (a < 0) then a := 0 else if (a > 255) then a := 255;
1123 if (r < 0) then r := 0 else if (r > 255) then r := 255;
1124 if (g < 0) then g := 0 else if (g > 255) then g := 255;
1125 if (b < 0) then b := 0 else if (b > 255) then b := 255;
1126 end;
1128 // returns `false` if the color is transparent
1129 function setupGLColor (r, g, b, a: Integer): Boolean;
1130 begin
1131 normRGBA(r, g, b, a);
1132 if (a < 255) then
1133 begin
1134 if (a = 0) then begin result := false; exit; end;
1135 glEnable(GL_BLEND);
1136 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1137 end
1138 else
1139 begin
1140 glDisable(GL_BLEND);
1141 end;
1142 glColor4ub(Byte(r), Byte(g), Byte(b), Byte(a));
1143 result := true;
1144 end;
1146 function isScaled (): Boolean;
1147 var
1148 mt: packed array [0..15] of Double;
1149 begin
1150 glGetDoublev(GL_MODELVIEW_MATRIX, @mt[0]);
1151 result := (mt[0] <> 1.0) or (mt[1*4+1] <> 1.0);
1152 end;
1155 // ////////////////////////////////////////////////////////////////////////// //
1156 function textWidth6 (const s: AnsiString): Integer;
1157 var
1158 f: Integer;
1159 begin
1160 result := 0;
1161 for f := 1 to Length(s) do Inc(result, Integer(kgiFont6PropWidth[Integer(s[f])] and $0f)+1);
1162 if (result > 0) then Dec(result); // don't count last empty pixel
1163 end;
1166 function textWidth8 (const s: AnsiString): Integer;
1167 var
1168 f: Integer;
1169 begin
1170 result := 0;
1171 for f := 1 to Length(s) do Inc(result, Integer(kgiFont8PropWidth[Integer(s[f])] and $0f)+1);
1172 if (result > 0) then Dec(result); // don't count last empty pixel
1173 end;
1176 // return width (including last empty pixel)
1177 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;
1178 var
1179 f, c: Integer;
1180 tx, ty: Integer;
1181 begin
1182 result := 0;
1183 if (Length(s) = 0) then exit;
1184 if not setupGLColor(r, g, b, a) then exit;
1186 glEnable(GL_ALPHA_TEST);
1187 glAlphaFunc(GL_NOTEQUAL, 0.0);
1188 glEnable(GL_TEXTURE_2D);
1189 glBindTexture(GL_TEXTURE_2D, tid);
1191 for f := 1 to Length(s) do
1192 begin
1193 c := Integer(s[f]) and $ff;
1194 tx := (c mod 16)*8;
1195 ty := (c div 16)*8;
1196 glBegin(GL_QUADS);
1197 glTexCoord2f((tx+0)/128.0, (ty+0)/128.0); glVertex2i(x+0, y+0); // top-left
1198 glTexCoord2f((tx+8)/128.0, (ty+0)/128.0); glVertex2i(x+8, y+0); // top-right
1199 glTexCoord2f((tx+8)/128.0, (ty+8)/128.0); glVertex2i(x+8, y+8); // bottom-right
1200 glTexCoord2f((tx+0)/128.0, (ty+8)/128.0); glVertex2i(x+0, y+8); // bottom-left
1201 glEnd();
1202 if prop then
1203 begin
1204 x += Integer(fontwdt[c] and $0f)+1;
1205 result += Integer(fontwdt[c] and $0f)+1;
1206 end
1207 else
1208 begin
1209 x += wdt;
1210 result += wdt;
1211 end;
1212 end;
1214 glDisable(GL_ALPHA_TEST);
1215 glDisable(GL_BLEND);
1216 glDisable(GL_TEXTURE_2D);
1217 glColor4f(1, 1, 1, 1);
1218 glBindTexture(GL_TEXTURE_2D, 0);
1219 end;
1222 // ////////////////////////////////////////////////////////////////////////// //
1223 procedure drawHLine (x, y, len: Integer; r, g, b: Integer; a: Integer=255);
1224 begin
1225 if (len < 1) then exit;
1226 if not setupGLColor(r, g, b, a) then exit;
1227 glDisable(GL_TEXTURE_2D);
1228 if (not isScaled) then
1229 begin
1230 glBegin(GL_LINES);
1231 glVertex2f(x+0.375, y+0.375);
1232 glVertex2f(x+len+0.375, y+0.375);
1233 glEnd();
1234 end
1235 else
1236 begin
1237 glBegin(GL_QUADS);
1238 glVertex2i(x, y);
1239 glVertex2i(x+len, y);
1240 glVertex2i(x+len, y+1);
1241 glVertex2i(x, y+1);
1242 glEnd();
1243 end;
1244 end;
1247 procedure drawVLine (x, y, len: Integer; r, g, b: Integer; a: Integer=255);
1248 begin
1249 if (len < 1) then exit;
1250 if not setupGLColor(r, g, b, a) then exit;
1251 glDisable(GL_TEXTURE_2D);
1252 if (not isScaled) then
1253 begin
1254 glBegin(GL_LINES);
1255 glVertex2f(x+0.375, y+0.375);
1256 glVertex2f(x+0.375, y+len+0.375);
1257 glEnd();
1258 end
1259 else
1260 begin
1261 glBegin(GL_QUADS);
1262 glVertex2i(x, y);
1263 glVertex2i(x, y+len);
1264 glVertex2i(x+1, y+len);
1265 glVertex2i(x+1, y);
1266 glEnd();
1267 end;
1268 end;
1271 procedure drawLine (x1, y1, x2, y2: Integer; r, g, b: Integer; a: Integer=255);
1272 begin
1273 if not setupGLColor(r, g, b, a) then exit;
1275 glDisable(GL_TEXTURE_2D);
1277 glLineWidth(1);
1278 glPointSize(1);
1280 if (not isScaled) then
1281 begin
1282 glBegin(GL_LINES);
1283 glVertex2f(x1+0.375, y1+0.375);
1284 glVertex2f(x2+0.375, y2+0.375);
1285 glEnd();
1287 if (x1 <> x2) or (y1 <> y2) then
1288 begin
1289 glBegin(GL_POINTS);
1290 glVertex2f(x2+0.375, y2+0.375);
1291 glEnd();
1292 end;
1293 end
1294 else
1295 begin
1296 glBegin(GL_LINES);
1297 glVertex2i(x1, y1);
1298 glVertex2i(x2, y2);
1299 // draw last point
1300 glVertex2i(x2, y2);
1301 glVertex2i(x2+1, y2+1);
1302 glEnd();
1303 end;
1305 glColor4f(1, 1, 1, 1);
1306 glDisable(GL_BLEND);
1307 end;
1310 procedure drawRect (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
1311 begin
1312 if (w < 0) or (h < 0) then exit;
1313 if not setupGLColor(r, g, b, a) then exit;
1314 glDisable(GL_TEXTURE_2D);
1315 glLineWidth(1);
1316 glDisable(GL_LINE_SMOOTH);
1317 glDisable(GL_POLYGON_SMOOTH);
1318 if (w = 1) and (h = 1) then
1319 begin
1320 glBegin(GL_POINTS);
1321 glVertex2f(x+0.375, y+0.375);
1322 glEnd();
1323 end
1324 else
1325 begin
1326 glBegin(GL_LINES);
1327 glVertex2i(x, y); glVertex2i(x+w, y); // top
1328 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1329 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1330 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1331 glEnd();
1332 end;
1333 //glRect(x, y, x+w, y+h);
1334 glColor4f(1, 1, 1, 1);
1335 glDisable(GL_BLEND);
1336 end;
1339 procedure drawRectUI (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
1340 procedure hline (x, y, len: Integer);
1341 begin
1342 if (len < 1) then exit;
1343 glBegin(GL_QUADS);
1344 glVertex2i(x, y);
1345 glVertex2i(x+len, y);
1346 glVertex2i(x+len, y+1);
1347 glVertex2i(x, y+1);
1348 glEnd();
1349 end;
1351 procedure vline (x, y, len: Integer);
1352 begin
1353 if (len < 1) then exit;
1354 glBegin(GL_QUADS);
1355 glVertex2i(x, y);
1356 glVertex2i(x, y+len);
1357 glVertex2i(x+1, y+len);
1358 glVertex2i(x+1, y);
1359 glEnd();
1360 end;
1362 var
1363 scaled: Boolean;
1364 begin
1365 if (w < 0) or (h < 0) then exit;
1366 if not setupGLColor(r, g, b, a) then exit;
1367 glDisable(GL_TEXTURE_2D);
1368 glLineWidth(1);
1369 glDisable(GL_LINE_SMOOTH);
1370 glDisable(GL_POLYGON_SMOOTH);
1371 scaled := isScaled();
1372 if (w = 1) and (h = 1) then
1373 begin
1374 glBegin(GL_POINTS);
1375 if scaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
1376 glEnd();
1377 end
1378 else
1379 begin
1380 if not scaled then
1381 begin
1382 glBegin(GL_LINES);
1383 glVertex2i(x, y); glVertex2i(x+w, y); // top
1384 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1385 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1386 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1387 glEnd();
1388 end
1389 else
1390 begin
1391 hline(x, y, w);
1392 hline(x, y+h-1, w);
1393 vline(x, y+1, h-2);
1394 vline(x+w-1, y+1, h-2);
1395 end;
1396 end;
1397 //glRect(x, y, x+w, y+h);
1398 glColor4f(1, 1, 1, 1);
1399 glDisable(GL_BLEND);
1400 end;
1403 procedure darkenRect (x, y, w, h: Integer; a: Integer);
1404 begin
1405 if (w < 0) or (h < 0) then exit;
1406 if (a < 0) then a := 0;
1407 if (a >= 255) then exit;
1408 glEnable(GL_BLEND);
1409 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
1410 glDisable(GL_LINE_SMOOTH);
1411 glDisable(GL_POLYGON_SMOOTH);
1412 glDisable(GL_TEXTURE_2D);
1413 glColor4f(0.0, 0.0, 0.0, a/255.0);
1414 glBegin(GL_QUADS);
1415 glVertex2i(x, y);
1416 glVertex2i(x+w, y);
1417 glVertex2i(x+w, y+h);
1418 glVertex2i(x, y+h);
1419 glEnd();
1420 //glRect(x, y, x+w, y+h);
1421 glColor4f(1, 1, 1, 1);
1422 glDisable(GL_BLEND);
1423 //glBlendEquation(GL_FUNC_ADD);
1424 end;
1427 procedure fillRect (x, y, w, h: Integer; r, g, b: Integer; a: Integer=255);
1428 begin
1429 if (w < 0) or (h < 0) then exit;
1430 if not setupGLColor(r, g, b, a) then exit;
1431 glDisable(GL_LINE_SMOOTH);
1432 glDisable(GL_POLYGON_SMOOTH);
1433 glDisable(GL_TEXTURE_2D);
1434 glBegin(GL_QUADS);
1435 glVertex2f(x, y);
1436 glVertex2f(x+w, y);
1437 glVertex2f(x+w, y+h);
1438 glVertex2f(x, y+h);
1439 glEnd();
1440 glColor4f(1, 1, 1, 1);
1441 glDisable(GL_BLEND);
1442 end;
1445 // ////////////////////////////////////////////////////////////////////////// //
1446 function drawText6 (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1447 begin
1448 if (font6texid = 0) then createFonts();
1449 drawTextInternal(6, x, y, s, r, g, b, a, font6texid, kgiFont6PropWidth, false);
1450 result := Length(s)*6;
1451 end;
1453 function drawText8 (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1454 begin
1455 if (font8texid = 0) then createFonts();
1456 drawTextInternal(8, x, y, s, r, g, b, a, font8texid, kgiFont8PropWidth, false);
1457 result := Length(s)*8;
1458 end;
1460 function drawText6Prop (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1461 begin
1462 if (prfont6texid = 0) then createFonts();
1463 result := drawTextInternal(6, x, y, s, r, g, b, a, prfont6texid, kgiFont6PropWidth, true);
1464 end;
1466 function drawText8Prop (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1467 begin
1468 if (prfont8texid = 0) then createFonts();
1469 result := drawTextInternal(8, x, y, s, r, g, b, a, prfont8texid, kgiFont8PropWidth, true);
1470 end;
1473 // ////////////////////////////////////////////////////////////////////////// //
1474 // x-centered at `x`
1475 function drawText6XC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1476 begin
1477 if (font6texid = 0) then createFonts();
1478 x -= Length(s)*6 div 2;
1479 drawTextInternal(6, x, y, s, r, g, b, a, font6texid, kgiFont6PropWidth, false);
1480 result := Length(s)*6;
1481 end;
1483 function drawText8XC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1484 begin
1485 if (font8texid = 0) then createFonts();
1486 x -= Length(s)*8 div 2;
1487 drawTextInternal(8, x, y, s, r, g, b, a, font8texid, kgiFont8PropWidth, false);
1488 result := Length(s)*8;
1489 end;
1491 function drawText6PropXC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1492 begin
1493 if (prfont6texid = 0) then createFonts();
1494 x -= textWidth6(s) div 2;
1495 result := drawTextInternal(6, x, y, s, r, g, b, a, prfont6texid, kgiFont6PropWidth, true);
1496 end;
1498 function drawText8PropXC (x, y: Integer; const s: AnsiString; r, g, b: Integer; a: Integer=255): Integer;
1499 begin
1500 if (prfont8texid = 0) then createFonts();
1501 x -= textWidth8(s) div 2;
1502 result := drawTextInternal(8, x, y, s, r, g, b, a, prfont8texid, kgiFont8PropWidth, true);
1503 end;
1506 // ////////////////////////////////////////////////////////////////////////// //
1507 procedure oglRestoreMode (doClear: Boolean);
1508 begin
1509 oglSetup2D(gScrWidth, gScrHeight);
1510 glScissor(0, 0, gScrWidth, gScrHeight);
1512 glBindTexture(GL_TEXTURE_2D, 0);
1513 glDisable(GL_BLEND);
1514 glDisable(GL_TEXTURE_2D);
1515 glDisable(GL_STENCIL_TEST);
1516 glDisable(GL_SCISSOR_TEST);
1517 glDisable(GL_LIGHTING);
1518 glDisable(GL_DEPTH_TEST);
1519 glDisable(GL_CULL_FACE);
1520 glDisable(GL_LINE_SMOOTH);
1521 glDisable(GL_POINT_SMOOTH);
1522 glLineWidth(1);
1523 glPointSize(1);
1524 glColor4f(1, 1, 1, 1);
1526 if doClear then
1527 begin
1528 glClearColor(0, 0, 0, 0);
1529 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_ACCUM_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
1530 end;
1532 // scale everything
1533 glMatrixMode(GL_MODELVIEW);
1534 glLoadIdentity();
1535 //glScalef(4, 4, 1);
1536 end;
1539 procedure onWinFocus (); begin end;
1541 procedure onWinBlur (); begin resetKMState(true); end;
1543 procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
1545 procedure onPostRender (); begin oglRestoreMode(false); oglDrawCursor(); end;
1547 procedure onInit ();
1548 begin
1549 oglSetup2D(gScrWidth, gScrHeight);
1551 createCursorTexture();
1552 createFonts();
1553 end;
1555 procedure onDeinit ();
1556 begin
1557 resetKMState(false);
1558 if (curtexid <> 0) then glDeleteTextures(1, @curtexid);
1559 curtexid := 0;
1560 deleteFonts();
1561 curButState := 0;
1562 curModState := 0;
1563 curMsX := 0;
1564 curMsY := 0;
1565 end;
1568 // ////////////////////////////////////////////////////////////////////////// //
1569 begin
1570 evSDLCB := onSDLEvent;
1571 winFocusCB := onWinFocus;
1572 winBlurCB := onWinBlur;
1573 prerenderFrameCB := onPreRender;
1574 postrenderFrameCB := onPostRender;
1575 oglInitCB := onInit;
1576 oglDeinitCB := onDeinit;
1577 end.