cosmetix
[chiroptera.git] / egfx.d
blobf4d3edba1473c8ed9f8aa507f5c6a8ded4c287af
1 /* E-Mail Client
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;
19 private:
21 import core.atomic;
22 import std.concurrency;
24 import arsd.simpledisplay;
26 import iv.bclamp;
27 import iv.cmdcon;
28 import iv.glbinds : glTexParameterfv; // rdmd hack
29 import iv.utfutil;
30 import iv.vfs;
32 enum FontHeight = 9;
35 // ////////////////////////////////////////////////////////////////////////// //
36 public char uni2local (dchar dch) nothrow @trusted @nogc {
37 if (!Utf8DecoderFast.isValidDC(dch)) return '\x97';
38 switch (cast(uint)dch) {
39 case 0: return ' ';
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
85 default:
86 char res = uni2koi!'\x97'(dch);
87 if (res == '\x97') conwritefln!"BAD: 0x%04X"(cast(uint)dch);
88 return res;
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 {
101 string res;
102 res.reserve(utf.length);
103 Utf8DecoderFast dc;
104 foreach (char ch; utf) {
105 if (dc.decode(cast(ubyte)ch)) res ~= uni2local(dc.codepoint);
107 return res;
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;
114 Utf8DecoderFast dc;
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 () {
155 import iv.glbinds;
157 enum wrapOpt = GL_REPEAT;
158 enum filterOpt = GL_NEAREST; //GL_LINEAR;
159 enum ttype = GL_UNSIGNED_BYTE;
161 if (vArrowTextureId) glDeleteTextures(1, &vArrowTextureId);
162 vArrowTextureId = 0;
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);
201 msk <<= 1;
202 spr <<= 1;
205 //pmap = 0xff_ff0000U;
206 //pmap[0] = 0;
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;
217 dc &= 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;
224 return orb|og;
228 // ////////////////////////////////////////////////////////////////////////// //
229 private template isGoodRGBInt(T) {
230 import std.traits : Unqual;
231 alias TT = Unqual!T;
232 enum isGoodRGBInt =
233 is(TT == ubyte) ||
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 {
262 clipX0 = clipY0 = 0;
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 {
272 clipX0 += dx;
273 clipY0 += dy;
274 clipX1 -= dx;
275 clipY1 -= dy;
278 public void clipGrow (int dx, int dy) nothrow @trusted @nogc {
279 clipX0 -= dx;
280 clipY0 -= dy;
281 clipX1 += dx;
282 clipY1 += dy;
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
309 *dp++ = c&0xff; //B
310 *dp = 0;
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]; }
322 // return char width
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;
327 wdt &= 0x0f;
328 foreach (immutable dy; 1..9) {
329 ubyte b = cast(ubyte)(font6x8p.ptr[cast(uint)ch*9+dy]<<shift);
330 if (b) {
331 foreach (immutable dx; 0..wdt) {
332 if (b&0x80) gxPutPixel(x+dx, y, fg);
333 b <<= 1;
336 ++y;
342 // ////////////////////////////////////////////////////////////////////////// //
343 public int gxCharWidthScaledP (int scale, char ch) nothrow @trusted @nogc { return (scale < 1 ? 0 : font6x8p.ptr[cast(uint)ch*9]*scale); }
345 // return char width
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;
352 wdt &= 0x0f;
353 foreach (immutable dy; 1..9) {
354 ubyte b = cast(ubyte)(font6x8p.ptr[cast(uint)ch*9+dy]<<shift);
355 if (b) {
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);
362 b <<= 1;
365 y += scale;
370 // ////////////////////////////////////////////////////////////////////////// //
371 struct GxKerning {
372 char pch = 0;
373 int wdt;
374 bool wasch;
375 int scale = 1;
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;
382 if (wdt &&
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) ||
403 false
407 wdt -= scale; // lowercase ce, lowercase de
409 if (wdt && ch != 196) wdt += scale; // lowercase de
410 pch = ch;
411 wasch = true;
412 return wdt;
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 {
423 GxKerning kern;
424 foreach (char ch; s) kern.fixWidthPre(ch);
425 return kern.finalWidth;
428 // return text width
429 public int gxDrawTextP (int x, int y, const(char)[] s, uint fg) nothrow @trusted @nogc {
430 GxKerning kern;
431 foreach (char ch; s) {
432 auto ofs = kern.fixWidthPre(ch);
433 gxDrawCharP(x+ofs, y, ch, fg);
435 return kern.finalWidth;
439 // ////////////////////////////////////////////////////////////////////////// //
440 // return text width
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;
482 if (x < 0) {
483 if (x+w <= 0) return;
484 w += x;
485 x = 0;
486 assert(w > 0);
488 if (x+w > VBufWidth) {
489 w = VBufWidth-x;
490 assert(w > 0);
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;
498 if (y < 0) {
499 if (y+h <= 0) return;
500 h += y;
501 y = 0;
502 assert(h > 0);
504 if (y+h > VBufHeight) {
505 h = VBufHeight-y;
506 assert(h > 0);
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 {
530 clipX1 += 8;
531 clipY1 += 8;
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));
534 clipX1 -= 8;
535 clipY1 -= 8;
539 public void gxDrawWindow (const(char)[] title, uint framecolor, uint titlecolor, uint windowcolor) nothrow @trusted @nogc {
540 gxDrawShadow();
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);