DEADSOFTWARE

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