2 import arsd
.simpledisplay
;
9 // ////////////////////////////////////////////////////////////////////////// //
10 public struct GxPoint
{
14 pure nothrow @safe @nogc:
15 this() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; } ///
16 this (int ax
, int ay
) { pragma(inline
, true); x
= ax
; y
= ay
; } ///
17 void opAssign() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; } ///
18 bool opEquals() (in auto ref GxPoint p
) const { pragma(inline
, true); return (p
.x
== x
&& p
.y
== y
); } ///
20 int opCmp() (in auto ref GxPoint p
) const {
22 if (auto d0
= y
-p
.y
) return (d0
< 0 ?
-1 : 1);
23 else if (auto d1
= x
-p
.x
) return (d1
< 0 ?
-1 : 1);
28 public struct GxRect
{
31 int width
= -1; // <0: invalid rect
32 int height
= -1; // <0: invalid rect
37 alias bottom
= y1
; ///
40 string
toString () const @trusted nothrow {
42 import core
.stdc
.stdio
: snprintf
;
44 return buf
[0..snprintf(buf
.ptr
, buf
.length
, "(%d,%d)-(%d,%d)", x0
, y0
, x0
+width
-1, y0
+height
-1)].idup
;
46 return "(invalid-rect)";
50 pure nothrow @safe @nogc:
52 this() (in auto ref GxRect rc
) { pragma(inline
, true); x0
= rc
.x0
; y0
= rc
.y0
; width
= rc
.width
; height
= rc
.height
; } ///
55 this (int ax0
, int ay0
, int awidth
, int aheight
) {
64 this() (in auto ref GxPoint xy0
, int awidth
, int aheight
) {
73 this() (in auto ref GxPoint xy0
, in auto ref GxPoint xy1
) {
77 width
= xy1
.x
-xy0
.x
+1;
78 height
= xy1
.y
-xy0
.y
+1;
81 void opAssign() (in auto ref GxRect rc
) { pragma(inline
, true); x0
= rc
.x0
; y0
= rc
.y0
; width
= rc
.width
; height
= rc
.height
; } ///
82 bool opEquals() (in auto ref GxRect rc
) const { pragma(inline
, true); return (rc
.x0
== x0
&& rc
.y0
== y0
&& rc
.width
== width
&& rc
.height
== height
); } ///
84 int opCmp() (in auto ref GxRect p
) const {
85 if (auto d0
= y0
-rc
.y0
) return (d0
< 0 ?
-1 : 1);
86 if (auto d1
= x0
-rc
.x0
) return (d1
< 0 ?
-1 : 1);
87 if (auto d2
= width
*height
-rc
.width
*rc
.height
) return (d2
< 0 ?
-1 : 1);
91 @property bool valid () const { pragma(inline
, true); return (width
>= 0 && height
>= 0); } ///
92 @property bool invalid () const { pragma(inline
, true); return (width
< 0 || height
< 0); } ///
93 @property bool empty () const { pragma(inline
, true); return (width
<= 0 || height
<= 0); } /// invalid rects are empty
95 void invalidate () { pragma(inline
, true); width
= height
= -1; } ///
97 @property GxPoint
lefttop () const { pragma(inline
, true); return GxPoint(x0
, y0
); } ///
98 @property GxPoint
righttop () const { pragma(inline
, true); return GxPoint(x0
+width
-1, y0
); } ///
99 @property GxPoint
leftbottom () const { pragma(inline
, true); return GxPoint(x0
, y0
+height
-1); } ///
100 @property GxPoint
rightbottom () const { pragma(inline
, true); return GxPoint(x0
+width
-1, y0
+height
-1); } ///
102 alias topleft
= lefttop
; ///
103 alias topright
= righttop
; ///
104 alias bottomleft
= leftbottom
; ///
105 alias bottomright
= rightbottom
; ///
107 @property int x1 () const { pragma(inline
, true); return (width
> 0 ? x0
+width
-1 : x0
-1); } ///
108 @property int y1 () const { pragma(inline
, true); return (height
> 0 ? y0
+height
-1 : y0
-1); } ///
110 @property void x1 (in int val
) { pragma(inline
, true); width
= val
-x0
+1; } ///
111 @property void y1 (in int val
) { pragma(inline
, true); height
= val
-y0
+1; } ///
114 bool inside() (in auto ref GxPoint p
) const {
115 pragma(inline
, true);
116 return (width
>= 0 && height
>= 0 ?
(p
.x
>= x0
&& p
.y
>= y0
&& p
.x
< x0
+width
&& p
.y
< y0
+height
) : false);
120 bool inside (in int ax
, in int ay
) const {
121 pragma(inline
, true);
122 return (width
>= 0 && height
>= 0 ?
(ax
>= x0
&& ay
>= y0
&& ax
< x0
+width
&& ay
< y0
+height
) : false);
125 /// is `r` inside `this`?
126 bool inside() (in auto ref GxRect r
) const {
127 pragma(inline
, true);
129 !empty
&& !r
.empty
&&
130 r
.x
>= x0
&& r
.y
>= y0
&&
131 r
.x1
<= x1
&& r
.y1
<= y1
;
134 /// is `r` and `this` overlaps?
135 bool overlap() (in auto ref GxRect r
) const {
136 pragma(inline
, true);
138 !empty
&& !r
.empty
&&
139 x
<= r
.x1
&& r
.x
<= x1
&& y
<= r
.y1
&& r
.y
<= y1
;
142 /// extend `this` so it will include `r`
143 void include() (in auto ref GxRect r
) {
144 pragma(inline
, true);
152 if (r
.x
< x0
) x0
= r
.x
;
153 if (r
.y
< y0
) y0
= r
.y
;
154 if (r
.x1
> x1
) x1
= r
.x1
;
155 if (r
.y1
> y1
) y1
= r
.y1
;
160 /// clip `this` so it will not be larger than `r`
161 bool intersect() (in auto ref GxRect r
) {
162 if (r
.invalid || invalid
) { width
= height
= -1; return false; }
163 if (r
.empty || empty
) { width
= height
= 0; return false; }
164 if (r
.y1
< y0 || r
.x1
< x0 || r
.x0
> x1 || r
.y0
> y1
) { width
= height
= 0; return false; }
165 // rc is at least partially inside this rect
166 if (x0
< r
.x0
) x0
= r
.x0
;
167 if (y0
< r
.y0
) y0
= r
.y0
;
168 if (x1
> r
.x1
) x1
= r
.x1
;
169 if (y1
> r
.y1
) y1
= r
.y1
;
170 assert(!empty
); // yeah, always
175 void shrinkBy (int dx
, int dy
) {
176 pragma(inline
, true);
177 if ((dx || dy
) && valid
) {
186 void growBy (int dx
, int dy
) {
187 pragma(inline
, true);
188 if ((dx || dy
) && valid
) {
197 void set (int ax0
, int ay0
, int awidth
, int aheight
) {
198 pragma(inline
, true);
206 void moveLeftTopBy (int dx
, int dy
) {
207 pragma(inline
, true);
214 alias moveTopLeftBy
= moveLeftTopBy
; /// ditto
217 void moveRightBottomBy (int dx
, int dy
) {
218 pragma(inline
, true);
223 alias moveBottomRightBy
= moveRightBottomBy
; /// ditto
226 void moveBy (int dx
, int dy
) {
227 pragma(inline
, true);
233 void moveTo (int nx
, int ny
) {
234 pragma(inline
, true);
240 * clip (x,y,len) stripe to this rect
243 * x = stripe start (not relative to rect)
244 * y = stripe start (not relative to rect)
245 * len = stripe length
250 * leftSkip = how much cells skipped at the left side
251 * result = false if stripe is completely clipped out
256 bool clipStripe (ref int x
, int y
, ref int len
, out int leftSkip
) const {
257 if (empty
) return false;
258 if (len
<= 0 || y
< y0 || y
>= y0
+height || x
>= x0
+width
) return false;
261 if (x
+len
<= x0
) return false;
262 len
-= (leftSkip
= x0
-x
);
265 if (x
+len
>= x0
+width
) {
268 assert(len
> 0); // yeah, always
274 bool clipStripe (ref int x
, int y
, ref int len
) const {
275 pragma(inline
, true);
277 return clipStripe(x
, y
, len
, dummy
);
282 // ////////////////////////////////////////////////////////////////////////// //
283 private import iv
.bclamp
;
285 //public enum GLTexType = GL_RGBA;
286 public enum GLTexType
= GL_BGRA
;
288 private __gshared
int VBufWidth
= 800;
289 private __gshared
int VBufHeight
= 600;
291 public __gshared
uint* vglTexBuf
= null; // OpenGL texture buffer
292 public __gshared
uint vglTexId
= 0;
295 public @property int winWidth () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
; }
296 public @property int winHeight () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
; }
299 // ////////////////////////////////////////////////////////////////////////// //
300 public void gxResize (int wdt
, int hgt
) nothrow @trusted {
301 if (wdt
< 1) wdt
= 1;
302 if (hgt
< 1) hgt
= 1;
303 if (wdt
> 16384) wdt
= 16384;
304 if (hgt
> 16384) hgt
= 16384;
305 if (vglTexBuf
is null || wdt
!= VBufWidth || hgt
!= VBufHeight || vglTexId
== 0) {
306 import core
.stdc
.stdlib
: realloc
;
309 vglTexBuf
= cast(uint*)realloc(vglTexBuf
, wdt
*hgt
*vglTexBuf
[0].sizeof
);
310 if (vglTexBuf
is null) assert(0, "VGL: out of memory");
311 vglTexBuf
[0..wdt
*hgt
] = 0;
313 if (gxRebuildScreenCB
!is null) {
317 } catch (Exception e
) {
318 conwriteln("SCREEN REBUILD ERROR: ", e
.msg
);
322 enum wrapOpt
= GL_REPEAT
;
323 enum filterOpt
= GL_NEAREST
; //GL_LINEAR;
324 enum ttype
= GL_UNSIGNED_BYTE
;
326 if (vglTexId
) glDeleteTextures(1, &vglTexId
);
328 glGenTextures(1, &vglTexId
);
329 if (vglTexId
== 0) assert(0, "VGL: can't create screen texture");
331 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
332 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrapOpt
);
333 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrapOpt
);
334 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, filterOpt
);
335 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, filterOpt
);
337 //GLfloat[4] bclr = 0.0;
338 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
340 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, VBufWidth
, VBufHeight
, 0, GLTexType
, GL_UNSIGNED_BYTE
, vglTexBuf
);
345 public void gxUpdateTexture () nothrow @trusted @nogc {
347 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
348 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0/*x*/, 0/*y*/, VBufWidth
, VBufHeight
, GLTexType
, GL_UNSIGNED_BYTE
, vglTexBuf
);
349 //glBindTexture(GL_TEXTURE_2D, 0);
354 public void gxBlitTexture () nothrow @trusted @nogc {
355 if (!vglTexId
) return;
357 glMatrixMode(GL_PROJECTION
); // for ortho camera
359 glViewport(0, 0, VBufWidth
, VBufHeight
);
360 glOrtho(0, VBufWidth
, VBufHeight
, 0, -1, 1); // top-to-bottom
361 glMatrixMode(GL_MODELVIEW
);
364 glEnable(GL_TEXTURE_2D
);
365 glDisable(GL_LIGHTING
);
366 glDisable(GL_DITHER
);
367 glDisable(GL_DEPTH_TEST
);
369 //glDisable(GL_STENCIL_TEST);
371 immutable w
= VBufWidth
;
372 immutable h
= VBufHeight
;
374 glColor4f(1, 1, 1, 1);
375 glBindTexture(GL_TEXTURE_2D
, vglTexId
);
376 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
378 glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); // top-left
379 glTexCoord2f(1.0f, 0.0f); glVertex2i(w
, 0); // top-right
380 glTexCoord2f(1.0f, 1.0f); glVertex2i(w
, h
); // bottom-right
381 glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h
); // bottom-left
386 // ////////////////////////////////////////////////////////////////////////// //
387 // mix dc with ARGB (or ABGR) col; dc A is ignored (removed)
388 public uint gxColMix (uint dc
, uint col
) pure nothrow @trusted @nogc {
389 pragma(inline
, true);
390 immutable uint a
= 256-(col
>>24); // to not loose bits
391 //immutable uint dc = (da)&0xffffff;
393 immutable uint srb
= (col
&0xff00ff);
394 immutable uint sg
= (col
&0x00ff00);
395 immutable uint drb
= (dc
&0xff00ff);
396 immutable uint dg
= (dc
&0x00ff00);
397 immutable uint orb
= (drb
+(((srb
-drb
)*a
+0x800080)>>8))&0xff00ff;
398 immutable uint og
= (dg
+(((sg
-dg
)*a
+0x008000)>>8))&0x00ff00;
403 // ////////////////////////////////////////////////////////////////////////// //
404 private template isGoodRGBInt(T
) {
405 import std
.traits
: Unqual
;
409 is(TT
== short) ||
is(TT
== ushort) ||
410 is(TT
== int) ||
is(TT
== uint) ||
411 is(TT
== long) ||
is(TT
== ulong);
415 // ////////////////////////////////////////////////////////////////////////// //
416 public uint gxrgb(T0
, T1
, T2
) (T0 r
, T1 g
, T2 b
) pure nothrow @trusted @nogc if (isGoodRGBInt
!T0
&& isGoodRGBInt
!T1
&& isGoodRGBInt
!T2
) {
417 pragma(inline
, true);
418 return (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
422 public template gxRGB(int r
, int g
, int b
) {
423 enum gxRGB
= (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
426 public template gxRGBA(int r
, int g
, int b
, int a
) {
427 enum gxRGBA
= (clampToByte(a
)<<24)|
(clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
431 // ////////////////////////////////////////////////////////////////////////// //
432 public __gshared GxRect gxClipRect
= GxRect(0, 0, 65535, 65535);
434 private struct GxClipSave
{
436 ~this () const nothrow @trusted @nogc {
437 pragma(inline
, true);
442 public GxClipSave
gxClipSave () nothrow @trusted @nogc {
443 pragma(inline
, true);
444 return GxClipSave(gxClipRect
);
447 public void gxClipRestore() (in auto ref GxClipSave cs
) nothrow @trusted @nogc {
448 pragma(inline
, true);
452 public void gxClipReset () nothrow @trusted @nogc {
453 pragma(inline
, true);
454 gxClipRect
= GxRect(0, 0, VBufWidth
, VBufHeight
);
458 // ////////////////////////////////////////////////////////////////////////// //
459 public void gxClearScreen (uint clr
) nothrow @trusted @nogc {
460 pragma(inline
, true);
461 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = clr
;
465 public void gxPutPixel (int x
, int y
, uint c
) nothrow @trusted @nogc {
466 pragma(inline
, true);
467 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(x
, y
)) {
468 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
;
469 *dp
= gxColMix(*dp
, c
);
474 public void gxPutPixel() (in auto ref GxPoint p
, uint c
) nothrow @trusted @nogc {
475 pragma(inline
, true);
476 if (p
.x
>= 0 && p
.y
>= 0 && p
.x
< VBufWidth
&& p
.y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(p
)) {
477 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+p
.y
*VBufWidth
+p
.x
;
478 *dp
= gxColMix(*dp
, c
);
483 // ////////////////////////////////////////////////////////////////////////// //
484 public void gxHLine (int x
, int y
, int w
, uint clr
) nothrow @trusted @nogc {
485 if (w
< 1 || y
< 0 || y
>= VBufHeight || x
>= VBufWidth
) return;
487 if (x
+w
<= 0) return;
492 if (x
+w
> VBufWidth
) {
496 while (w
-- > 0) gxPutPixel(x
++, y
, clr
);
499 public void gxHLine() (in auto ref GxPoint p
, int w
, uint clr
) nothrow @trusted @nogc { gxHLine(p
.x
, p
.y
, w
, clr
); }
501 public void gxVLine (int x
, int y
, int h
, uint clr
) nothrow @trusted @nogc {
502 if (h
< 1 || x
< 0 || x
>= VBufWidth || y
>= VBufHeight
) return;
504 if (y
+h
<= 0) return;
509 if (y
+h
> VBufHeight
) {
513 while (h
-- > 0) gxPutPixel(x
, y
++, clr
);
516 public void gxVLine() (in auto ref GxPoint p
, int h
, uint clr
) nothrow @trusted @nogc { gxVLine(p
.x
, p
.y
, h
, clr
); }
518 public void gxFillRect (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 while (h
-- > 0) gxHLine(x
, y
++, w
, clr
);
523 public void gxFillRect() (in auto ref GxRect rc
, uint clr
) nothrow @trusted @nogc {
524 gxFillRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
527 public void gxDrawRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
528 if (w
< 1 || h
< 1 || x
>= VBufWidth || y
>= VBufHeight
) return;
529 gxHLine(x
, y
, w
, clr
);
530 gxHLine(x
, y
+h
-1, w
, clr
);
531 gxVLine(x
, y
+1, h
-2, clr
);
532 gxVLine(x
+w
-1, y
+1, h
-2, clr
);
535 public void gxDrawRect() (in auto ref GxRect rc
, uint clr
) nothrow @trusted @nogc {
536 gxDrawRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
540 // ////////////////////////////////////////////////////////////////////////// //
541 public __gshared
void delegate () gxRebuildScreenCB
;
543 public void gxRebuildScreen () nothrow {
544 if (gxRebuildScreenCB
!is null) {
548 } catch (Exception e
) {
549 conwriteln("SCREEN REBUILD ERROR: ", e
.msg
);
556 // ////////////////////////////////////////////////////////////////////////// //
557 void main (string
[] args
) {
558 glconShowKey
= "M-Grave";
560 conProcessQueue(); // load config
561 conProcessArgs
!true(args
);
563 gxRebuildScreenCB
= delegate () {
564 gxClearScreen(gxRGB
!(0, 0, 0));
565 gxDrawRect(10, 10, VBufWidth
-20, VBufHeight
-20, gxRGB
!(255, 127, 0));
570 oglSetupDG
= delegate () {
571 // this will create texture
572 gxResize(glconCtlWindow
.width
, glconCtlWindow
.height
);
576 redrawFrameDG
= delegate () {
578 glClearColor(0, 0, 0, 0);
579 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_ACCUM_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
580 glViewport(0, 0, sdwin.width, sdwin.height);
585 // rebuild main screen
586 nextFrameDG
= delegate () {
590 keyEventDG
= delegate (KeyEvent event
) {
591 if (!event
.pressed
) return;
593 case Key
.Escape
: concmd("quit"); break;
598 mouseEventDG
= delegate (MouseEvent event
) {
601 charEventDG
= delegate (dchar ch
) {
602 if (ch
== 'q') { concmd("quit"); return; }
605 resizeEventDG
= delegate (int wdt
, int hgt
) {
612 sdpyWindowClass
= "SDPY WINDOW";
613 auto sdwin
= new SimpleWindow(VBufWidth
, VBufHeight
, "My D App", OpenGlOptions
.yes
, Resizability
.allowResizing
);
614 //sdwin.hideCursor();
615 glconSetupForGLWindow(sdwin
);
618 conProcessQueue(int.max
/4);
621 glconRunGLWindowResizeable(VBufWidth
, VBufHeight
, "My D App", "SDPY WINDOW");