DEADSOFTWARE

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