egra: cosmetix
[iv.d.git] / egra / gfx / base.d
blobdc4648ff8a369396f345ad34ac53f353ee79aa73
1 /*
2 * Simple Framebuffer Gfx/GUI lib
4 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
5 * Understanding is not required. Only obedience.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 module iv.egra.gfx.base /*is aliced*/;
20 private:
22 import iv.alice;
23 import iv.bclamp;
24 import iv.cmdcon;
26 import iv.egra.gfx.config;
29 // ////////////////////////////////////////////////////////////////////////// //
30 package __gshared int VBufWidth = 740*2;
31 package __gshared int VBufHeight = 520*2;
32 package __gshared ubyte vbufEffScale = 1; // effective (current) window scale
34 package __gshared uint* vglTexBuf; // OpenGL texture buffer
37 // ////////////////////////////////////////////////////////////////////////// //
38 public @property int screenEffScale () nothrow @trusted @nogc { pragma(inline, true); return vbufEffScale; }
39 public @property void screenEffScale (int scale) nothrow @trusted @nogc { pragma(inline, true); if (scale < 1) scale = 1; if (scale > 32) scale = 32; vbufEffScale = cast(ubyte)scale; }
41 public @property int screenWidth () nothrow @trusted @nogc { pragma(inline, true); return VBufWidth; }
42 public @property int screenHeight () nothrow @trusted @nogc { pragma(inline, true); return VBufHeight; }
44 public @property int screenWidthScaled () nothrow @trusted @nogc { pragma(inline, true); return VBufWidth*vbufEffScale; }
45 public @property int screenHeightScaled () nothrow @trusted @nogc { pragma(inline, true); return VBufHeight*vbufEffScale; }
48 // ////////////////////////////////////////////////////////////////////////// //
49 public enum GxDir {
50 Horiz,
51 Vert,
55 // ////////////////////////////////////////////////////////////////////////// //
56 public struct GxPoint {
57 public:
58 int x, y;
60 nothrow @safe @nogc:
61 this() (in auto ref GxPoint p) pure { pragma(inline, true); x = p.x; y = p.y; }
62 this (in int ax, in int ay) pure { pragma(inline, true); x = ax; y = ay; }
64 bool inside() (in auto ref GxRect rc) pure const { pragma(inline, true); return rc.inside(this); }
65 bool inside() (in auto ref GxSize sz) pure const { pragma(inline, true); return sz.inside(this); }
67 bool opEquals() (in auto ref GxPoint p) pure const { pragma(inline, true); return ((p.x^x)|(p.y^y)); }
69 int opCmp() (in auto ref GxPoint p) pure const {
70 pragma(inline, true);
71 return
72 y < p.y ? -1 :
73 y > p.y ? +1 :
74 x < p.x ? -1 :
75 x > p.x ? +1 :
79 void opAssign() (in auto ref GxPoint p) { pragma(inline, true); x = p.x; y = p.y; }
81 void opOpAssign(string op) (in int v) if (op == "+" || op == "-" || op == "*" || op == "/") {
82 pragma(inline, true);
83 mixin("x"~op~"=v; y"~op~"=v;");
86 void opOpAssign(string op) (in auto ref GxPoint pt) if (op == "+" || op == "-") {
87 pragma(inline, true);
88 mixin("x"~op~"=pt.x; y"~op~"=pt.y;");
91 GxPoint opBinary(string op) (in auto ref GxPoint pt) pure const if (op == "+" || op == "-") {
92 pragma(inline, true);
93 mixin("return GxPoint(x"~op~"pt.x, y"~op~"pt.y);");
96 GxPoint opBinary(string op) (in auto ref GxSize sz) pure const if (op == "+" || op == "-") {
97 pragma(inline, true);
98 mixin("return GxPoint(x"~op~"sz.w, y"~op~"sz.h);");
101 GxPoint opBinary(string op) (in int v) pure const if (op == "+" || op == "-") {
102 pragma(inline, true);
103 mixin("return GxPoint(x"~op~"v, y"~op~"v);");
106 int opIndex (in GxDir dir) pure const { pragma(inline, true); return (dir == GxDir.Horiz ? x : y); }
108 void opIndexAssign (in int v, in GxDir dir) { pragma(inline, true); if (dir == GxDir.Horiz) x = v; else y = v; }
110 void opIndexOpAssign(string op) (in int v, in GxDir dir) if (op == "+" || op == "-") {
111 pragma(inline, true);
112 if (dir == GxDir.Horiz) mixin("x "~op~"= v;"); else mixin("y "~op~"= v;");
117 // ////////////////////////////////////////////////////////////////////////// //
118 public struct GxSize {
119 public:
120 int w, h;
122 nothrow @safe @nogc:
123 @property bool valid () pure const { pragma(inline, true); return (w >= 0 && h >= 0); }
124 @property bool empty () pure const { pragma(inline, true); return (w < 1 || h < 1); }
126 bool inside() (in auto ref GxPoint pt) pure const { pragma(inline, true); return (pt.x >= 0 && pt.y >= 0 && pt.x < w && pt.y < h); }
128 void sanitize () { pragma(inline, true); if (w < 0) w = 0; if (h < 0) h = 0; }
130 void opOpAssign(string op) (in int v) if (op == "+" || op == "-" || op == "*" || op == "/") {
131 pragma(inline, true);
132 mixin("w"~op~"=v; h"~op~"=v;");
135 void opOpAssign(string op) (in auto ref GxSize sz) if (op == "+" || op == "-") {
136 pragma(inline, true);
137 mixin("w"~op~"=sz.w; h"~op~"=sz.h;");
140 GxSize opBinary(string op) (in auto ref GxSize sz) pure const if (op == "+" || op == "-") {
141 pragma(inline, true);
142 mixin("return GxSize(w"~op~"sz.w, h"~op~"sz.h);");
145 int opIndex (in GxDir dir) pure const { pragma(inline, true); return (dir == GxDir.Horiz ? w : h); }
147 void opIndexAssign (in int v, in GxDir dir) { pragma(inline, true); if (dir == GxDir.Horiz) w = v; else h = v; }
149 //FIXME: overflow!
150 void opIndexOpAssign(string op) (in int v, in GxDir dir) if (op == "+" || op == "-") {
151 pragma(inline, true);
152 if (dir == GxDir.Horiz) {
153 mixin("w "~op~"= v;");
154 if (w < 0) w = 0;
155 } else {
156 mixin("h "~op~"= v;");
157 if (h < 0) h = 0;
163 // ////////////////////////////////////////////////////////////////////////// //
164 public struct GxRect {
165 public:
166 GxPoint pos;
167 GxSize size = GxSize(-1, -1); // <0: invalid rect
169 string toString () const @trusted nothrow {
170 if (valid) {
171 import core.stdc.stdio : snprintf;
172 char[128] buf = void;
173 return buf[0..snprintf(buf.ptr, buf.length, "(%d,%d)-(%d,%d)", pos.x, pos.y, pos.x+size.w-1, pos.y+size.h-1)].idup;
174 } else {
175 return "(invalid-rect)";
179 nothrow @safe @nogc:
180 this() (in auto ref GxRect rc) pure {
181 pragma(inline, true);
182 pos = rc.pos;
183 size = rc.size;
186 this (in int ax0, in int ay0, in int awidth, in int aheight) pure {
187 pragma(inline, true);
188 pos.x = ax0;
189 pos.y = ay0;
190 size.w = awidth;
191 size.h = aheight;
194 this() (in int ax0, in int ay0, in auto ref GxSize asize) pure {
195 pragma(inline, true);
196 pos.x = ax0;
197 pos.y = ay0;
198 size = asize;
201 this() (in auto ref GxPoint xy0, in int awidth, in int aheight) pure {
202 pragma(inline, true);
203 pos = xy0;
204 size.w = awidth;
205 size.h = aheight;
208 this() (in auto ref GxPoint xy0, in auto ref GxSize asize) pure {
209 pragma(inline, true);
210 pos = xy0;
211 size = asize;
214 this() (in auto ref GxPoint xy0, in auto ref GxPoint xy1) pure {
215 pragma(inline, true);
216 pos = xy0;
217 size.w = xy1.x-xy0.x+1;
218 size.h = xy1.y-xy0.y+1;
221 this (in int awidth, in int aheight) pure {
222 pragma(inline, true);
223 pos.x = pos.y = 0;
224 size.w = awidth;
225 size.h = aheight;
228 this() (in auto ref GxSize asize) pure {
229 pragma(inline, true);
230 pos.x = pos.y = 0;
231 size = asize;
234 void setCoords(bool doSwap=true) (in int ax0, in int ay0, in int ax1, in int ay1) {
235 pragma(inline, true);
236 static if (doSwap) {
237 pos.x = (ax0 < ax1 ? ax0 : ax1);
238 pos.y = (ay0 < ay1 ? ay0 : ay1);
239 size.w = (ax0 < ax1 ? ax1 : ax0)-pos.x+1;
240 size.h = (ay0 < ay1 ? ay1 : ay0)-pos.y+1;
241 } else {
242 pos.x = ax0;
243 pos.y = ay0;
244 size.w = ax1-ax0+1;
245 size.h = ay1-ay0+1;
249 void setCoords(bool doSwap=true) (in auto ref GxPoint p0, in int ax1, in int ay1) { pragma(inline, true); setCoords!doSwap(p0.x, p0.y, ax1, ay1); }
250 void setCoords(bool doSwap=true) (in int ax0, in int ay0, in auto ref GxPoint p1) { pragma(inline, true); setCoords!doSwap(ax0, ay0, p1.x, p1.y); }
251 void setCoords(bool doSwap=true) (in auto ref GxPoint p0, in auto ref GxPoint p1) { pragma(inline, true); setCoords!doSwap(p0.x, p0.y, p1.x, p1.y); }
253 void opAssign() (in auto ref GxRect rc) { pragma(inline, true); pos = rc.pos; size = rc.size; }
255 bool opEquals() (in auto ref GxRect rc) pure const { pragma(inline, true); return (pos == rc.pos && size == rc.size); }
257 int opCmp() (in auto ref GxRect p) pure const { pragma(inline, true); return pos.opCmp(p.pos); }
259 @property bool valid () pure const { pragma(inline, true); return size.valid; }
260 @property bool invalid () pure const { pragma(inline, true); return !size.valid; }
261 @property bool empty () pure const { pragma(inline, true); return size.empty; } // invalid rects are empty
263 void invalidate () { pragma(inline, true); size.w = size.h = -1; }
265 @property int left () pure const { pragma(inline, true); return pos.x; }
266 @property void left (in int v) { pragma(inline, true); pos.x = v; }
268 @property int top () pure const { pragma(inline, true); return pos.y; }
269 @property void top (in int v) { pragma(inline, true); pos.y = v; }
271 @property int right () pure const { pragma(inline, true); return pos.x+size.w-1; }
272 @property void right (in int v) { pragma(inline, true); size.w = v-pos.x+1; }
274 @property int bottom () pure const { pragma(inline, true); return pos.y+size.h-1; }
275 @property void bottom (in int v) { pragma(inline, true); size.h = v-pos.y+1; }
277 @property GxPoint lefttop () pure const { pragma(inline, true); return pos; }
278 @property GxPoint righttop () pure const { pragma(inline, true); return pos+GxSize(size.w-1, 0); }
279 @property GxPoint leftbottom () pure const { pragma(inline, true); return pos+GxSize(0, size.h-1); }
280 @property GxPoint rightbottom () pure const { pragma(inline, true); return pos+size-1; }
282 @property void lefttop() (in auto ref GxPoint p) { pragma(inline, true); setCoords!false(p, rightbottom); }
283 @property void rightbottom() (in auto ref GxPoint p) { pragma(inline, true); setCoords!false(lefttop, p); }
285 alias topleft = lefttop;
286 alias topright = righttop;
287 alias bottomleft = leftbottom;
288 alias bottomright = rightbottom;
290 @property int x0 () pure const { pragma(inline, true); return pos.x; }
291 @property int y0 () pure const { pragma(inline, true); return pos.y; }
293 @property void x0 (in int val) { pragma(inline, true); setCoords!false(val, pos.y, rightbottom); }
294 @property void y0 (in int val) { pragma(inline, true); setCoords!false(pos.x, val, rightbottom); }
296 @property int x1 () pure const { pragma(inline, true); return (width > 0 ? x0+width-1 : x0-1); }
297 @property int y1 () pure const { pragma(inline, true); return (height > 0 ? y0+height-1 : y0-1); }
299 @property void x1 (in int val) { pragma(inline, true); width = val-x0+1; }
300 @property void y1 (in int val) { pragma(inline, true); height = val-y0+1; }
302 @property int width () pure const { pragma(inline, true); return size.w; }
303 @property int height () pure const { pragma(inline, true); return size.h; }
305 @property void width (in int val) { pragma(inline, true); size.w = val; }
306 @property void height (in int val) { pragma(inline, true); size.h = val; }
308 // is point inside this rect?
309 bool inside() (in auto ref GxPoint p) pure const {
310 pragma(inline, true);
311 return (width > 0 && height > 0 ? (p.x >= x0 && p.y >= y0 && p.x < x0+width && p.y < y0+height) : false);
314 // is point inside this rect?
315 bool inside (in int ax, in int ay) pure const {
316 pragma(inline, true);
317 return (width > 0 && height > 0 ? (ax >= x0 && ay >= y0 && ax < x0+width && ay < y0+height) : false);
320 // is `r` inside `this`?
321 bool contains() (in auto ref GxRect r) pure const {
322 pragma(inline, true);
323 return
324 width > 0 && height > 0 &&
325 r.width > 0 && r.height > 0 &&
326 r.x0 >= x0 && r.y0 >= y0 &&
327 r.x0+r.width <= x0+width && r.y0+r.height <= y0+height;
330 // does `r` and `this` overlap?
331 bool overlaps() (in auto ref GxRect r) pure const {
332 pragma(inline, true);
333 return
334 width > 0 && height > 0 &&
335 r.width > 0 && r.height > 0 &&
336 x0 < r.x0+r.width && r.x0 < x0+width &&
337 y0 < r.y0+r.height && r.y0 < y0+height;
340 // extend `this` so it will include `p`
341 void include() (in auto ref GxPoint p) {
342 pragma(inline, true);
343 if (empty) {
344 x0 = p.x;
345 y0 = p.y;
346 width = 1;
347 height = 1;
348 } else {
349 if (p.x < x0) x0 = p.x0;
350 if (p.y < y0) y0 = p.y0;
351 if (p.x1 > x1) x1 = p.x1;
352 if (p.y1 > y1) y1 = p.y1;
356 // extend `this` so it will include `r`
357 void include() (in auto ref GxRect r) {
358 pragma(inline, true);
359 if (!r.empty) {
360 if (empty) {
361 x0 = r.x;
362 y0 = r.y;
363 width = r.width;
364 height = r.height;
365 } else {
366 if (r.x < x0) x0 = r.x0;
367 if (r.y < y0) y0 = r.y0;
368 if (r.x1 > x1) x1 = r.x1;
369 if (r.y1 > y1) y1 = r.y1;
374 // clip `this` so it will not be larger than `r`
375 // returns `false` if the resulting rect (this) is empty or invalid
376 bool intersect (in int rx0, in int ry0, in int rwdt, in int rhgt) {
377 if (rwdt < 0 || rhgt < 0 || invalid) { size.w = size.h = -1; return false; }
378 if (rwdt == 0 || rhgt == 0 || empty) { size.w = size.h = 0; return false; }
379 immutable int rx1 = rx0+rwdt-1;
380 immutable int ry1 = ry0+rhgt-1;
381 if (ry1 < y0 || rx1 < x0 || rx0 > x1 || ry0 > y1) { size.w = size.h = 0; return false; }
382 // rc is at least partially inside this rect
383 if (x0 < rx0) x0 = rx0;
384 if (y0 < ry0) y0 = ry0;
385 if (x1 > rx1) x1 = rx1;
386 if (y1 > ry1) y1 = ry1;
387 assert(!empty); // yeah, always
388 return true;
391 // clip `this` so it will not be larger than `r`
392 // returns `false` if the resulting rect (this) is empty or invalid
393 bool intersect (in int rwdt, in int rhgt) {
394 pragma(inline, true);
395 return intersect(0, 0, rwdt, rhgt);
398 // clip `this` so it will not be larger than `r`
399 // returns `false` if the resulting rect (this) is empty or invalid
400 bool intersect() (in auto ref GxRect r) {
401 pragma(inline, true);
402 return intersect(r.x0, r.y0, r.width, r.height);
405 void shrinkBy (in int dx, in int dy) {
406 pragma(inline, true);
407 if ((dx|dy) && valid) {
408 pos.x += dx;
409 pos.y += dy;
410 size.w -= dx<<1;
411 size.h -= dy<<1;
415 void shrinkBy() (in auto ref GxSize sz) { pragma(inline, true); shrinkBy(sz.w, sz.h); }
417 void growBy (in int dx, in int dy) {
418 pragma(inline, true);
419 if ((dx|dy) && valid) {
420 pos.x -= dx;
421 pos.y -= dy;
422 size.w += dx<<1;
423 size.h += dy<<1;
427 void growBy() (in auto ref GxSize sz) { pragma(inline, true); growBy(sz.w, sz.h); }
429 void set (in int ax0, in int ay0, in int awidth, in int aheight) {
430 pragma(inline, true);
431 x0 = ax0;
432 y0 = ay0;
433 width = awidth;
434 height = aheight;
437 void set() (in auto ref GxPoint p0, in int awidth, in int aheight) { pragma(inline, true); set(p0.x, p0.y, awidth, aheight); }
438 void set() (in auto ref GxPoint p0, in auto ref GxSize asize) { pragma(inline, true); set(p0.x, p0.y, asize.w, asize.h); }
439 void set() (in int ax0, in int ay0, in auto ref GxSize asize) { pragma(inline, true); set(ax0, ay0, asize.w, asize.h); }
441 void moveLeftTopBy (in int dx, in int dy) {
442 pragma(inline, true);
443 pos.x += dx;
444 pos.y += dy;
445 size.w -= dx;
446 size.h -= dy;
449 void moveLeftTopBy() (in auto ref GxPoint p) { pragma(inline, true); moveLeftTopBy(p.x, p.y); }
451 alias moveTopLeftBy = moveLeftTopBy;
453 void moveRightBottomBy (in int dx, in int dy) {
454 pragma(inline, true);
455 size.w += dx;
456 size.h += dy;
459 void moveRightBottomBy() (in auto ref GxPoint p) { pragma(inline, true); moveRightBottomBy(p.x, p.y); }
461 alias moveBottomRightBy = moveRightBottomBy;
463 void moveBy (in int dx, in int dy) {
464 pragma(inline, true);
465 pos.x += dx;
466 pos.y += dy;
469 void moveBy() (in auto ref GxPoint p) { pragma(inline, true); moveBy(p.x, p.y); }
471 void moveTo (in int nx, in int ny) {
472 pragma(inline, true);
473 x0 = nx;
474 y0 = ny;
477 void moveTo() (in auto ref GxPoint p) { pragma(inline, true); moveTo(p.x, p.y); }
480 * clip (x,y,wdt) stripe to this rect
482 * Params:
483 * x = stripe start (not relative to rect)
484 * y = stripe start (not relative to rect)
485 * wdt = stripe length
487 * Returns:
488 * x = fixed x (invalid if result is false)
489 * wdt = fixed length (invalid if result is false)
490 * leftSkip = how much cells skipped at the left side (invalid if result is false)
491 * result = false if stripe is completely clipped out
493 bool clipHStripe (ref int x, int y, ref int wdt, int* leftSkip=null) const @trusted {
494 if (empty) return false;
495 if (wdt <= 0 || y < y0 || y >= y0+height || x >= x0+width) return false;
496 if (x < x0) {
497 // left clip
498 immutable int dx = x0-x;
499 if (dx >= wdt) return false; // avoid overflow
500 if (leftSkip !is null) *leftSkip = dx;
501 wdt -= dx;
502 x = x0;
503 assert(wdt > 0); // yeah, always
505 if (wdt > width) wdt = width; // avoid overflow
506 if (x+wdt > x0+width) {
507 // right clip
508 wdt = x0+width-x;
509 assert(wdt > 0); // yeah, always
511 return true;
514 bool clipHStripe (ref GxPoint p, ref int wdt, int* leftSkip=null) const @trusted {
515 pragma(inline, true);
516 return clipHStripe(ref p.x, p.y, ref wdt, leftSkip);
520 * clip (x,y,hgt) stripe to this rect
522 * Params:
523 * x = stripe start (not relative to rect)
524 * y = stripe start (not relative to rect)
525 * hgt = stripe length
527 * Returns:
528 * y = fixed y (invalid if result is false)
529 * hgt = fixed length (invalid if result is false)
530 * topSkip = how much cells skipped at the top side (invalid if result is false)
531 * result = false if stripe is completely clipped out
533 bool clipVStripe (int x, ref int y, ref int hgt, int* topSkip=null) const @trusted {
534 if (empty) return false;
535 if (hgt <= 0 || x < x0 || x >= x0+width || y >= y0+height) return false;
536 if (y < y0) {
537 // top clip
538 immutable int dy = y0-y;
539 if (dy >= hgt) return false; // avoid overflow
540 if (topSkip !is null) *topSkip = dy;
541 hgt -= dy;
542 y = y0;
543 assert(hgt > 0); // yeah, always
545 if (hgt > height) hgt = height; // avoid overflow
546 if (y+hgt > y0+height) {
547 // bottom clip
548 hgt = y0+height-y;
549 assert(hgt > 0); // yeah, always
551 return true;
554 bool clipVStripe (ref GxPoint p, ref int hgt, int* topSkip=null) const @trusted {
555 pragma(inline, true);
556 return clipVStripe(p.x, ref p.y, ref hgt, topSkip);
559 bool clipHVStripes (ref int x, ref int y, ref int wdt, ref int hgt, int* leftSkip=null, int* topSkip=null) const @trusted {
560 if (empty || wdt <= 0 || hgt <= 0) return false;
561 if (y >= y0+height || x >= x0+width) return false;
562 // use dummy `x` and `y` for horizontal and vertical clippers, because they are only checked for validity there
563 if (!clipHStripe(ref x, y0, ref wdt, leftSkip)) return false;
564 return clipVStripe(x0, ref y, ref hgt, topSkip);
567 bool clipHVStripes (ref GxPoint p, ref int wdt, ref int hgt, int* leftSkip=null, int* topSkip=null) const @trusted {
568 pragma(inline, true);
569 return clipHVStripes(ref p.x, ref p.y, ref wdt, ref hgt, leftSkip, topSkip);
572 bool clipHVStripes (ref GxPoint p, ref GxSize sz, int* leftSkip=null, int* topSkip=null) const @trusted {
573 pragma(inline, true);
574 return clipHVStripes(ref p.x, ref p.y, ref sz.w, ref sz.h, leftSkip, topSkip);
577 bool clipHVStripes (ref int x, ref int y, ref GxSize sz, int* leftSkip=null, int* topSkip=null) const @trusted {
578 pragma(inline, true);
579 return clipHVStripes(ref x, ref y, ref sz.w, ref sz.h, leftSkip, topSkip);
584 // ////////////////////////////////////////////////////////////////////////// //
585 public bool gxIsTransparent (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return ((clr&0xff000000u) == 0x00000000u); }
586 public bool gxIsSolid (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return ((clr&0xff000000u) == 0xff000000u); }
588 public bool gxIsSolidBlack (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return (clr == 0xff000000u); }
590 public ubyte gxGetRed (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)(clr>>16); }
591 public ubyte gxGetGreen (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)(clr>>8); }
592 public ubyte gxGetBlue (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)clr; }
593 public ubyte gxGetAlpha (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)(clr>>24); }
595 public enum gxSolidBlack = 0xff000000u;
596 public enum gxSolidWhite = 0xffffffffu;
598 public enum gxTransparent = 0x00000000u;
599 public enum gxColorMask = 0x00ffffffu;
601 public enum gxUnknown = 0x00010203u;
604 // ////////////////////////////////////////////////////////////////////////// //
605 // mix dc with ARGB (or ABGR) clr; dc A is ignored (removed)
606 // main code never calls this with solid or transparent `colvar`
607 // see http://stereopsis.com/doubleblend.html for the inspiration
608 enum GxColMixMixin(string destvar, string dcvar, string colvar, bool useAddCorrection=false) = `{
609 immutable uint col_ = `~colvar~`;
610 immutable uint dc_ = (`~dcvar~`)&0xffffff;
611 /*immutable uint a_ = 256-(col_>>24);*/ /* to not loose bits */
612 immutable uint a_ = (col_>>24)+1; /* so it will work for both 0 and 255 correctly */
613 immutable uint srb_ = (col_&0xff00ff);
614 immutable uint sg_ = (col_&0x00ff00);
615 immutable uint drb_ = (dc_&0xff00ff);
616 immutable uint dg_ = (dc_&0x00ff00);
617 immutable uint orb_ = (drb_+(((srb_-drb_)*a_+0x800080)>>8))&0xff00ff;
618 immutable uint og_ = (dg_+(((sg_-dg_)*a_+0x008000)>>8))&0x00ff00;
619 (`~destvar~`) = orb_|og_;
622 public uint gxColMix (in uint dc, in uint clr) pure nothrow @trusted @nogc {
623 pragma(inline, true);
624 if (gxIsSolid(clr)) return clr;
625 else if (gxIsTransparent(clr)) return dc;
626 else {
627 uint res = void;
628 mixin(GxColMixMixin!("res", "dc", "clr"));
629 return res;
634 // ////////////////////////////////////////////////////////////////////////// //
635 private template isGoodRGBInt(T) {
636 import std.traits : Unqual;
637 alias TT = Unqual!T;
638 enum isGoodRGBInt =
639 is(TT == ubyte) ||
640 is(TT == short) || is(TT == ushort) ||
641 is(TT == int) || is(TT == uint) ||
642 is(TT == long) || is(TT == ulong);
646 // ////////////////////////////////////////////////////////////////////////// //
647 public uint gxrgb(T0, T1, T2) (T0 r, T1 g, T2 b) pure nothrow @trusted @nogc
648 if (isGoodRGBInt!T0 && isGoodRGBInt!T1 && isGoodRGBInt!T2)
650 pragma(inline, true);
651 return (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b)|0xff000000u;
654 public uint gxrgba(T0, T1, T2, T3) (T0 r, T1 g, T2 b, T3 a) pure nothrow @trusted @nogc
655 if (isGoodRGBInt!T0 && isGoodRGBInt!T1 && isGoodRGBInt!T2 && isGoodRGBInt!T3)
657 pragma(inline, true);
658 return (clampToByte(a)<<24)|(clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
662 public enum gxRGB(int r, int g, int b) = (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b)|0xff000000u;
663 public enum gxRGBA(int r, int g, int b, int a) = (clampToByte(a)<<24)|(clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
666 // ////////////////////////////////////////////////////////////////////////// //
667 // current clip rect
668 public __gshared GxRect gxClipRect = GxRect(65535, 65535);
670 public void gxWithSavedClip(DG) (scope DG dg)
671 if (is(typeof((inout int=0) { DG dg = void; dg(); })))
673 pragma(inline, true);
674 if (dg !is null) {
675 immutable rc = gxClipRect;
676 scope(exit) gxClipRect = rc;
677 dg();
681 public void gxClipReset () nothrow @trusted @nogc {
682 pragma(inline, true);
683 gxClipRect = GxRect(65535, 65535);
687 // ////////////////////////////////////////////////////////////////////////// //
688 public void gxClearScreen (uint clr) nothrow @trusted @nogc {
689 clr &= gxColorMask; // only solid color matters here anyway
690 memFillDW(vglTexBuf, clr, VBufWidth*VBufHeight);
692 if (clr == 0u) {
693 import core.stdc.string : memset;
694 memset(vglTexBuf, 0, (VBufWidth*VBufHeight)<<2);
695 } else {
696 vglTexBuf[0..VBufWidth*VBufHeight] = clr;
702 public void gxPutPixel (in int x, in int y, in uint c) nothrow @trusted @nogc {
703 pragma(inline, true);
704 if (x >= 0 && y >= 0 && x < VBufWidth && y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(x, y)) {
705 uint* dp = cast(uint*)(cast(ubyte*)vglTexBuf)+y*VBufWidth+x;
706 *dp = gxColMix(*dp, c);
711 public void gxPutPixel() (in auto ref GxPoint p, in uint c) nothrow @trusted @nogc {
712 pragma(inline, true);
713 if (p.x >= 0 && p.y >= 0 && p.x < VBufWidth && p.y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(p)) {
714 uint* dp = cast(uint*)(cast(ubyte*)vglTexBuf)+p.y*VBufWidth+p.x;
715 *dp = (gxIsSolid(c) ? (c&gxColorMask) : gxColMix(*dp, c));
720 public void gxSetPixel (in int x, in int y, in uint c) nothrow @trusted @nogc {
721 pragma(inline, true);
722 if (x >= 0 && y >= 0 && x < VBufWidth && y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(x, y)) {
723 *(cast(uint*)(cast(ubyte*)vglTexBuf)+y*VBufWidth+x) = c&gxColorMask;
728 public void gxSetPixel() (in auto ref GxPoint p, in uint c) nothrow @trusted @nogc {
729 pragma(inline, true);
730 if (p.x >= 0 && p.y >= 0 && p.x < VBufWidth && p.y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(p)) {
731 *(cast(uint*)(cast(ubyte*)vglTexBuf)+p.y*VBufWidth+p.x) = c&gxColorMask;
736 // ////////////////////////////////////////////////////////////////////////// //
737 public void gxHStripedLine (int x, int y, int w, in int stlen, in uint stclr) nothrow @trusted @nogc {
738 if (stlen < 1 || w < 1) return;
739 if (gxIsTransparent(stclr)) return;
740 immutable int xstart = x;
741 if (!gxClipRect.clipHStripe(x, y, w)) return;
742 if (!GxRect(VBufWidth, VBufHeight).clipHStripe(x, y, w)) return;
743 immutable uint stFull = cast(uint)stlen<<1;
744 int sofs = cast(uint)(x-xstart)%stFull;
745 uint* dptr = vglTexBuf+y*VBufWidth+x;
746 if (gxIsSolid(stclr)) {
747 if (sofs != 0) {
748 // fill
749 uint nlen = void;
750 if (sofs < stlen) {
751 nlen = cast(uint)(stlen-sofs);
752 if (nlen >= cast(uint)w) {
753 //dptr[0..cast(uint)w] = stclr;
754 memFillDW(dptr, stclr, w);
755 return;
757 //dptr[0..nlen] = stclr;
758 memFillDW(dptr, stclr, cast(int)nlen);
759 dptr += nlen;
760 w -= nlen;
761 sofs = stlen;
763 // skip
764 assert(sofs >= stlen && sofs < cast(int)stFull);
765 nlen = stFull-cast(uint)sofs;
766 if (nlen >= cast(uint)w) return;
767 dptr += nlen;
768 w -= cast(int)nlen;
769 // done
770 sofs = 0;
772 // rest
773 while (w > 0) {
774 if (stlen >= w) {
775 //dptr[0..cast(uint)w] = stclr;
776 memFillDW(dptr, stclr, w);
777 return;
779 //dptr[0..cast(uint)stlen] = stclr;
780 memFillDW(dptr, stclr, stlen);
781 if ((w -= stlen) <= 0) return;
782 w -= stlen;
783 dptr += stFull;
785 } else {
786 //TODO: optimise this
787 if (sofs) {
788 while (sofs < stlen && w > 0) {
789 if (sofs < stlen) { mixin(GxColMixMixin!("*dptr", "*dptr", "stclr")); }
790 ++dptr;
791 ++sofs;
792 --w;
794 if (w <= 0) return;
795 assert(sofs >= stlen && sofs < cast(int)stFull);
796 immutable uint nlen = stFull-cast(uint)sofs;
797 if (nlen >= cast(uint)w) return;
798 dptr += nlen;
799 w -= cast(int)nlen;
800 // done
801 sofs = 0;
803 assert(sofs == 0);
804 while (w > 0) {
805 immutable int nlen = (stlen < w ? stlen : w);
806 foreach (; 0..nlen) {
807 mixin(GxColMixMixin!("*dptr++", "*dptr", "stclr"));
809 if ((w -= nlen) <= 0) return;
810 w -= stlen;
811 dptr += cast(uint)stlen;
816 public void gxHStripedLine() (in auto ref GxPoint p, in int w, int stlen, in uint stclr) nothrow @trusted @nogc { pragma(inline, true); gxHStripedLine(p.x, p.y, w, stlen, stclr); }
819 // ////////////////////////////////////////////////////////////////////////// //
820 public void gxHLine (int x, int y, int w, in uint clr) nothrow @trusted @nogc {
821 if (gxIsTransparent(clr)) return;
822 if (!gxClipRect.clipHStripe(x, y, w)) return;
823 if (!GxRect(VBufWidth, VBufHeight).clipHStripe(x, y, w)) return;
824 if (gxIsSolid(clr)) {
825 memFillDW(vglTexBuf+y*VBufWidth+x, clr, w);
827 immutable uint addr = y*VBufWidth+x;
828 if (gxIsSolidBlack(clr)) {
829 import core.stdc.string : memset;
830 memset(vglTexBuf+addr, 0, w<<2);
831 } else {
832 vglTexBuf[addr..addr+w] = clr;
835 } else {
836 uint* dptr = vglTexBuf+y*VBufWidth+x;
837 while (w-- > 0) {
838 mixin(GxColMixMixin!("*dptr++", "*dptr", "clr"));
843 public void gxHLine() (in auto ref GxPoint p, in int w, in uint clr) nothrow @trusted @nogc { pragma(inline, true); gxHLine(p.x, p.y, w, clr); }
845 public void gxVLine (int x, int y, int h, in uint clr) nothrow @trusted @nogc {
846 if (gxIsTransparent(clr)) return;
847 if (!gxClipRect.clipVStripe(x, y, h)) return;
848 if (!GxRect(VBufWidth, VBufHeight).clipVStripe(x, y, h)) return;
849 uint* dptr = vglTexBuf+y*VBufWidth+x;
850 if (gxIsSolid(clr)) {
851 while (h-- > 0) { *dptr = clr; dptr += VBufWidth; }
852 } else {
853 while (h-- > 0) { mixin(GxColMixMixin!("*dptr", "*dptr", "clr")); dptr += VBufWidth; }
857 public void gxVLine() (in auto ref GxPoint p, in int h, in uint clr) nothrow @trusted @nogc { pragma(inline, true); gxVLine(p.x, p.y, h, clr); }
860 // ////////////////////////////////////////////////////////////////////////// //
861 public void gxFillRect (int x, int y, int w, int h, in uint clr) nothrow @trusted @nogc {
862 if (gxIsTransparent(clr)) return;
863 if (!gxClipRect.clipHVStripes(x, y, w, h)) return;
864 if (!GxRect(VBufWidth, VBufHeight).clipHVStripes(x, y, w, h)) return;
865 if (w == 1) { gxVLine(x, y, h, clr); return; }
866 if (h == 1) { gxHLine(x, y, w, clr); return; }
867 if (gxIsSolid(clr)) {
868 uint addr = y*VBufWidth+x;
869 while (h-- > 0) {
870 memFillDW(vglTexBuf+addr, clr, w);
871 addr += VBufWidth;
874 uint addr = y*VBufWidth+x;
875 if (gxIsSolidBlack(clr)) {
876 import core.stdc.string : memset;
877 while (h-- > 0) {
878 memset(vglTexBuf+addr, 0, w<<2);
879 addr += VBufWidth;
881 } else {
882 while (h-- > 0) {
883 vglTexBuf[addr..addr+w] = clr;
884 addr += VBufWidth;
888 } else {
889 uint* dptr = vglTexBuf+y*VBufWidth+x;
890 immutable uint dinc = VBufWidth-w;
891 while (h-- > 0) {
892 foreach (immutable _; 0..w) {
893 mixin(GxColMixMixin!("*dptr++", "*dptr", "clr"));
895 dptr += dinc;
900 public void gxFillRect() (in auto ref GxRect rc, in uint clr) nothrow @trusted @nogc {
901 pragma(inline, true);
902 gxFillRect(rc.x0, rc.y0, rc.width, rc.height, clr);
905 public void gxDrawRect (in int x, in int y, in int w, in int h, in uint clr) nothrow @trusted @nogc {
906 if (w < 1 || h < 1 || gxIsTransparent(clr)) return;
907 gxHLine(x, y, w, clr);
908 if (h > 1) gxHLine(x, y+h-1, w, clr);
909 if (h > 2) {
910 gxVLine(x, y+1, h-2, clr);
911 if (w > 1) gxVLine(x+w-1, y+1, h-2, clr);
915 public void gxDrawRect() (in auto ref GxRect rc, in uint clr) nothrow @trusted @nogc {
916 pragma(inline, true);
917 gxDrawRect(rc.x0, rc.y0, rc.width, rc.height, clr);
921 // ////////////////////////////////////////////////////////////////////////// //
922 // use clip region as boundaries
923 public void gxDrawShadow (in GxRect winrect, int size, in uint clrshadow=gxRGBA!(0, 0, 0, 127)) nothrow @trusted @nogc {
924 if (size < 1 || winrect.empty || gxIsTransparent(clrshadow)) return;
925 gxWithSavedClip{
926 gxClipReset();
927 gxFillRect(winrect.x1+1, winrect.y0+size, size, winrect.height-size, clrshadow);
928 gxFillRect(winrect.x0+size, winrect.y1+1, winrect.width, size, clrshadow);
933 public void gxDrawWindow (GxRect winrect,
934 const(char)[] title, in uint framecolor,
935 in uint titlecolor, in uint titlebackcolor,
936 in uint windowcolor, in uint shadowcolor, int shadowsize=8) nothrow @trusted
938 import iv.egra.gfx.text;
940 if (winrect.empty) return;
941 gxDrawShadow(winrect, shadowsize, shadowcolor);
943 gxDrawRect(winrect, framecolor);
944 if (winrect.width <= 2 || winrect.height <= 2) return;
945 winrect.shrinkBy(1, 1);
947 if (title is null) {
948 gxFillRect(winrect, windowcolor);
949 return;
952 gxWithSavedClip{
953 immutable int hgt = (gxTextHeightUtf < 10 ? 10 : gxTextHeightUtf+1);
954 immutable int oh = winrect.size.h;
955 if (hgt < winrect.size.h) winrect.size.h = hgt;
956 if (gxClipRect.intersect(winrect)) {
957 gxFillRect(gxClipRect, titlebackcolor);
958 gxDrawTextUtf(winrect.x0+(winrect.width-gxTextWidthUtf(title))/2, winrect.y0+(hgt-gxTextHeightUtf)/2, title, titlecolor);
960 winrect.pos.y += hgt;
961 winrect.size.h = oh-hgt;
963 gxFillRect(winrect, windowcolor);
967 // ////////////////////////////////////////////////////////////////////////// //
968 public void gxDrawScrollBar() (in auto ref GxRect r, in int max, in int value) nothrow @trusted @nogc { pragma(inline, true); gxDrawScrollBar(r, 0, max, value); }
970 public void gxDrawScrollBar() (in auto ref GxRect r, int min, int max, int value) nothrow @trusted @nogc {
971 enum FrameColor = gxRGB!(220, 220, 220);
972 enum EmptyColor = gxRGB!(0, 0, 0);
973 enum FullColor = gxRGB!(160, 160, 160);
974 if (r.empty) return;
975 if (max <= min) min = max = value = 0;
976 // move it to 0
977 //conwriteln("00: min=", min, "; max=", max, "; value=", value);
978 max -= min;
979 value -= min;
980 if (value < 0) value = 0; else if (value > max) value = max;
981 //conwriteln("01: min=", min, "; max=", max, "; value=", value);
982 int sx0 = r.x0+1;
983 int sy0 = r.y0+1;
984 int wdt = r.width-2;
985 int hgt = r.height-2;
986 bool vert = (r.width < r.height);
987 // frame
988 if ((vert && wdt > 1) || (!vert && hgt > 1)) {
989 gxHLine(r.x0+1, r.y0+0, wdt, FrameColor);
990 gxVLine(r.x0+0, r.y0+1, hgt, FrameColor);
991 gxVLine(r.x1+0, r.y0+1, hgt, FrameColor);
992 gxHLine(r.x0+1, r.y1+0, wdt, FrameColor);
993 } else {
994 sx0 -= 1;
995 sy0 -= 1;
996 wdt += 2;
997 hgt += 2;
999 if (max <= 0) {
1000 gxFillRect(sx0, sy0, wdt, hgt, FullColor);
1001 return;
1003 if (vert) {
1004 int pix = hgt*value/max;
1005 if (pix > hgt) pix = hgt; // just in case
1006 gxFillRect(sx0, sy0, wdt, pix, FullColor);
1007 gxFillRect(sx0, sy0+pix, wdt, hgt-pix, EmptyColor);
1008 } else {
1009 int pix = wdt*value/max;
1010 if (pix > wdt) pix = wdt; // just in case
1011 gxFillRect(sx0, sy0, pix, hgt, FullColor);
1012 gxFillRect(sx0+pix, sy0, wdt-pix, hgt, EmptyColor);
1017 // ////////////////////////////////////////////////////////////////////////// //
1018 private int abs (int a) pure nothrow @safe @nogc { pragma(inline, true); return (a < 0 ? -a : a); }
1020 public void gxCircle (in int cx, in int cy, in int radius, in uint clr) nothrow @trusted @nogc {
1021 static void plot4points (in int cx, in int cy, in int x, in int y, in uint clr) nothrow @trusted @nogc {
1022 pragma(inline, true);
1023 gxPutPixel(cx+x, cy+y, clr);
1024 if (x) gxPutPixel(cx-x, cy+y, clr);
1025 if (y) gxPutPixel(cx+x, cy-y, clr);
1026 gxPutPixel(cx-x, cy-y, clr);
1029 if (radius <= 0 || gxIsTransparent(clr)) return;
1030 if (radius == 1) { gxPutPixel(cx, cy, clr); return; }
1031 int error = -radius, x = radius, y = 0;
1032 while (x > y) {
1033 plot4points(cx, cy, x, y, clr);
1034 plot4points(cx, cy, y, x, clr);
1035 error += y*2+1;
1036 ++y;
1037 if (error >= 0) { --x; error -= x*2; }
1039 plot4points(cx, cy, x, y, clr);
1042 public void gxCircle() (in auto ref GxPoint c, in int radius, in uint clr) nothrow @trusted @nogc { pragma(inline, true); gxCircle(c.x, c.y, radius, clr); }
1045 public void gxFillCircle (in int cx, in int cy, in int radius, in uint clr) nothrow @trusted @nogc {
1046 if (radius <= 0 || gxIsTransparent(clr)) return;
1047 if (radius == 1) { gxPutPixel(cx, cy, clr); return; }
1048 int error = -radius, x = radius, y = 0;
1049 while (x >= y) {
1050 int last_y = y;
1051 error += y;
1052 ++y;
1053 error += y;
1054 gxHLine(cx-x, cy+last_y, 2*x+1, clr);
1055 if (x != 0 && last_y != 0) gxHLine(cx-x, cy-last_y, 2*x+1, clr);
1056 if (error >= 0) {
1057 if (x != last_y) {
1058 gxHLine(cx-last_y, cy+x, 2*last_y+1, clr);
1059 if (last_y != 0 && x != 0) gxHLine(cx-last_y, cy-x, 2*last_y+1, clr);
1061 error -= x;
1062 --x;
1063 error -= x;
1068 public void gxFillCircle() (in auto ref GxPoint c, in int radius, in uint clr) nothrow @trusted @nogc { pragma(inline, true); gxFillCircle(c.x, c.y, radius, clr); }
1071 public void gxEllipse (int x0, int y0, int x1, int y1, in uint clr) nothrow @trusted @nogc {
1072 if (gxIsTransparent(clr)) return;
1073 if (y0 == y1) { gxHLine(x0, y0, x1-x0+1, clr); return; }
1074 if (x0 == x1) { gxVLine(x0, y0, y1-y0+1, clr); return; }
1075 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
1076 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
1077 long err = dx+dy+b1*a*a; // error of 1.step
1078 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
1079 if (y0 > y1) y0 = y1; // ...exchange them
1080 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
1081 a *= 8*a; b1 = 8*b*b;
1082 do {
1083 long e2;
1084 gxPutPixel(x1, y0, clr); // I. Quadrant
1085 gxPutPixel(x0, y0, clr); // II. Quadrant
1086 gxPutPixel(x0, y1, clr); // III. Quadrant
1087 gxPutPixel(x1, y1, clr); // IV. Quadrant
1088 e2 = 2*err;
1089 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
1090 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
1091 } while (x0 <= x1);
1092 while (y0-y1 < b) {
1093 // too early stop of flat ellipses a=1
1094 gxPutPixel(x0-1, ++y0, clr); // complete tip of ellipse
1095 gxPutPixel(x0-1, --y1, clr);
1099 public void gxEllipse() (in auto ref GxRect rc, in int radius, in uint clr) nothrow @trusted @nogc { pragma(inline, true); gxEllipse(rc.x0, rc.y0, rc.x1, rc.y1, clr); }
1102 public void gxFillEllipse (int x0, int y0, int x1, int y1, in uint clr) nothrow @trusted @nogc {
1103 if (gxIsTransparent(clr)) return;
1104 if (y0 == y1) { gxHLine(x0, y0, x1-x0+1, clr); return; }
1105 if (x0 == x1) { gxVLine(x0, y0, y1-y0+1, clr); return; }
1106 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
1107 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
1108 long err = dx+dy+b1*a*a; // error of 1.step
1109 int prev_y0 = -1, prev_y1 = -1;
1110 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
1111 if (y0 > y1) y0 = y1; // ...exchange them
1112 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
1113 a *= 8*a; b1 = 8*b*b;
1114 do {
1115 long e2;
1116 if (y0 != prev_y0) { gxHLine(x0, y0, x1-x0+1, clr); prev_y0 = y0; }
1117 if (y1 != y0 && y1 != prev_y1) { gxHLine(x0, y1, x1-x0+1, clr); prev_y1 = y1; }
1118 e2 = 2*err;
1119 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
1120 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
1121 } while (x0 <= x1);
1122 while (y0-y1 < b) {
1123 // too early stop of flat ellipses a=1
1124 gxPutPixel(x0-1, ++y0, clr); // complete tip of ellipse
1125 gxPutPixel(x0-1, --y1, clr);
1129 public void gxFillEllipse() (in auto ref GxRect rc, in int radius, in uint clr) nothrow @trusted @nogc { pragma(inline, true); gxFillEllipse(rc.x0, rc.y0, rc.x1, rc.y1, clr); }
1132 // ////////////////////////////////////////////////////////////////////////// //
1133 public void gxDrawRoundedRect (int x0, int y0, int wdt, int hgt, int radius, in uint clr) nothrow @trusted @nogc {
1134 static void gxArcs (int cx, int cy, int wdt, int hgt, in int radius, in uint clr) nothrow @trusted @nogc {
1135 static void plot4points (in int radius, in int wdt, in int hgt, in int cx, in int cy, in int x, in int y, in uint clr) nothrow @trusted @nogc {
1136 pragma(inline, true);
1137 gxPutPixel(cx+wdt-1+x, cy+hgt-1+y, clr);
1138 gxPutPixel(cx-x, cy+hgt-1+y, clr);
1139 gxPutPixel(cx+wdt-1+x, cy-y, clr);
1140 gxPutPixel(cx-x, cy-y, clr);
1143 wdt -= (radius<<1); if (wdt <= 0) return;
1144 hgt -= (radius<<1); if (hgt <= 0) return;
1145 cx += radius; cy += radius;
1146 int error = -radius, x = radius, y = 0;
1147 while (x > y) {
1148 plot4points(radius, wdt, hgt, cx, cy, x, y, clr);
1149 plot4points(radius, wdt, hgt, cx, cy, y, x, clr);
1150 error += y*2+1;
1151 ++y;
1152 if (error >= 0) { --x; error -= x*2; }
1154 if (x || y != radius) plot4points(radius, wdt, hgt, cx, cy, x, y, clr);
1158 if (wdt < 1 || hgt < 1) return;
1159 if (radius < 1) { gxDrawRect(x0, y0, wdt, hgt, clr); return; }
1160 if (gxIsTransparent(clr)) return;
1161 if (hgt == 1) { gxHLine(x0, y0, wdt, clr); return; }
1162 if (wdt == 1) { gxVLine(x0, y0, hgt, clr); return; }
1163 // fix radius
1164 immutable int minsz = (wdt < hgt ? wdt : hgt);
1165 if (radius >= (minsz>>1)) {
1166 radius = (minsz>>1)-1;
1167 if (radius < 1) { gxDrawRect(x0, y0, wdt, hgt, clr); return; }
1169 // draw the parts of the rect
1170 gxHLine(x0+radius+1, y0, wdt-(radius<<1)-2, clr); // top
1171 gxHLine(x0+radius+1, y0+hgt-1, wdt-(radius<<1)-2, clr); // bottom
1172 gxVLine(x0, y0+radius+1, hgt-(radius<<1)-2, clr); // left
1173 gxVLine(x0+wdt-1, y0+radius+1, hgt-(radius<<1)-2, clr); // right
1174 // left arc
1175 gxArcs(x0, y0, wdt, hgt, radius, clr);
1178 public void gxDrawRoundedRect() (in auto ref GxRect rc, in int radius, in uint clr) nothrow @trusted @nogc {
1179 pragma(inline, true);
1180 gxDrawRoundedRect(rc.x0, rc.y0, rc.width, rc.height, radius, clr);
1184 // ////////////////////////////////////////////////////////////////////////// //
1185 __gshared usize frectXCoords; // array of ints
1186 __gshared usize frectXCSize; // in items
1189 /* cyclic dependency
1190 shared static ~this () {
1191 if (frectXCoords) {
1192 import core.stdc.stdlib : free;
1193 free(cast(void*)frectXCoords);
1194 frectXCoords = 0;
1200 int* ensureXCoords (int radius) nothrow @trusted @nogc {
1201 if (radius < 1) return null;
1202 if (radius > 1024*1024) return null;
1203 if (cast(usize)radius > frectXCSize) {
1204 import core.stdc.stdlib : realloc;
1205 immutable usize newsz = (cast(usize)radius|0x7fu)+1;
1206 void* np = realloc(cast(void*)frectXCoords, newsz*int.sizeof);
1207 if (np is null) return null; // out of memory
1208 frectXCSize = newsz;
1209 frectXCoords = cast(usize)np;
1211 return cast(int*)frectXCoords;
1215 // this is wrong, but i'm ok with it for now
1216 public void gxFillRoundedRect (int x0, int y0, int wdt, int hgt, int radius, in uint clr) nothrow @trusted @nogc {
1217 if (wdt < 1 || hgt < 1) return;
1218 if (radius < 1) { gxFillRect(x0, y0, wdt, hgt, clr); return; }
1219 if (gxIsTransparent(clr)) return;
1220 if (hgt == 1) { gxHLine(x0, y0, wdt, clr); return; }
1221 if (wdt == 1) { gxVLine(x0, y0, hgt, clr); return; }
1222 // fix radius
1223 immutable int minsz = (wdt < hgt ? wdt : hgt);
1224 if (radius >= (minsz>>1)) {
1225 radius = (minsz>>1)-1;
1226 if (radius < 1) { gxFillRect(x0, y0, wdt, hgt, clr); return; }
1229 // create border coords
1230 auto xpt = ensureXCoords(radius+1);
1231 if (xpt is null) { gxFillRect(x0, y0, wdt, hgt, clr); return; } // do at least something
1232 xpt[0..radius+1] = int.min;
1234 // create coords
1236 int error = -radius, x = radius, y = 0;
1237 while (x > y) {
1238 if (y <= radius && xpt[y] < x) xpt[y] = x;
1239 if (x >= 0 && x <= radius && xpt[x] < y) xpt[x] = y;
1240 error += y*2+1;
1241 ++y;
1242 if (error >= 0) { --x; error -= x*2; }
1244 if (y <= radius && xpt[y] < x) xpt[y] = x;
1245 if (x >= 0 && x <= radius && xpt[x] < y) xpt[x] = y;
1248 // draw the filled rect
1249 gxFillRect(x0, y0+radius+1, wdt, hgt-(radius<<1)-2, clr);
1251 // draw arc
1252 foreach (immutable dy; 0..radius+1) {
1253 if (xpt[dy] == int.min) continue;
1254 immutable topy = y0+radius-dy;
1255 immutable topx0 = x0+radius-xpt[dy];
1256 immutable topx1 = x0+wdt-radius-1+xpt[dy];
1257 //gxPutPixel(topx0, topy, clr);
1258 //gxPutPixel(topx1, topy, clr);
1259 gxHLine(topx0, topy, topx1-topx0+1, clr);
1260 immutable boty = y0+hgt-radius+dy-1;
1261 //gxPutPixel(topx0, boty, clr);
1262 //gxPutPixel(topx1, boty, clr);
1263 gxHLine(topx0, boty, topx1-topx0+1, clr);
1267 public void gxFillRoundedRect() (in auto ref GxRect rc, in int radius, in uint clr) nothrow @trusted @nogc {
1268 pragma(inline, true);
1269 gxFillRoundedRect(rc.x0, rc.y0, rc.width, rc.height, radius, clr);
1273 // ////////////////////////////////////////////////////////////////////////// //
1274 // bresenham with clipping
1275 // the idea is that we can simply skip the right number of steps
1276 // if the point is off the drawing area
1277 public void gxDrawLine (int x0, int y0, int x1, int y1, in uint clr, bool lastPoint=true) nothrow @trusted @nogc {
1278 enum swap(string a, string b) = "{immutable int tmp_="~a~";"~a~"="~b~";"~b~"=tmp_;}";
1280 if (gxIsTransparent(clr)) return;
1282 GxRect realClip = gxClipRect;
1283 if (!realClip.intersect(VBufWidth, VBufHeight)) return;
1285 // just a point?
1286 if (x0 == x1 && y0 == y1) {
1287 if (lastPoint) gxPutPixel(x0, y0, clr);
1288 return;
1291 // horizontal line?
1292 if (y0 == y1) {
1293 if (x0 > x1) {
1294 gxHLine(x1+(lastPoint ? 0 : 1), y0, x0-x1+(lastPoint ? 1 : 0), clr);
1295 } else {
1296 gxHLine(x0, y0, x1-x0+(lastPoint ? 1 : 0), clr);
1298 return;
1301 // clip rectange
1302 int wx0 = realClip.x0, wy0 = realClip.y0, wx1 = realClip.x1, wy1 = realClip.y1;
1303 if (wx0 > wx1 || wy0 > wy1) return; // this should not happen, but...
1305 // vertical setup; always go from top to bottom, so we'll draw the same line regardless of the starting point
1306 bool skipFirst = false;
1307 if (y0 > y1) {
1308 // swap endpoints
1309 if (!lastPoint) skipFirst = lastPoint = true;
1310 mixin(swap!("x0", "x1"));
1311 mixin(swap!("y0", "y1"));
1313 if (y0 > wy1 || y1 < wy0) return; // out of clip rectange
1314 int sty = 1; // "step sign" for x axis; we still need the var, because there is a possible swap down there
1316 // horizontal setup
1317 int stx = void; // "step sign" for x axis
1318 if (x0 < x1) {
1319 // from left to right
1320 if (x0 > wx1 || x1 < wx0) return; // out of clip rectange
1321 stx = 1; // going right
1322 } else {
1323 // from right to left
1324 if (x1 > wx1 || x0 < wx0) return; // out of clip rectange
1325 stx = -1; // going left
1326 x0 = -x0;
1327 x1 = -x1;
1328 wx0 = -wx0;
1329 wx1 = -wx1;
1330 mixin(swap!("wx0", "wx1"));
1333 int dsx = x1-x0; // "length" for x axis
1334 int dsy = y1-y0; // "length" for y axis
1335 int xd = void, yd = void; // current coord
1336 bool xyswapped = false; // if `true`, `xd` and `yd` are swapped
1337 if (dsx < dsy) {
1338 xyswapped = true;
1339 mixin(swap!("x0", "y0"));
1340 mixin(swap!("x1", "y1"));
1341 mixin(swap!("dsx", "dsy"));
1342 mixin(swap!("wx0", "wy0"));
1343 mixin(swap!("wx1", "wy1"));
1344 mixin(swap!("stx", "sty"));
1346 xd = x0;
1347 yd = y0;
1348 int dx2 = 2*dsx; // "double length" for x axis
1349 int dy2 = 2*dsy; // "double length" for y axis
1350 int e = 2*dsy-dsx; // "error" (as in bresenham algo)
1351 int term = x1; // termination point
1352 bool xfixed = false; // will be set if we properly fixed x0 coord while fixing the y0
1353 // note that clipping can overflow for insane coords
1354 // if you are completely sure that it can't happen, you can use `int` instead of `long`
1355 if (y0 < wy0) {
1356 // clip at top
1357 immutable long temp = cast(long)dx2*(wy0-y0)-dsx;
1358 xd += cast(int)(temp/dy2);
1359 if (xd > wx1) return; // x is moved out of clipping rect, nothing to do
1360 immutable int rem = cast(int)(temp%dy2);
1361 if (xd+(rem > 0 ? 1 : 0) >= wx0) {
1362 xfixed = true; // startx is inside the clipping rect, no need to perform left clip
1363 yd = wy0;
1364 e -= rem+dsx;
1365 if (rem > 0) { ++xd; e += dy2; }
1368 if (!xfixed && x0 < wx0) {
1369 // clip at left
1370 immutable long temp = cast(long)dy2*(wx0-x0);
1371 yd += cast(int)(temp/dx2);
1372 immutable int rem = cast(int)(temp%dx2);
1373 if (yd > wy1 || (yd == wy1 && rem >= dsx)) return; // y is moved out of clipping rect, nothing to do
1374 xd = wx0;
1375 e += rem;
1376 if (rem >= dsx) { ++yd; e -= dx2; }
1378 if (y1 > wy1) {
1379 // clip at bottom
1380 immutable long temp = cast(long)dx2*(wy1-y0)+dsx;
1381 term = x0+cast(int)(temp/dy2);
1382 // it should be safe to decrement here
1383 if (cast(int)(temp%dy2) == 0) --term;
1385 if (term > wx1) term = wx1; // clip at right
1387 if (sty == -1) yd = -yd;
1388 if (stx == -1) { xd = -xd; term = -term; }
1389 dx2 -= dy2;
1391 if (lastPoint) term += stx;
1392 if (skipFirst) {
1393 if (term == xd) return;
1394 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
1395 xd += stx;
1398 // draw it; `putPixel()` can omit checks
1399 if (gxIsSolid(clr)) {
1400 while (xd != term) {
1401 // inlined putpixel
1402 *cast(uint*)((cast(ubyte*)vglTexBuf)+(xyswapped ? xd*VBufWidth+yd : yd*VBufWidth+xd)) = clr;
1403 // done drawing, move coords
1404 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
1405 xd += stx;
1407 } else {
1408 while (xd != term) {
1409 // inlined putpixel
1410 uint* dp = cast(uint*)((cast(ubyte*)vglTexBuf)+(xyswapped ? xd*VBufWidth+yd : yd*VBufWidth+xd));
1411 mixin(GxColMixMixin!("*dp", "*dp", "clr"));
1412 // done drawing, move coords
1413 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
1414 xd += stx;
1419 public void gxDrawLine() (in auto ref GxPoint p0, in int x1, in int y1, in uint clr, in bool lastPoint=true) { pragma(inline, true); gxDrawLine(p0.x, p0.y, x1, y1, clr, lastPoint); }
1420 public void gxDrawLine() (in int x0, in int y0, auto ref GxPoint p1, in uint clr, in bool lastPoint=true) { pragma(inline, true); gxDrawLine(x0, y0, p1.x, p1.y, clr, lastPoint); }
1421 public void gxDrawLine() (in auto ref GxPoint p0, auto ref GxPoint p1, in uint clr, in bool lastPoint=true) { pragma(inline, true); gxDrawLine(p0.x, p0.y, p1.x, p1.y, clr, lastPoint); }
1424 // ////////////////////////////////////////////////////////////////////////// //
1425 version (DigitalMars) {
1426 version(X86) {
1427 version = EGRA_USE_SSE_MEMFILL_DW;
1430 // size is in dwords
1432 version(EGRA_USE_SSE_MEMFILL_DW) {
1433 // for x86 naked functions, DMD will pass last arg in EAX
1434 public void memFillDW (void* mptr, in uint value, in int count) nothrow @trusted @nogc {
1435 asm nothrow @trusted @nogc {
1436 naked;
1437 mov ECX,EAX; // ECX=count (because last arg is in EAX)
1438 mov EAX,SS:[ESP+8]; // mptr
1439 mov SS:[ESP+8],EDI; // save EDI
1440 mov EDI,EAX; // EDI=mptr
1441 mov EAX,SS:[ESP+4]; // EAX=value
1442 cmp ECX,0;
1443 jle done;
1444 test EDI,0x03;
1445 jnz simplestore; // oops, unaligned
1446 cmp ECX,12;
1447 jbe simplestore; // too small
1448 // the following loop cannot make ECX zero or less
1449 alignloop:
1450 test EDI,0x0f;
1451 jz alignedok;
1452 stosd;
1453 dec ECX;
1454 jmp alignloop;
1455 alignedok:
1456 cmp ECX,12;
1457 jbe simplestore; // too small
1458 // EAX: value
1459 // EDI: destination
1460 // ECX: count
1461 // store 4 values, so we could load them to XMM
1462 stosd;
1463 stosd;
1464 stosd;
1465 stosd;
1466 // use EDX as a counter/4
1467 mov EDX,ECX;
1468 shr EDX,2;
1469 dec EDX;
1470 // prepare XMM0
1471 movaps XMM0,[EDI-16];
1472 storeloop:
1473 movaps [EDI],XMM0;
1474 add EDI,16;
1475 dec EDX;
1476 jnz storeloop;
1478 and ECX,0x03;
1479 jz done;
1480 simplestore:
1481 rep; stosd;
1482 done:
1483 // restore EDI
1484 mov EDI,SS:[ESP+8];
1485 ret 8;
1488 } else {
1489 public void memFillDW (void* ptr, in uint value, in int count) nothrow @trusted @nogc {
1490 pragma(inline, true);
1491 if (count > 0) (cast(uint*)ptr)[0..cast(uint)count] = value;