egra: some agg mini optimisations (rendering, hittest)
[iv.d.git] / skeletons / sdpygl.d
blob46bc89ade7b076d969da25d34fa62a9ee9de2f7b
1 import arsd.color;
2 import arsd.simpledisplay;
4 //import iv.bclamp;
5 import iv.cmdcon;
6 import iv.cmdcongl;
9 // ////////////////////////////////////////////////////////////////////////// //
10 public struct GxPoint {
11 public:
12 int x, y; ///
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); } ///
19 ///
20 int opCmp() (in auto ref GxPoint p) const {
21 pragma(inline, true);
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);
24 else return 0;
28 public struct GxRect {
29 public:
30 int x0, y0; ///
31 int width = -1; // <0: invalid rect
32 int height = -1; // <0: invalid rect
34 alias left = x0; ///
35 alias top = y0; ///
36 alias right = x1; ///
37 alias bottom = y1; ///
39 ///
40 string toString () const @trusted nothrow {
41 if (valid) {
42 import core.stdc.stdio : snprintf;
43 char[128] buf = void;
44 return buf[0..snprintf(buf.ptr, buf.length, "(%d,%d)-(%d,%d)", x0, y0, x0+width-1, y0+height-1)].idup;
45 } else {
46 return "(invalid-rect)";
50 pure nothrow @safe @nogc:
51 ///
52 this() (in auto ref GxRect rc) { pragma(inline, true); x0 = rc.x0; y0 = rc.y0; width = rc.width; height = rc.height; } ///
54 ///
55 this (int ax0, int ay0, int awidth, int aheight) {
56 pragma(inline, true);
57 x0 = ax0;
58 y0 = ay0;
59 width = awidth;
60 height = aheight;
63 ///
64 this() (in auto ref GxPoint xy0, int awidth, int aheight) {
65 pragma(inline, true);
66 x0 = xy0.x;
67 y0 = xy0.y;
68 width = awidth;
69 height = aheight;
72 ///
73 this() (in auto ref GxPoint xy0, in auto ref GxPoint xy1) {
74 pragma(inline, true);
75 x0 = xy0.x;
76 y0 = xy0.y;
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); } ///
83 ///
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);
88 return 0;
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);
119 /// ditto
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);
128 return
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);
137 return
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);
145 if (!r.empty) {
146 if (empty) {
147 x0 = r.x;
148 y0 = r.y;
149 width = r.width;
150 height = r.height;
151 } else {
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
171 return true;
175 void shrinkBy (int dx, int dy) {
176 pragma(inline, true);
177 if ((dx || dy) && valid) {
178 x0 += dx;
179 y0 += dy;
180 width -= dx*2;
181 height -= dy*2;
186 void growBy (int dx, int dy) {
187 pragma(inline, true);
188 if ((dx || dy) && valid) {
189 x0 -= dx;
190 y0 -= dy;
191 width += dx*2;
192 height += dy*2;
197 void set (int ax0, int ay0, int awidth, int aheight) {
198 pragma(inline, true);
199 x0 = ax0;
200 y0 = ay0;
201 width = awidth;
202 height = aheight;
206 void moveLeftTopBy (int dx, int dy) {
207 pragma(inline, true);
208 x0 += dx;
209 y0 += dy;
210 width -= dx;
211 height -= dy;
214 alias moveTopLeftBy = moveLeftTopBy; /// ditto
217 void moveRightBottomBy (int dx, int dy) {
218 pragma(inline, true);
219 width += dx;
220 height += dy;
223 alias moveBottomRightBy = moveRightBottomBy; /// ditto
226 void moveBy (int dx, int dy) {
227 pragma(inline, true);
228 x0 += dx;
229 y0 += dy;
233 void moveTo (int nx, int ny) {
234 pragma(inline, true);
235 x0 = nx;
236 y0 = ny;
240 * clip (x,y,len) stripe to this rect
242 * Params:
243 * x = stripe start (not relative to rect)
244 * y = stripe start (not relative to rect)
245 * len = stripe length
247 * Returns:
248 * x = fixed x
249 * len = fixed length
250 * leftSkip = how much cells skipped at the left side
251 * result = false if stripe is completely clipped out
253 * TODO:
254 * overflows
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;
259 if (x < x0) {
260 // left clip
261 if (x+len <= x0) return false;
262 len -= (leftSkip = x0-x);
263 x = x0;
265 if (x+len >= x0+width) {
266 // right clip
267 len = x0+width-x;
268 assert(len > 0); // yeah, always
270 return true;
273 /// ditto
274 bool clipStripe (ref int x, int y, ref int len) const {
275 pragma(inline, true);
276 int dummy = void;
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;
307 VBufWidth = wdt;
308 VBufHeight = hgt;
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) {
314 gxClipReset();
315 try {
316 gxRebuildScreenCB();
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);
327 vglTexId = 0;
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 {
346 if (vglTexId) {
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
358 glLoadIdentity();
359 glViewport(0, 0, VBufWidth, VBufHeight);
360 glOrtho(0, VBufWidth, VBufHeight, 0, -1, 1); // top-to-bottom
361 glMatrixMode(GL_MODELVIEW);
362 glLoadIdentity();
364 glEnable(GL_TEXTURE_2D);
365 glDisable(GL_LIGHTING);
366 glDisable(GL_DITHER);
367 glDisable(GL_DEPTH_TEST);
368 glDisable(GL_BLEND);
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);
377 glBegin(GL_QUADS);
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
382 glEnd();
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;
392 dc &= 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;
399 return orb|og;
403 // ////////////////////////////////////////////////////////////////////////// //
404 private template isGoodRGBInt(T) {
405 import std.traits : Unqual;
406 alias TT = Unqual!T;
407 enum isGoodRGBInt =
408 is(TT == ubyte) ||
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 {
435 GxRect rc;
436 ~this () const nothrow @trusted @nogc {
437 pragma(inline, true);
438 gxClipRect = rc;
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);
449 gxClipRect = cs.rc;
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;
486 if (x < 0) {
487 if (x+w <= 0) return;
488 w += x;
489 x = 0;
490 assert(w > 0);
492 if (x+w > VBufWidth) {
493 w = VBufWidth-x;
494 assert(w > 0);
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;
503 if (y < 0) {
504 if (y+h <= 0) return;
505 h += y;
506 y = 0;
507 assert(h > 0);
509 if (y+h > VBufHeight) {
510 h = VBufHeight-y;
511 assert(h > 0);
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) {
545 gxClipReset();
546 try {
547 gxRebuildScreenCB();
548 } catch (Exception e) {
549 conwriteln("SCREEN REBUILD ERROR: ", e.msg);
551 gxUpdateTexture();
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));
569 // first time setup
570 oglSetupDG = delegate () {
571 // this will create texture
572 gxResize(glconCtlWindow.width, glconCtlWindow.height);
575 // draw main screen
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);
582 gxBlitTexture();
585 // rebuild main screen
586 nextFrameDG = delegate () {
587 gxRebuildScreenCB();
590 keyEventDG = delegate (KeyEvent event) {
591 if (!event.pressed) return;
592 switch (event.key) {
593 case Key.Escape: concmd("quit"); break;
594 default:
598 mouseEventDG = delegate (MouseEvent event) {
601 charEventDG = delegate (dchar ch) {
602 if (ch == 'q') { concmd("quit"); return; }
605 resizeEventDG = delegate (int wdt, int hgt) {
606 gxResize(wdt, hgt);
607 gxRebuildScreen();
610 version(none) {
611 // this...
612 sdpyWindowClass = "SDPY WINDOW";
613 auto sdwin = new SimpleWindow(VBufWidth, VBufHeight, "My D App", OpenGlOptions.yes, Resizability.allowResizing);
614 //sdwin.hideCursor();
615 glconSetupForGLWindow(sdwin);
616 sdwin.eventLoop(0);
617 flushGui();
618 conProcessQueue(int.max/4);
619 } else {
620 // or this
621 glconRunGLWindowResizeable(VBufWidth, VBufHeight, "My D App", "SDPY WINDOW");