2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module egfx
is aliced
;
22 import std
.concurrency
;
24 import arsd
.simpledisplay
;
28 import iv
.glbinds
: glTexParameterfv
; // rdmd hack
35 // ////////////////////////////////////////////////////////////////////////// //
36 public char uni2local (dchar dch
) nothrow @trusted @nogc {
37 if (!Utf8DecoderFast
.isValidDC(dch
)) return '\x97';
38 switch (cast(uint)dch
) {
40 case 0x00C7: return 'C';
41 case 0x00A7: return '\xab'; // "section sign" (aka paragraph)
42 case 0x00AE: return '\xbe'; // (r)
43 case 0x00AB: return '<'; // left doublequote
44 case 0x00B2: return '2'; // superscript, lol
45 case 0x00B3: return '3'; // superscript, lol
46 case 0x00B4: return '\'';
47 case 0x00B9: return '1'; // superscript, lol
48 case 0x00BB: return '>'; // right doublequote
49 case 0x00DA: return 'U';
50 case 0x00E0: return 'a';
51 case 0x00E1: return 'a';
52 case 0x00E2: return 'a';
53 case 0x00E4: return 'a';
54 case 0x00E9: return 'e';
55 case 0x00ED: return 'i';
56 case 0x00F6: return 'o';
57 case 0x00F8: return 'o';
58 case 0x00FC: return 'u';
59 case 0x00FD: return 'y';
60 case 0x010D: return 'c';
61 case 0x0159: return 'r';
62 case 0x0161: return 's';
63 case 0x0308: return 'C';
64 case 0x0398: return '\xa9'; // theta
65 case 0x032A: return 's';
66 case 0x033B: return 'o';
67 case 0x03BC: return '\x7f';
68 case 0x1E81: return 'w';
69 case 0x2013: return '-';
70 case 0x2014: return '-';
71 case 0x2018: return '`';
72 case 0x2019: return '\'';
73 case 0x201C: return '"';
74 case 0x201D: return '"';
75 case 0x2026: return '\x1f'; // ellipsis
76 case 0x20AC: return 'E'; // "euro" sign
77 case 0x221E: return '\xac'; // infinity
78 case 0x2236: return '\xb0'; // "ratio"
79 case 0x2713: return '\xba'; // check mark
80 case 0x276E: return '<'; // <<
81 case 0x276F: return '>'; // >>
82 case 0x3001: return ','; // "ideographic comma" -- ???
83 case 0xFF1A: return ':';
84 case 0x1F60A: return '\x9d'; // fuckin' emoji
86 char res
= uni2koi
!'\x97'(dch
);
87 if (res
== '\x97') conwritefln
!"BAD: 0x%04X"(cast(uint)dch
);
93 public @property string
triangleUpStr () nothrow @safe @nogc { return "\x8d"; }
94 public @property string
triangleDownStr () nothrow @safe @nogc { return "\x8e"; }
96 public @property string
arrowLeftStr () nothrow @safe @nogc { return "\x90"; }
97 public @property string
arrowRightStr () nothrow @safe @nogc { return "\x8f"; }
100 public string
uniRecode (const(char)[] utf
) nothrow @trusted {
102 res
.reserve(utf
.length
);
104 foreach (char ch
; utf
) {
105 if (dc
.decode(cast(ubyte)ch
)) res
~= uni2local(dc
.codepoint
);
111 // ////////////////////////////////////////////////////////////////////////// //
112 public void utfByLocal (const(char)[] s
, scope void delegate (char ch
) nothrow @trusted @nogc dg
) nothrow @trusted @nogc {
113 if (dg
is null) return;
115 foreach (char ch
; s
) {
116 if (dc
.decode(cast(ubyte)ch
)) dg(uni2local(dc
.codepoint
));
121 // ////////////////////////////////////////////////////////////////////////// //
122 // font: (hi nibble: lshift; lo nibble: width); 8 data bytes
123 static immutable ubyte[] font6x8p
= cast(immutable(ubyte)[])import("databin/zxpfontkoi.fnt");
126 // ////////////////////////////////////////////////////////////////////////// //
127 //public enum GLTexType = GL_RGBA;
128 public enum GLTexType
= GL_BGRA
;
130 public __gshared
int VBufWidth
= 740;
131 public __gshared
int VBufHeight
= 520;
132 public __gshared
ubyte VBufScale
= 2; // new window scale
133 public __gshared
ubyte vbufEffScale
= 2; // effective (current) window scale
135 public __gshared
uint* vglTexBuf
; // OpenGL texture buffer
136 public __gshared
uint vArrowTextureId
= 0;
138 shared static this () {
139 import core
.stdc
.stdlib
: malloc
;
140 vglTexBuf
= cast(uint*)malloc((VBufWidth
*VBufHeight
+4)*4);
141 if (vglTexBuf
is null) assert(0, "out of memory!");
142 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = 0;
146 public @property int winWidth () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
; }
147 public @property int winHeight () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
; }
149 public @property int winWidthScaled () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
*vbufEffScale
; }
150 public @property int winHeightScaled () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
*vbufEffScale
; }
153 // ////////////////////////////////////////////////////////////////////////// //
154 public void createArrowTexture () {
157 enum wrapOpt
= GL_REPEAT
;
158 enum filterOpt
= GL_NEAREST
; //GL_LINEAR;
159 enum ttype
= GL_UNSIGNED_BYTE
;
161 if (vArrowTextureId
) glDeleteTextures(1, &vArrowTextureId
);
163 glGenTextures(1, &vArrowTextureId
);
164 if (vArrowTextureId
== 0) assert(0, "can't create arrow texture");
166 //GLint gltextbinding;
167 //glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
168 //scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
170 glBindTexture(GL_TEXTURE_2D
, vArrowTextureId
);
171 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
172 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
173 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
174 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
175 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
176 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
178 GLfloat
[4] bclr
= 0.0;
179 glTexParameterfv(GL_TEXTURE_2D
, GL_TEXTURE_BORDER_COLOR
, bclr
.ptr
);
181 uint[16*8] pmap
= 0x00_000000U;
182 // sprite,sprite,mask,mask
183 static immutable ushort[$] spx
= [
184 0b11111111_10000000, 0b00000000_01111111,
185 0b01000000_10000000, 0b10000000_01111111,
186 0b00100000_10000000, 0b11000000_01111111,
187 0b00010000_01100000, 0b11100000_00011111,
188 0b00001001_10011000, 0b11110000_00000111,
189 0b00000110_01100110, 0b11111001_10000001,
190 0b00000000_00011001, 0b11111111_11100000,
191 0b00000000_00000110, 0b11111111_11111001,
194 foreach (immutable dy
; 0..8) {
195 ushort spr
= spx
[dy
*2+0];
196 ushort msk
= spx
[dy
*2+1];
197 foreach (immutable dx
; 0..16) {
198 if ((msk
&0x8000) == 0) {
199 pmap
[dy
*16+dx
] = (spr
&0x8000 ?
0xff_ffffffU
: 0xff_000000U);
205 //pmap = 0xff_ff0000U;
208 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, 16, 8, 0, GLTexType
, GL_UNSIGNED_BYTE
, pmap
.ptr
);
211 // ////////////////////////////////////////////////////////////////////////// //
212 // mix dc with ARGB (or ABGR) col; dc A is ignored (removed)
213 public uint gxColMix (uint dc
, uint col
) pure nothrow @trusted @nogc {
214 pragma(inline
, true);
215 immutable uint a
= 256-(col
>>24); // to not loose bits
216 //immutable uint dc = (da)&0xffffff;
218 immutable uint srb
= (col
&0xff00ff);
219 immutable uint sg
= (col
&0x00ff00);
220 immutable uint drb
= (dc
&0xff00ff);
221 immutable uint dg
= (dc
&0x00ff00);
222 immutable uint orb
= (drb
+(((srb
-drb
)*a
+0x800080)>>8))&0xff00ff;
223 immutable uint og
= (dg
+(((sg
-dg
)*a
+0x008000)>>8))&0x00ff00;
228 // ////////////////////////////////////////////////////////////////////////// //
229 private template isGoodRGBInt(T
) {
230 import std
.traits
: Unqual
;
234 is(TT
== short) ||
is(TT
== ushort) ||
235 is(TT
== int) ||
is(TT
== uint) ||
236 is(TT
== long) ||
is(TT
== ulong);
240 // ////////////////////////////////////////////////////////////////////////// //
241 public uint ztrgb(T0
, T1
, T2
) (T0 r
, T1 g
, T2 b
) pure nothrow @trusted @nogc if (isGoodRGBInt
!T0
&& isGoodRGBInt
!T1
&& isGoodRGBInt
!T2
) {
242 pragma(inline
, true);
243 return (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
247 public template gxRGB(int r
, int g
, int b
) {
248 enum gxRGB
= (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
251 public template gxRGBA(int r
, int g
, int b
, int a
) {
252 enum gxRGBA
= (clampToByte(a
)<<24)|
(clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
256 // ////////////////////////////////////////////////////////////////////////// //
257 public __gshared
int clipX0
= 0, clipY0
= 0;
258 public __gshared
int clipX1
= 65535, clipY1
= 65535;
261 public void clipReset () nothrow @trusted @nogc {
263 clipX1
= clipY1
= 65535;
266 public @property bool clipEmpty () nothrow @trusted @nogc { return (clipX0
< clipX1 || clipY0
< clipY1
); }
268 public @property int clipWidth () nothrow @trusted @nogc { return (clipX0
<= clipX1 ? clipX1
-clipX0
+1 : 0); }
269 public @property int clipHeight () nothrow @trusted @nogc { return (clipY0
<= clipY1 ? clipY1
-clipY0
+1 : 0); }
271 public void clipShrink (int dx
, int dy
) nothrow @trusted @nogc {
278 public void clipGrow (int dx
, int dy
) nothrow @trusted @nogc {
286 public void clipIntersect (int x0
, int y0
, int x1
, int y1
) nothrow @trusted @nogc {
287 if (clipX0
< x0
) clipX0
= x0
;
288 if (clipY0
< y0
) clipY0
= y0
;
289 if (clipX1
> x1
) clipX1
= x1
;
290 if (clipY1
> y1
) clipY1
= y1
;
294 // ////////////////////////////////////////////////////////////////////////// //
295 public void gxClearScreen (uint clr
) nothrow @trusted @nogc {
296 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = clr
;
300 public void gxPutPixel (int x
, int y
, uint c
) nothrow @trusted @nogc {
301 pragma(inline
, true);
302 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& (c
&0xff000000) != 0xff000000) {
303 if (x
>= clipX0
&& y
>= clipY0
&& x
<= clipX1
&& y
<= clipY1
) {
305 ubyte* dp = cast(ubyte*)zxtexbuf.ptr;
306 dp += y*(VBufWidth*4)+x*4;
307 *dp++ = (c>>16)&0xff; //R
308 *dp++ = (c>>8)&0xff; //G
312 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
;
313 *dp
= gxColMix(*dp
, c
);
319 // ////////////////////////////////////////////////////////////////////////// //
320 public int gxCharWidthP (char ch
) nothrow @trusted @nogc { return font6x8p
.ptr
[cast(uint)ch
*9]; }
323 public void gxDrawCharP (int x
, int y
, char ch
, uint fg
) nothrow @trusted @nogc {
324 if (FontHeight
> 8) y
+= 1;
325 ubyte wdt
= font6x8p
.ptr
[cast(uint)ch
*9];
326 ubyte shift
= wdt
>>4;
328 foreach (immutable dy
; 1..9) {
329 ubyte b
= cast(ubyte)(font6x8p
.ptr
[cast(uint)ch
*9+dy
]<<shift
);
331 foreach (immutable dx
; 0..wdt
) {
332 if (b
&0x80) gxPutPixel(x
+dx
, y
, fg
);
342 // ////////////////////////////////////////////////////////////////////////// //
343 public int gxCharWidthScaledP (int scale
, char ch
) nothrow @trusted @nogc { return (scale
< 1 ?
0 : font6x8p
.ptr
[cast(uint)ch
*9]*scale
); }
346 public void gxDrawCharScaledP (int scale
, int x
, int y
, char ch
, uint fg
) nothrow @trusted @nogc {
347 if (scale
< 1) return;
348 if (scale
== 1) { gxDrawCharP(x
, y
, ch
, fg
); return; }
349 if (FontHeight
> 8) y
+= scale
;
350 ubyte wdt
= font6x8p
.ptr
[cast(uint)ch
*9];
351 ubyte shift
= wdt
>>4;
353 foreach (immutable dy
; 1..9) {
354 ubyte b
= cast(ubyte)(font6x8p
.ptr
[cast(uint)ch
*9+dy
]<<shift
);
356 foreach (immutable dx
; 0..wdt
) {
357 foreach (immutable scx
; 0..scale
) {
358 foreach (immutable scy
; 0..scale
) {
359 if (b
&0x80) gxPutPixel(x
+dx
*scale
+scx
, y
+scy
, fg
);
370 // ////////////////////////////////////////////////////////////////////////// //
377 nothrow @trusted @nogc:
378 this (int ascale
) { scale
= ascale
; }
380 int fixWidthPre (char ch
) {
381 if (wasch
) wdt
+= font6x8p
.ptr
[cast(uint)pch
*9]*scale
;
383 (pch
== 195 || pch
== 196 || pch
== 227 ||
384 (ch
== 193 && pch
== 210) ||
385 (pch
== 210 && ch
== 214) ||
386 (pch
== 214 && ch
== 193) ||
387 (pch
== 197 && ch
== 193) ||
388 (pch
== 200 && ch
== 207) ||
389 (pch
== 207 && ch
== 200) ||
390 (pch
== 207 && ch
== 212) ||
391 (pch
== 212 && ch
== 207) ||
392 (pch
== 197 && ch
== 212) ||
393 (pch
== 212 && ch
== 197) ||
394 (pch
== 194 && ch
== 213) ||
395 //(pch == 203 && ch == 197) ||
396 //(pch == 203 && ch == 207) ||
397 //(pch == 203 && ch == 193) ||
398 //(pch == 210 && ch == 209) ||
399 (pch
== 193 && ch
== 212) ||
400 (pch
== 220 && ch
== 212) ||
401 (pch
== 212 && ch
== 220) ||
402 (pch
== 212 && ch
== 209) ||
407 wdt
-= scale
; // lowercase ce, lowercase de
409 if (wdt
&& ch
!= 196) wdt
+= scale
; // lowercase de
415 @property int finalWidth () const { return wdt
+(wasch ? font6x8p
.ptr
[cast(uint)pch
*9]*scale
: 0); }
419 // ////////////////////////////////////////////////////////////////////////// //
420 public int gxTextHeightP () nothrow @trusted @nogc { return FontHeight
; }
422 public int gxTextWidthP (const(char)[] s
) nothrow @trusted @nogc {
424 foreach (char ch
; s
) kern
.fixWidthPre(ch
);
425 return kern
.finalWidth
;
429 public int gxDrawTextP (int x
, int y
, const(char)[] s
, uint fg
) nothrow @trusted @nogc {
431 foreach (char ch
; s
) {
432 auto ofs
= kern
.fixWidthPre(ch
);
433 gxDrawCharP(x
+ofs
, y
, ch
, fg
);
435 return kern
.finalWidth
;
439 // ////////////////////////////////////////////////////////////////////////// //
441 public int gxDrawTextOutP (int x
, int y
, const(char)[] s
, uint fg
, uint ot
) nothrow @trusted @nogc {
442 foreach (immutable dy
; -1..2) {
443 foreach (immutable dx
; -1..2) {
444 if (dx
== 0 && dy
== 0) continue;
445 gxDrawTextP(x
+dx
, y
+dy
, s
, ot
);
448 return gxDrawTextP(x
, y
, s
, fg
);
452 // ////////////////////////////////////////////////////////////////////////// //
453 public int gxTextHeightUtf () nothrow @trusted @nogc { return FontHeight
; }
454 public int gxTextHeightScaledUtf (int scale
) nothrow @trusted @nogc { return (scale
< 1 ?
0 : FontHeight
*scale
); }
456 public int gxTextWidthScaledUtf (int scale
, const(char)[] s
) nothrow @trusted @nogc {
457 if (scale
< 1) return 0;
458 auto kern
= GxKerning(scale
);
459 s
.utfByLocal(delegate (char ch
) { kern
.fixWidthPre(ch
); });
460 return kern
.finalWidth
;
463 public int gxTextWidthUtf (const(char)[] s
) nothrow @trusted @nogc { return gxTextWidthScaledUtf(1, s
); }
466 public int gxDrawTextScaledUtf (int scale
, int x
, int y
, const(char)[] s
, uint clr
) nothrow @trusted @nogc {
467 if (scale
< 1) return 0;
468 auto kern
= GxKerning(scale
);
469 s
.utfByLocal(delegate (char ch
) {
470 auto ofs
= kern
.fixWidthPre(ch
);
471 gxDrawCharScaledP(scale
, x
+ofs
, y
, ch
, clr
);
473 return kern
.finalWidth
;
476 public int gxDrawTextUtf (int x
, int y
, const(char)[] s
, uint clr
) nothrow @trusted @nogc { return gxDrawTextScaledUtf(1, x
, y
, s
, clr
); }
479 // ////////////////////////////////////////////////////////////////////////// //
480 public void gxHLine (int x
, int y
, int w
, uint clr
) nothrow @trusted @nogc {
481 if (w
< 1 || y
< 0 || y
>= VBufHeight || x
>= VBufWidth
) return;
483 if (x
+w
<= 0) return;
488 if (x
+w
> VBufWidth
) {
492 while (w
-- > 0) gxPutPixel(x
++, y
, clr
);
496 public void gxVLine (int x
, int y
, int h
, uint clr
) nothrow @trusted @nogc {
497 if (h
< 1 || x
< 0 || x
>= VBufWidth || y
>= VBufHeight
) return;
499 if (y
+h
<= 0) return;
504 if (y
+h
> VBufHeight
) {
508 while (h
-- > 0) gxPutPixel(x
, y
++, clr
);
512 public void gxFillRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
513 if (w
< 1 || h
< 1 || x
>= VBufWidth || y
>= VBufHeight
) return;
514 while (h
-- > 0) gxHLine(x
, y
++, w
, clr
);
518 public void gxDrawRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
519 if (w
< 1 || h
< 1 || x
>= VBufWidth || y
>= VBufHeight
) return;
520 gxHLine(x
, y
, w
, clr
);
521 gxHLine(x
, y
+h
-1, w
, clr
);
522 gxVLine(x
, y
+1, h
-2, clr
);
523 gxVLine(x
+w
-1, y
+1, h
-2, clr
);
527 // ////////////////////////////////////////////////////////////////////////// //
528 // use clip region as boundaries
529 public void gxDrawShadow () nothrow @trusted @nogc {
532 gxFillRect(clipX1
-7, clipY0
+8, 8, clipHeight
-8*2, gxRGBA
!(0, 0, 0, 127));
533 gxFillRect(clipX0
+8, clipY1
-7, clipWidth
-8, 8, gxRGBA
!(0, 0, 0, 127));
539 public void gxDrawWindow (const(char)[] title
, uint framecolor
, uint titlecolor
, uint windowcolor
) nothrow @trusted @nogc {
542 gxFillRect(clipX0
, clipY0
, clipWidth
, clipHeight
, windowcolor
);
543 gxDrawRect(clipX0
, clipY0
, clipWidth
, clipHeight
, framecolor
);
545 if (title
!is null) {
546 gxFillRect(clipX0
, clipY0
, clipWidth
, (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2), framecolor
);
547 gxDrawTextUtf(clipX0
+(clipWidth
-gxTextWidthUtf(title
))/2, clipY0
+1, title
, titlecolor
);