DEADSOFTWARE

1237550cc98d8b425a42f041810b30681702fe78
[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 glLineWidth(1);
1250 glBegin(GL_LINES);
1251 glVertex2f(x+0.375, y+0.375);
1252 glVertex2f(x+len+0.375, y+0.375);
1253 glEnd();
1254 end
1255 else
1256 begin
1257 glBegin(GL_QUADS);
1258 glVertex2i(x, y);
1259 glVertex2i(x+len, y);
1260 glVertex2i(x+len, y+1);
1261 glVertex2i(x, y+1);
1262 glEnd();
1263 end;
1264 end;
1267 procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
1268 begin
1269 if (len < 1) then exit;
1270 if not setupGLColor(clr) then exit;
1271 glDisable(GL_TEXTURE_2D);
1272 if (not isScaled) then
1273 begin
1274 glLineWidth(1);
1275 glBegin(GL_LINES);
1276 glVertex2f(x+0.375, y+0.375);
1277 glVertex2f(x+0.375, y+len+0.375);
1278 glEnd();
1279 end
1280 else
1281 begin
1282 glBegin(GL_QUADS);
1283 glVertex2i(x, y);
1284 glVertex2i(x, y+len);
1285 glVertex2i(x+1, y+len);
1286 glVertex2i(x+1, y);
1287 glEnd();
1288 end;
1289 end;
1292 procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
1293 begin
1294 if not setupGLColor(clr) then exit;
1296 glDisable(GL_TEXTURE_2D);
1298 glLineWidth(1);
1299 glPointSize(1);
1301 if (not isScaled) then
1302 begin
1303 glLineWidth(1);
1304 glBegin(GL_LINES);
1305 glVertex2f(x1+0.375, y1+0.375);
1306 glVertex2f(x2+0.375, y2+0.375);
1307 glEnd();
1309 if (x1 <> x2) or (y1 <> y2) then
1310 begin
1311 glBegin(GL_POINTS);
1312 glVertex2f(x2+0.375, y2+0.375);
1313 glEnd();
1314 end;
1315 end
1316 else
1317 begin
1318 glLineWidth(1);
1319 glBegin(GL_LINES);
1320 glVertex2i(x1, y1);
1321 glVertex2i(x2, y2);
1322 // draw last point
1323 glVertex2i(x2, y2);
1324 glVertex2i(x2+1, y2+1);
1325 glEnd();
1326 end;
1328 glColor4f(1, 1, 1, 1);
1329 glDisable(GL_BLEND);
1330 end;
1333 procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
1334 begin
1335 if (w < 0) or (h < 0) then exit;
1336 if not setupGLColor(clr) then exit;
1337 glDisable(GL_TEXTURE_2D);
1338 glLineWidth(1);
1339 glDisable(GL_LINE_SMOOTH);
1340 glDisable(GL_POLYGON_SMOOTH);
1341 if (w = 1) and (h = 1) then
1342 begin
1343 glBegin(GL_POINTS);
1344 glVertex2f(x+0.375, y+0.375);
1345 glEnd();
1346 end
1347 else
1348 begin
1349 glLineWidth(1);
1350 glBegin(GL_LINES);
1351 glVertex2i(x, y); glVertex2i(x+w, y); // top
1352 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1353 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1354 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1355 glEnd();
1356 end;
1357 //glRect(x, y, x+w, y+h);
1358 glColor4f(1, 1, 1, 1);
1359 glDisable(GL_BLEND);
1360 end;
1363 procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
1364 procedure hline (x, y, len: Integer);
1365 begin
1366 if (len < 1) then exit;
1367 glBegin(GL_QUADS);
1368 glVertex2i(x, y);
1369 glVertex2i(x+len, y);
1370 glVertex2i(x+len, y+1);
1371 glVertex2i(x, y+1);
1372 glEnd();
1373 end;
1375 procedure vline (x, y, len: Integer);
1376 begin
1377 if (len < 1) then exit;
1378 glBegin(GL_QUADS);
1379 glVertex2i(x, y);
1380 glVertex2i(x, y+len);
1381 glVertex2i(x+1, y+len);
1382 glVertex2i(x+1, y);
1383 glEnd();
1384 end;
1386 var
1387 scaled: Boolean;
1388 begin
1389 if (w < 0) or (h < 0) then exit;
1390 if not setupGLColor(clr) then exit;
1391 glDisable(GL_TEXTURE_2D);
1392 glLineWidth(1);
1393 glDisable(GL_LINE_SMOOTH);
1394 glDisable(GL_POLYGON_SMOOTH);
1395 scaled := isScaled();
1396 if (w = 1) and (h = 1) then
1397 begin
1398 glBegin(GL_POINTS);
1399 if scaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
1400 glEnd();
1401 end
1402 else
1403 begin
1404 if not scaled then
1405 begin
1406 glLineWidth(1);
1407 glBegin(GL_LINES);
1408 glVertex2i(x, y); glVertex2i(x+w, y); // top
1409 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1410 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1411 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1412 glEnd();
1413 end
1414 else
1415 begin
1416 hline(x, y, w);
1417 hline(x, y+h-1, w);
1418 vline(x, y+1, h-2);
1419 vline(x+w-1, y+1, h-2);
1420 end;
1421 end;
1422 //glRect(x, y, x+w, y+h);
1423 glColor4f(1, 1, 1, 1);
1424 glDisable(GL_BLEND);
1425 end;
1428 procedure darkenRect (x, y, w, h: Integer; a: Integer);
1429 begin
1430 if (w < 0) or (h < 0) then exit;
1431 if (a < 0) then a := 0;
1432 if (a >= 255) then exit;
1433 glEnable(GL_BLEND);
1434 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
1435 glDisable(GL_LINE_SMOOTH);
1436 glDisable(GL_POLYGON_SMOOTH);
1437 glDisable(GL_TEXTURE_2D);
1438 glColor4f(0.0, 0.0, 0.0, a/255.0);
1439 glBegin(GL_QUADS);
1440 glVertex2i(x, y);
1441 glVertex2i(x+w, y);
1442 glVertex2i(x+w, y+h);
1443 glVertex2i(x, y+h);
1444 glEnd();
1445 //glRect(x, y, x+w, y+h);
1446 glColor4f(1, 1, 1, 1);
1447 glDisable(GL_BLEND);
1448 //glBlendEquation(GL_FUNC_ADD);
1449 end;
1452 procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
1453 begin
1454 if (w < 0) or (h < 0) then exit;
1455 if not setupGLColor(clr) then exit;
1456 glDisable(GL_LINE_SMOOTH);
1457 glDisable(GL_POLYGON_SMOOTH);
1458 glDisable(GL_TEXTURE_2D);
1459 glBegin(GL_QUADS);
1460 glVertex2f(x, y);
1461 glVertex2f(x+w, y);
1462 glVertex2f(x+w, y+h);
1463 glVertex2f(x, y+h);
1464 glEnd();
1465 glColor4f(1, 1, 1, 1);
1466 glDisable(GL_BLEND);
1467 end;
1470 // ////////////////////////////////////////////////////////////////////////// //
1471 function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1472 begin
1473 if (font6texid = 0) then createFonts();
1474 drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
1475 result := Length(s)*6;
1476 end;
1478 function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1479 begin
1480 if (font8texid = 0) then createFonts();
1481 drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
1482 result := Length(s)*8;
1483 end;
1485 function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1486 begin
1487 if (prfont6texid = 0) then createFonts();
1488 result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
1489 end;
1491 function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1492 begin
1493 if (prfont8texid = 0) then createFonts();
1494 result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
1495 end;
1498 // ////////////////////////////////////////////////////////////////////////// //
1499 // x-centered at `x`
1500 function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1501 begin
1502 if (font6texid = 0) then createFonts();
1503 x -= Length(s)*6 div 2;
1504 drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
1505 result := Length(s)*6;
1506 end;
1508 function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1509 begin
1510 if (font8texid = 0) then createFonts();
1511 x -= Length(s)*8 div 2;
1512 drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
1513 result := Length(s)*8;
1514 end;
1516 function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1517 begin
1518 if (prfont6texid = 0) then createFonts();
1519 x -= textWidth6(s) div 2;
1520 result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
1521 end;
1523 function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1524 begin
1525 if (prfont8texid = 0) then createFonts();
1526 x -= textWidth8(s) div 2;
1527 result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
1528 end;
1531 // ////////////////////////////////////////////////////////////////////////// //
1532 procedure oglRestoreMode (doClear: Boolean);
1533 begin
1534 oglSetup2D(gScrWidth, gScrHeight);
1535 glScissor(0, 0, gScrWidth, gScrHeight);
1537 glBindTexture(GL_TEXTURE_2D, 0);
1538 glDisable(GL_BLEND);
1539 glDisable(GL_TEXTURE_2D);
1540 glDisable(GL_STENCIL_TEST);
1541 glDisable(GL_SCISSOR_TEST);
1542 glDisable(GL_LIGHTING);
1543 glDisable(GL_DEPTH_TEST);
1544 glDisable(GL_CULL_FACE);
1545 glDisable(GL_LINE_SMOOTH);
1546 glDisable(GL_POINT_SMOOTH);
1547 glLineWidth(1);
1548 glPointSize(1);
1549 glColor4f(1, 1, 1, 1);
1551 if doClear then
1552 begin
1553 glClearColor(0, 0, 0, 0);
1554 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_ACCUM_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
1555 end;
1557 // scale everything
1558 glMatrixMode(GL_MODELVIEW);
1559 glLoadIdentity();
1560 //glScalef(4, 4, 1);
1561 end;
1564 procedure onWinFocus (); begin end;
1566 procedure onWinBlur (); begin resetKMState(true); end;
1568 procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
1570 procedure onPostRender (); begin oglRestoreMode(false); oglDrawCursor(); end;
1572 procedure onInit ();
1573 begin
1574 oglSetup2D(gScrWidth, gScrHeight);
1576 createCursorTexture();
1577 createFonts();
1578 end;
1580 procedure onDeinit ();
1581 begin
1582 resetKMState(false);
1583 if (curtexid <> 0) then glDeleteTextures(1, @curtexid);
1584 curtexid := 0;
1585 deleteFonts();
1586 curButState := 0;
1587 curModState := 0;
1588 curMsX := 0;
1589 curMsY := 0;
1590 end;
1593 // ////////////////////////////////////////////////////////////////////////// //
1594 begin
1595 evSDLCB := onSDLEvent;
1596 winFocusCB := onWinFocus;
1597 winBlurCB := onWinBlur;
1598 prerenderFrameCB := onPreRender;
1599 postrenderFrameCB := onPostRender;
1600 oglInitCB := onInit;
1601 oglDeinitCB := onDeinit;
1602 end.