2 import arsd
.simpledisplay
;
13 // ////////////////////////////////////////////////////////////////////////// //
14 public struct GxPoint
{
18 pure nothrow @safe @nogc:
19 this() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; } ///
20 this (int ax
, int ay
) { pragma(inline
, true); x
= ax
; y
= ay
; } ///
21 void opAssign() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; } ///
22 bool opEquals() (in auto ref GxPoint p
) const { pragma(inline
, true); return (p
.x
== x
&& p
.y
== y
); } ///
24 int opCmp() (in auto ref GxPoint p
) const {
26 if (auto d0
= y
-p
.y
) return (d0
< 0 ?
-1 : 1);
27 else if (auto d1
= x
-p
.x
) return (d1
< 0 ?
-1 : 1);
32 public struct GxRect
{
35 int width
= -1; // <0: invalid rect
36 int height
= -1; // <0: invalid rect
41 alias bottom
= y1
; ///
44 string
toString () const @trusted nothrow {
46 import core
.stdc
.stdio
: snprintf
;
48 return buf
[0..snprintf(buf
.ptr
, buf
.length
, "(%d,%d)-(%d,%d)", x0
, y0
, x0
+width
-1, y0
+height
-1)].idup
;
50 return "(invalid-rect)";
54 pure nothrow @safe @nogc:
56 this() (in auto ref GxRect rc
) { pragma(inline
, true); x0
= rc
.x0
; y0
= rc
.y0
; width
= rc
.width
; height
= rc
.height
; } ///
59 this (int ax0
, int ay0
, int awidth
, int aheight
) {
68 this() (in auto ref GxPoint xy0
, int awidth
, int aheight
) {
77 this() (in auto ref GxPoint xy0
, in auto ref GxPoint xy1
) {
81 width
= xy1
.x
-xy0
.x
+1;
82 height
= xy1
.y
-xy0
.y
+1;
85 void opAssign() (in auto ref GxRect rc
) { pragma(inline
, true); x0
= rc
.x0
; y0
= rc
.y0
; width
= rc
.width
; height
= rc
.height
; } ///
86 bool opEquals() (in auto ref GxRect rc
) const { pragma(inline
, true); return (rc
.x0
== x0
&& rc
.y0
== y0
&& rc
.width
== width
&& rc
.height
== height
); } ///
88 int opCmp() (in auto ref GxRect p
) const {
89 if (auto d0
= y0
-rc
.y0
) return (d0
< 0 ?
-1 : 1);
90 if (auto d1
= x0
-rc
.x0
) return (d1
< 0 ?
-1 : 1);
91 if (auto d2
= width
*height
-rc
.width
*rc
.height
) return (d2
< 0 ?
-1 : 1);
95 @property bool valid () const { pragma(inline
, true); return (width
>= 0 && height
>= 0); } ///
96 @property bool invalid () const { pragma(inline
, true); return (width
< 0 || height
< 0); } ///
97 @property bool empty () const { pragma(inline
, true); return (width
<= 0 || height
<= 0); } /// invalid rects are empty
99 void invalidate () { pragma(inline
, true); width
= height
= -1; } ///
101 @property GxPoint
lefttop () const { pragma(inline
, true); return GxPoint(x0
, y0
); } ///
102 @property GxPoint
righttop () const { pragma(inline
, true); return GxPoint(x0
+width
-1, y0
); } ///
103 @property GxPoint
leftbottom () const { pragma(inline
, true); return GxPoint(x0
, y0
+height
-1); } ///
104 @property GxPoint
rightbottom () const { pragma(inline
, true); return GxPoint(x0
+width
-1, y0
+height
-1); } ///
106 alias topleft
= lefttop
; ///
107 alias topright
= righttop
; ///
108 alias bottomleft
= leftbottom
; ///
109 alias bottomright
= rightbottom
; ///
111 @property int x1 () const { pragma(inline
, true); return (width
> 0 ? x0
+width
-1 : x0
-1); } ///
112 @property int y1 () const { pragma(inline
, true); return (height
> 0 ? y0
+height
-1 : y0
-1); } ///
114 @property void x1 (in int val
) { pragma(inline
, true); width
= val
-x0
+1; } ///
115 @property void y1 (in int val
) { pragma(inline
, true); height
= val
-y0
+1; } ///
118 bool inside() (in auto ref GxPoint p
) const {
119 pragma(inline
, true);
120 return (width
>= 0 && height
>= 0 ?
(p
.x
>= x0
&& p
.y
>= y0
&& p
.x
< x0
+width
&& p
.y
< y0
+height
) : false);
124 bool inside (in int ax
, in int ay
) const {
125 pragma(inline
, true);
126 return (width
>= 0 && height
>= 0 ?
(ax
>= x0
&& ay
>= y0
&& ax
< x0
+width
&& ay
< y0
+height
) : false);
129 /// is `r` inside `this`?
130 bool inside() (in auto ref GxRect r
) const {
131 pragma(inline
, true);
133 !empty
&& !r
.empty
&&
134 r
.x
>= x0
&& r
.y
>= y0
&&
135 r
.x1
<= x1
&& r
.y1
<= y1
;
138 /// is `r` and `this` overlaps?
139 bool overlap() (in auto ref GxRect r
) const {
140 pragma(inline
, true);
142 !empty
&& !r
.empty
&&
143 x
<= r
.x1
&& r
.x
<= x1
&& y
<= r
.y1
&& r
.y
<= y1
;
146 /// extend `this` so it will include `r`
147 void include() (in auto ref GxRect r
) {
148 pragma(inline
, true);
156 if (r
.x
< x0
) x0
= r
.x
;
157 if (r
.y
< y0
) y0
= r
.y
;
158 if (r
.x1
> x1
) x1
= r
.x1
;
159 if (r
.y1
> y1
) y1
= r
.y1
;
164 /// clip `this` so it will not be larger than `r`
165 bool intersect() (in auto ref GxRect r
) {
166 if (r
.invalid || invalid
) { width
= height
= -1; return false; }
167 if (r
.empty || empty
) { width
= height
= 0; return false; }
168 if (r
.y1
< y0 || r
.x1
< x0 || r
.x0
> x1 || r
.y0
> y1
) { width
= height
= 0; return false; }
169 // rc is at least partially inside this rect
170 if (x0
< r
.x0
) x0
= r
.x0
;
171 if (y0
< r
.y0
) y0
= r
.y0
;
172 if (x1
> r
.x1
) x1
= r
.x1
;
173 if (y1
> r
.y1
) y1
= r
.y1
;
174 assert(!empty
); // yeah, always
179 void shrinkBy (int dx
, int dy
) {
180 pragma(inline
, true);
181 if ((dx || dy
) && valid
) {
190 void growBy (int dx
, int dy
) {
191 pragma(inline
, true);
192 if ((dx || dy
) && valid
) {
201 void set (int ax0
, int ay0
, int awidth
, int aheight
) {
202 pragma(inline
, true);
210 void moveLeftTopBy (int dx
, int dy
) {
211 pragma(inline
, true);
218 alias moveTopLeftBy
= moveLeftTopBy
; /// ditto
221 void moveRightBottomBy (int dx
, int dy
) {
222 pragma(inline
, true);
227 alias moveBottomRightBy
= moveRightBottomBy
; /// ditto
230 void moveBy (int dx
, int dy
) {
231 pragma(inline
, true);
237 void moveTo (int nx
, int ny
) {
238 pragma(inline
, true);
244 * clip (x,y,len) stripe to this rect
247 * x = stripe start (not relative to rect)
248 * y = stripe start (not relative to rect)
249 * len = stripe length
254 * leftSkip = how much cells skipped at the left side
255 * result = false if stripe is completely clipped out
260 bool clipStripe (ref int x
, int y
, ref int len
, out int leftSkip
) const {
261 if (empty
) return false;
262 if (len
<= 0 || y
< y0 || y
>= y0
+height || x
>= x0
+width
) return false;
265 if (x
+len
<= x0
) return false;
266 len
-= (leftSkip
= x0
-x
);
269 if (x
+len
>= x0
+width
) {
272 assert(len
> 0); // yeah, always
278 bool clipStripe (ref int x
, int y
, ref int len
) const {
279 pragma(inline
, true);
281 return clipStripe(x
, y
, len
, dummy
);
286 // ////////////////////////////////////////////////////////////////////////// //
287 private import iv
.bclamp
;
289 //public enum GLTexType = GL_RGBA;
290 public enum GLTexType
= GL_BGRA
;
292 private __gshared
int VBufWidth
= 800;
293 private __gshared
int VBufHeight
= 600;
295 public __gshared
uint* vglTexBuf
= null; // OpenGL texture buffer
296 public __gshared
uint vglTexId
= 0;
299 public @property int winWidth () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
; }
300 public @property int winHeight () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
; }
303 // ////////////////////////////////////////////////////////////////////////// //
304 public void gxResize (int wdt
, int hgt
) nothrow @trusted {
305 if (wdt
< 1) wdt
= 1;
306 if (hgt
< 1) hgt
= 1;
307 if (wdt
> 16384) wdt
= 16384;
308 if (hgt
> 16384) hgt
= 16384;
309 if (vglTexBuf
is null || wdt
!= VBufWidth || hgt
!= VBufHeight || vglTexId
== 0) {
310 import core
.stdc
.stdlib
: realloc
;
313 vglTexBuf
= cast(uint*)realloc(vglTexBuf
, wdt
*hgt
*vglTexBuf
[0].sizeof
);
314 if (vglTexBuf
is null) assert(0, "VGL: out of memory");
315 vglTexBuf
[0..wdt
*hgt
] = 0;
317 if (gxRebuildScreenCB
!is null) {
321 } catch (Exception e
) {
322 conwriteln("SCREEN REBUILD ERROR: ", e
.msg
);
326 enum wrapOpt
= GL_REPEAT
;
327 enum filterOpt
= GL_NEAREST
; //GL_LINEAR;
328 enum ttype
= GL_UNSIGNED_BYTE
;
330 if (vglTexId
) glDeleteTextures(1, &vglTexId
);
332 glGenTextures(1, &vglTexId
);
333 if (vglTexId
== 0) assert(0, "VGL: can't create screen texture");
335 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
336 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
337 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
338 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
339 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
341 //GLfloat[4] bclr = 0.0;
342 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
344 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, VBufWidth
, VBufHeight
, 0, GLTexType
, GL_UNSIGNED_BYTE
, vglTexBuf
);
349 public void gxUpdateTexture () nothrow @trusted @nogc {
351 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
352 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0/*x*/, 0/*y*/, VBufWidth
, VBufHeight
, GLTexType
, GL_UNSIGNED_BYTE
, vglTexBuf
);
353 //glBindTexture(GL_TEXTURE_2D, 0);
358 public void gxBlitTexture () nothrow @trusted @nogc {
359 if (!vglTexId
) return;
361 glMatrixMode(GL_PROJECTION
); // for ortho camera
363 glViewport(0, 0, VBufWidth
, VBufHeight
);
364 glOrtho(0, VBufWidth
, VBufHeight
, 0, -1, 1); // top-to-bottom
365 glMatrixMode(GL_MODELVIEW
);
368 glEnable(GL_TEXTURE_2D
);
369 glDisable(GL_LIGHTING
);
370 glDisable(GL_DITHER
);
371 glDisable(GL_DEPTH_TEST
);
373 //glDisable(GL_STENCIL_TEST);
375 immutable w
= VBufWidth
;
376 immutable h
= VBufHeight
;
378 glColor4f(1, 1, 1, 1);
379 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
380 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
382 glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); // top-left
383 glTexCoord2f(1.0f, 0.0f); glVertex2i(w
, 0); // top-right
384 glTexCoord2f(1.0f, 1.0f); glVertex2i(w
, h
); // bottom-right
385 glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h
); // bottom-left
390 // ////////////////////////////////////////////////////////////////////////// //
391 // mix dc with ARGB (or ABGR) col; dc A is ignored (removed)
392 public uint gxColMix (uint dc
, uint col
) pure nothrow @trusted @nogc {
393 pragma(inline
, true);
394 immutable uint a
= 256-(col
>>24); // to not loose bits
395 //immutable uint dc = (da)&0xffffff;
397 immutable uint srb
= (col
&0xff00ff);
398 immutable uint sg
= (col
&0x00ff00);
399 immutable uint drb
= (dc
&0xff00ff);
400 immutable uint dg
= (dc
&0x00ff00);
401 immutable uint orb
= (drb
+(((srb
-drb
)*a
+0x800080)>>8))&0xff00ff;
402 immutable uint og
= (dg
+(((sg
-dg
)*a
+0x008000)>>8))&0x00ff00;
407 // ////////////////////////////////////////////////////////////////////////// //
408 private template isGoodRGBInt(T
) {
409 import std
.traits
: Unqual
;
413 is(TT
== short) ||
is(TT
== ushort) ||
414 is(TT
== int) ||
is(TT
== uint) ||
415 is(TT
== long) ||
is(TT
== ulong);
419 // ////////////////////////////////////////////////////////////////////////// //
420 public uint gxrgb(T0
, T1
, T2
) (T0 r
, T1 g
, T2 b
) pure nothrow @trusted @nogc if (isGoodRGBInt
!T0
&& isGoodRGBInt
!T1
&& isGoodRGBInt
!T2
) {
421 pragma(inline
, true);
422 return (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
426 public template gxRGB(int r
, int g
, int b
) {
427 enum gxRGB
= (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
430 public template gxRGBA(int r
, int g
, int b
, int a
) {
431 enum gxRGBA
= (clampToByte(a
)<<24)|
(clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
435 // ////////////////////////////////////////////////////////////////////////// //
436 public __gshared GxRect gxClipRect
= GxRect(0, 0, 65535, 65535);
438 private struct GxClipSave
{
440 ~this () const nothrow @trusted @nogc {
441 pragma(inline
, true);
446 public GxClipSave
gxClipSave () nothrow @trusted @nogc {
447 pragma(inline
, true);
448 return GxClipSave(gxClipRect
);
451 public void gxClipRestore() (in auto ref GxClipSave cs
) nothrow @trusted @nogc {
452 pragma(inline
, true);
456 public void gxClipReset () nothrow @trusted @nogc {
457 pragma(inline
, true);
458 gxClipRect
= GxRect(0, 0, VBufWidth
, VBufHeight
);
462 // ////////////////////////////////////////////////////////////////////////// //
463 public void gxClearScreen (uint clr
) nothrow @trusted @nogc {
464 pragma(inline
, true);
465 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = clr
;
469 public void gxPutPixel (int x
, int y
, uint c
) nothrow @trusted @nogc {
470 pragma(inline
, true);
471 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(x
, y
)) {
472 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
;
473 *dp
= gxColMix(*dp
, c
);
478 public void gxPutPixel() (in auto ref GxPoint p
, uint c
) nothrow @trusted @nogc {
479 pragma(inline
, true);
480 if (p
.x
>= 0 && p
.y
>= 0 && p
.x
< VBufWidth
&& p
.y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(p
)) {
481 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+p
.y
*VBufWidth
+p
.x
;
482 *dp
= gxColMix(*dp
, c
);
487 // ////////////////////////////////////////////////////////////////////////// //
488 public void gxHLine (int x
, int y
, int w
, uint clr
) nothrow @trusted @nogc {
489 if (w
< 1 || y
< 0 || y
>= VBufHeight || x
>= VBufWidth
) return;
491 if (x
+w
<= 0) return;
496 if (x
+w
> VBufWidth
) {
500 while (w
-- > 0) gxPutPixel(x
++, y
, clr
);
503 public void gxHLine() (in auto ref GxPoint p
, int w
, uint clr
) nothrow @trusted @nogc { gxHLine(p
.x
, p
.y
, w
, clr
); }
505 public void gxVLine (int x
, int y
, int h
, uint clr
) nothrow @trusted @nogc {
506 if (h
< 1 || x
< 0 || x
>= VBufWidth || y
>= VBufHeight
) return;
508 if (y
+h
<= 0) return;
513 if (y
+h
> VBufHeight
) {
517 while (h
-- > 0) gxPutPixel(x
, y
++, clr
);
520 public void gxVLine() (in auto ref GxPoint p
, int h
, uint clr
) nothrow @trusted @nogc { gxVLine(p
.x
, p
.y
, h
, clr
); }
522 public void gxFillRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
523 if (w
< 1 || h
< 1 || x
>= VBufWidth || y
>= VBufHeight
) return;
524 while (h
-- > 0) gxHLine(x
, y
++, w
, clr
);
527 public void gxFillRect() (in auto ref GxRect rc
, uint clr
) nothrow @trusted @nogc {
528 gxFillRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
531 public void gxDrawRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
532 if (w
< 1 || h
< 1 || x
>= VBufWidth || y
>= VBufHeight
) return;
533 gxHLine(x
, y
, w
, clr
);
534 gxHLine(x
, y
+h
-1, w
, clr
);
535 gxVLine(x
, y
+1, h
-2, clr
);
536 gxVLine(x
+w
-1, y
+1, h
-2, clr
);
539 public void gxDrawRect() (in auto ref GxRect rc
, uint clr
) nothrow @trusted @nogc {
540 gxDrawRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
544 // ////////////////////////////////////////////////////////////////////////// //
545 public __gshared
void delegate () gxRebuildScreenCB
;
547 public void gxRebuildScreen () nothrow {
548 if (gxRebuildScreenCB
!is null) {
552 } catch (Exception e
) {
553 conwriteln("SCREEN REBUILD ERROR: ", e
.msg
);
560 // ////////////////////////////////////////////////////////////////////////// //
561 __gshared string ttffilename
= "~/ttf/ms/verdana.ttf";
562 __gshared
ubyte* ttfontdata
;
563 __gshared
uint ttfontdatasize
;
565 __gshared FT_Library ttflibrary
;
566 __gshared FTC_Manager ttfcache
;
567 __gshared FTC_CMapCache ttfcachecmap
;
568 __gshared FTC_ImageCache ttfcacheimage
;
570 shared static ~this () {
571 conwriteln("closing...");
574 conwriteln("freeing cache...");
575 //FTC_Manager_Reset(ttfcache);
576 FTC_Manager_Done(ttfcache
);
579 conwriteln("closing library...");
580 FT_Done_FreeType(ttflibrary
);
586 enum FontID
= cast(FTC_FaceID
)1;
589 void ttfFontFinalizer (void* obj
) {
590 import core
.stdc
.stdlib
: free
;
591 if (obj
is null) return;
592 auto tf
= cast(FT_Face
)obj
;
593 if (tf
.generic
.data
!is ttfontdata
) return;
594 if (ttfontdata
!is null) {
595 conwriteln("TTF CACHE: freeing loaded font...");
602 FT_Error
ttfFontLoader (FTC_FaceID face_id
, FT_Library library
, FT_Pointer request_data
, FT_Face
* aface
) {
603 if (face_id
== FontID
) {
605 if (ttfontdata
is null) {
606 conwriteln("TTF CACHE: loading '", ttffilename
, "'...");
607 import core
.stdc
.stdlib
: malloc
;
608 auto fl
= VFile(ttffilename
);
610 if (fsz
< 16 || fsz
> int.max
/8) throw new Exception("invalid ttf size");
611 ttfontdatasize
= cast(uint)fsz
;
612 ttfontdata
= cast(ubyte*)malloc(ttfontdatasize
);
613 if (ttfontdata
is null) assert(0, "out of memory");
614 fl
.rawReadExact(ttfontdata
[0..ttfontdatasize
]);
616 auto res
= FT_New_Memory_Face(library
, cast(const(FT_Byte
)*)ttfontdata
, ttfontdatasize
, 0, aface
);
617 if (res
!= 0) throw new Exception("error loading ttf: '"~ttffilename
~"'");
618 (*aface
).generic
.data
= ttfontdata
;
619 (*aface
).generic
.finalizer
= &ttfFontFinalizer
;
620 } catch (Exception e
) {
621 if (ttfontdata
!is null) {
622 import core
.stdc
.stdlib
: free
;
627 conwriteln("ERROR loading font: ", e
.msg
);
628 return FT_Err_Cannot_Open_Resource
;
632 conwriteln("TTF CACHE: invalid font id");
634 return FT_Err_Cannot_Open_Resource
;
639 if (FT_Init_FreeType(&ttflibrary
)) assert(0, "can't initialize FreeType");
640 if (FTC_Manager_New(ttflibrary
, 0, 0, 0, &ttfFontLoader
, null, &ttfcache
)) assert(0, "can't initialize FreeType cache manager");
641 if (FTC_CMapCache_New(ttfcache
, &ttfcachecmap
)) assert(0, "can't initialize FreeType cache manager");
642 if (FTC_ImageCache_New(ttfcache
, &ttfcacheimage
)) assert(0, "can't initialize FreeType cache manager");
643 conwriteln("TTF CACHE initialized.");
647 float ttfGetPixelHeightScale (FT_Face ttfont
, float size
) {
648 pragma(inline
, true);
649 return size
/(ttfont
.ascender
-ttfont
.descender
);
653 // returns advance to next x
654 int ttfRenderGlyphBitmap(bool mono
=true) (FTC_FaceID ttfontid
, int x
, int y
, int codepoint
, int size
, uint clr
, int prevcp
=-1) {
655 import core
.stdc
.stdlib
: malloc
;
657 int glyph
= FTC_CMapCache_Lookup(ttfcachecmap
, ttfontid
, -1, codepoint
);
658 if (glyph
== 0) return -666;
662 int glp
= FTC_CMapCache_Lookup(ttfcachecmap
, ttfontid
, -1, prevcp
);
665 //if (FTC_Manager_LookupFace(ttfcache, ttfontid, &ttface)) return -666;
668 fsc
.face_id
= ttfontid
;
671 fsc
.pixel
= 1; // size in pixels
674 if (FTC_Manager_LookupSize(ttfcache
, &fsc
, &ttfontsz
)) return -666;
675 ttface
= ttfontsz
.face
;
676 conwriteln("INTERLINE: ", ttfontsz
.metrics
.height
>>6);
678 if (FT_HAS_KERNING(ttface
)) {
680 if (FT_Get_Kerning(ttface
, glp
, glyph
, FT_KERNING_UNSCALED
, &kk
) == 0) {
681 auto kadvfrac
= FT_MulFix(kk
.x
, ttfontsz
.metrics
.x_scale
); // 1/64 of pixel
682 kadv
= cast(int)(kadvfrac
+(kadvfrac
< 0 ?
-32 : 32)>>6);
684 conwriteln("kerning: prevcp=", prevcp
, "; codepoint=", codepoint
, "; kk.x=", kk
.x
, "; kadv=", kadv
);
686 gxVLine(x
, y
-50, 50, gxRGB
!(0, 255, 0));
687 gxVLine(x
+kadv
, y
-50, 50, gxRGB
!(255, 0, 0));
695 static if (mono
) enum exflags
= FT_LOAD_MONOCHROME
/*|FT_LOAD_NO_AUTOHINT*/; else enum exflags
= 0/*|FT_LOAD_NO_AUTOHINT*/;
697 FTC_ImageTypeRec fimg
;
698 fimg
.face_id
= ttfontid
;
701 fimg
.flags
= FT_LOAD_RENDER|exflags
;
704 if (FTC_ImageCache_Lookup(ttfcacheimage
, &fimg
, glyph
, &fg
, null)) return -666;
705 if (fg
.format
!= FT_GLYPH_FORMAT_BITMAP
) return -666;
706 FT_BitmapGlyph fgi
= cast(FT_BitmapGlyph
)fg
;
708 auto adv
= fg
.advance
.x
>>16;
711 auto src
= fgi
.bitmap
.buffer
;
712 auto spt
= fgi
.bitmap
.pitch
;
713 if (spt
< 0) spt
= -spt
;
714 int x0
= x
+fgi
.left
+kadv
;
717 foreach (immutable int dy
; 0..fgi
.bitmap
.rows
) {
718 ubyte count
= 0, b
= 0;
720 foreach (immutable int dx
; 0..fgi
.bitmap
.width
) {
721 if (count
-- == 0) { count
= 7; b
= *s
++; } else b
<<= 1;
722 if (b
&0x80) gxPutPixel(x0
+dx
, y0
+dy
, clr
);
727 foreach (immutable int dy
; 0..fgi
.bitmap
.rows
) {
729 foreach (immutable int dx
; 0..fgi
.bitmap
.width
) {
730 immutable ubyte b
= *s
++;
731 if (b
&0x80) gxPutPixel(x0
+dx
, y0
+dy
, (clr
&0xff_ff_ff
)|
((255-b
)<<24));
741 // ////////////////////////////////////////////////////////////////////////// //
742 void ttfDrawTextUtf (int x
, int y
, const(char)[] str, uint fg
) {
745 enum FontHeight
= 14;
746 //immutable float scale = ttfGetPixelHeightScale(ttfont, FontHeight);
747 foreach (char ch
; str) {
748 if (ud
.decode(cast(ubyte)ch
)) {
749 int dc
= (ud
.complete ? ud
.codepoint
: ud
.replacement
);
750 int adv
= ttfRenderGlyphBitmap
!true(FontID
, x
, y
, dc
, FontHeight
, fg
, prevcp
);
760 // ////////////////////////////////////////////////////////////////////////// //
761 void main (string
[] args
) {
762 conRegVar
!ttffilename("ttf_file", "ttf font file name");
764 sdpyWindowClass
= "SDPY WINDOW";
765 glconShowKey
= "M-Grave";
767 conProcessQueue(); // load config
768 conProcessArgs
!true(args
);
772 gxRebuildScreenCB
= delegate () {
773 gxClearScreen(gxRGB
!(0, 0, 0));
774 gxDrawRect(10, 10, VBufWidth
-20, VBufHeight
-20, gxRGB
!(255, 127, 0));
775 ttfDrawTextUtf(100, 100, "Hello, пизда!", gxRGB
!(255, 255, 0));
778 auto sdwin
= new SimpleWindow(VBufWidth
, VBufHeight
, "My D App", OpenGlOptions
.yes
, Resizablity
.allowResizing
);
779 glconMainWindow
= sdwin
;
780 //sdwin.hideCursor();
782 static if (is(typeof(&sdwin
.closeQuery
))) {
783 sdwin
.closeQuery
= delegate () { concmd("quit"); glconPostDoConCommands(); };
786 sdwin
.addEventListener((GLConScreenRebuildEvent evt
) {
787 if (sdwin
.closed
) return;
788 if (isQuitRequested
) { sdwin
.close(); return; }
790 sdwin
.redrawOpenGlSceneNow();
793 sdwin
.addEventListener((GLConScreenRepaintEvent evt
) {
794 if (sdwin
.closed
) return;
795 if (isQuitRequested
) { sdwin
.close(); return; }
796 sdwin
.redrawOpenGlSceneNow();
799 sdwin
.addEventListener((GLConDoConsoleCommandsEvent evt
) {
800 bool sendAnother
= false;
801 bool prevVisible
= isConsoleVisible
;
804 scope(exit
) consoleUnlock();
806 sendAnother
= !conQueueEmpty();
808 if (sdwin
.closed
) return;
809 if (isQuitRequested
) { sdwin
.close(); return; }
810 if (sendAnother
) glconPostDoConCommands();
811 if (prevVisible || isConsoleVisible
) glconPostScreenRepaintDelayed();
815 sdwin
.windowResized
= delegate (int wdt
, int hgt
) {
816 if (sdwin
.closed
) return;
817 glconResize(wdt
, hgt
);
819 //glconPostScreenRebuild();
823 sdwin
.redrawOpenGlScene
= delegate () {
824 if (sdwin
.closed
) return;
828 scope(exit
) consoleUnlock();
829 if (!conQueueEmpty()) glconPostDoConCommands();
834 glClearColor(0, 0, 0, 0);
835 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_ACCUM_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
836 glViewport(0, 0, sdwin.width, sdwin.height);
841 //if (isQuitRequested()) sdwin.glconPostEvent(new QuitEvent());
844 sdwin
.visibleForTheFirstTime
= delegate () {
845 sdwin
.setAsCurrentOpenGlContext();
846 glconInit(sdwin
.width
, sdwin
.height
);
847 gxResize(sdwin
.width
, sdwin
.height
);
849 sdwin
.redrawOpenGlSceneNow();
854 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
855 if (sdwin
.closed
) return;
856 if (isQuitRequested
) { sdwin
.close(); return; }
858 delegate (KeyEvent event
) {
859 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
860 if (sdwin
.closed
) return;
861 if (isQuitRequested
) { sdwin
.close(); return; }
862 if (glconKeyEvent(event
)) { glconPostScreenRepaint(); return; }
863 if (event
.pressed
&& event
== "Escape") { concmd("quit"); return; }
865 delegate (MouseEvent event
) {
866 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
867 if (sdwin
.closed
) return;
869 delegate (dchar ch
) {
870 if (sdwin
.closed
) return;
871 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
872 if (glconCharEvent(ch
)) { glconPostScreenRepaint(); return; }
876 conProcessQueue(int.max
/4);