egra: added some useful primitives to agg drawer
[iv.d.git] / egra / gfx / base.d
blobe283ee6b2789e52a3062f7db94a718e2f3cc643b
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;
27 import iv.egra.gfx.lowlevel;
30 // ////////////////////////////////////////////////////////////////////////// //
31 package __gshared int VBufWidth = 740*2;
32 package __gshared int VBufHeight = 520*2;
33 package __gshared ubyte vbufEffScale = 1; // effective (current) window scale
35 // framebuffer, BGRA format
36 package __gshared uint* vglTexBuf;
39 // ////////////////////////////////////////////////////////////////////////// //
40 public @property int screenEffScale () nothrow @trusted @nogc { pragma(inline, true); return vbufEffScale; }
41 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; }
43 public @property int screenWidth () nothrow @trusted @nogc { pragma(inline, true); return VBufWidth; }
44 public @property int screenHeight () nothrow @trusted @nogc { pragma(inline, true); return VBufHeight; }
46 public @property int screenWidthScaled () nothrow @trusted @nogc { pragma(inline, true); return VBufWidth*vbufEffScale; }
47 public @property int screenHeightScaled () nothrow @trusted @nogc { pragma(inline, true); return VBufHeight*vbufEffScale; }
50 // ////////////////////////////////////////////////////////////////////////// //
51 public enum GxDir {
52 Horiz,
53 Vert,
57 // ////////////////////////////////////////////////////////////////////////// //
58 public struct GxPoint {
59 public:
60 int x, y;
62 nothrow @safe @nogc:
63 this() (in auto ref GxPoint p) pure { pragma(inline, true); x = p.x; y = p.y; }
64 this (in int ax, in int ay) pure { pragma(inline, true); x = ax; y = ay; }
66 bool inside() (in auto ref GxRect rc) pure const { pragma(inline, true); return rc.inside(this); }
67 bool inside() (in auto ref GxSize sz) pure const { pragma(inline, true); return sz.inside(this); }
69 bool opEquals() (in auto ref GxPoint p) pure const { pragma(inline, true); return !((p.x^x)|(p.y^y)); }
71 int opCmp() (in auto ref GxPoint p) pure const {
72 pragma(inline, true);
73 return
74 y < p.y ? -1 :
75 y > p.y ? +1 :
76 x < p.x ? -1 :
77 x > p.x ? +1 :
81 void opAssign() (in auto ref GxPoint p) { pragma(inline, true); x = p.x; y = p.y; }
83 void opOpAssign(string op) (in int v) if (op == "+" || op == "-" || op == "*" || op == "/") {
84 pragma(inline, true);
85 mixin("x"~op~"=v; y"~op~"=v;");
88 void opOpAssign(string op) (in auto ref GxPoint pt) if (op == "+" || op == "-") {
89 pragma(inline, true);
90 mixin("x"~op~"=pt.x; y"~op~"=pt.y;");
93 GxPoint opBinary(string op) (in auto ref GxPoint pt) pure const if (op == "+" || op == "-") {
94 pragma(inline, true);
95 mixin("return GxPoint(x"~op~"pt.x, y"~op~"pt.y);");
98 GxPoint opBinary(string op) (in auto ref GxSize sz) pure const if (op == "+" || op == "-") {
99 pragma(inline, true);
100 mixin("return GxPoint(x"~op~"sz.w, y"~op~"sz.h);");
103 GxPoint opBinary(string op) (in int v) pure const if (op == "+" || op == "-") {
104 pragma(inline, true);
105 mixin("return GxPoint(x"~op~"v, y"~op~"v);");
108 int opIndex (in GxDir dir) pure const { pragma(inline, true); return (dir == GxDir.Horiz ? x : y); }
110 void opIndexAssign (in int v, in GxDir dir) { pragma(inline, true); if (dir == GxDir.Horiz) x = v; else y = v; }
112 void opIndexOpAssign(string op) (in int v, in GxDir dir) if (op == "+" || op == "-") {
113 pragma(inline, true);
114 if (dir == GxDir.Horiz) mixin("x "~op~"= v;"); else mixin("y "~op~"= v;");
119 // ////////////////////////////////////////////////////////////////////////// //
120 public struct GxSize {
121 public:
122 int w, h;
124 nothrow @safe @nogc:
125 @property bool valid () pure const { pragma(inline, true); return (w >= 0 && h >= 0); }
126 @property bool empty () pure const { pragma(inline, true); return (w < 1 || h < 1); }
128 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); }
130 void sanitize () { pragma(inline, true); if (w < 0) w = 0; if (h < 0) h = 0; }
132 void opOpAssign(string op) (in int v) if (op == "+" || op == "-" || op == "*" || op == "/") {
133 pragma(inline, true);
134 mixin("w"~op~"=v; h"~op~"=v;");
137 void opOpAssign(string op) (in auto ref GxSize sz) if (op == "+" || op == "-") {
138 pragma(inline, true);
139 mixin("w"~op~"=sz.w; h"~op~"=sz.h;");
142 GxSize opBinary(string op) (in auto ref GxSize sz) pure const if (op == "+" || op == "-") {
143 pragma(inline, true);
144 mixin("return GxSize(w"~op~"sz.w, h"~op~"sz.h);");
147 int opIndex (in GxDir dir) pure const { pragma(inline, true); return (dir == GxDir.Horiz ? w : h); }
149 void opIndexAssign (in int v, in GxDir dir) { pragma(inline, true); if (dir == GxDir.Horiz) w = v; else h = v; }
151 //FIXME: overflow!
152 void opIndexOpAssign(string op) (in int v, in GxDir dir) if (op == "+" || op == "-") {
153 pragma(inline, true);
154 if (dir == GxDir.Horiz) {
155 mixin("w "~op~"= v;");
156 if (w < 0) w = 0;
157 } else {
158 mixin("h "~op~"= v;");
159 if (h < 0) h = 0;
165 // ////////////////////////////////////////////////////////////////////////// //
166 public struct GxRect {
167 public:
168 GxPoint pos;
169 GxSize size = GxSize(-1, -1); // <0: invalid rect
171 string toString () const @trusted nothrow {
172 if (valid) {
173 import core.stdc.stdio : snprintf;
174 char[128] buf = void;
175 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;
176 } else {
177 return "(invalid-rect)";
181 nothrow @safe @nogc:
182 this() (in auto ref GxRect rc) pure {
183 pragma(inline, true);
184 pos = rc.pos;
185 size = rc.size;
188 this (in int ax0, in int ay0, in int awidth, in int aheight) pure {
189 pragma(inline, true);
190 pos.x = ax0;
191 pos.y = ay0;
192 size.w = awidth;
193 size.h = aheight;
196 this() (in int ax0, in int ay0, in auto ref GxSize asize) pure {
197 pragma(inline, true);
198 pos.x = ax0;
199 pos.y = ay0;
200 size = asize;
203 this() (in auto ref GxPoint xy0, in int awidth, in int aheight) pure {
204 pragma(inline, true);
205 pos = xy0;
206 size.w = awidth;
207 size.h = aheight;
210 this() (in auto ref GxPoint xy0, in auto ref GxSize asize) pure {
211 pragma(inline, true);
212 pos = xy0;
213 size = asize;
216 this() (in auto ref GxPoint xy0, in auto ref GxPoint xy1) pure {
217 pragma(inline, true);
218 pos = xy0;
219 size.w = xy1.x-xy0.x+1;
220 size.h = xy1.y-xy0.y+1;
223 this (in int awidth, in int aheight) pure {
224 pragma(inline, true);
225 pos.x = pos.y = 0;
226 size.w = awidth;
227 size.h = aheight;
230 this() (in auto ref GxSize asize) pure {
231 pragma(inline, true);
232 pos.x = pos.y = 0;
233 size = asize;
236 void setCoords(bool doSwap=true) (in int ax0, in int ay0, in int ax1, in int ay1) {
237 pragma(inline, true);
238 static if (doSwap) {
239 pos.x = (ax0 < ax1 ? ax0 : ax1);
240 pos.y = (ay0 < ay1 ? ay0 : ay1);
241 size.w = (ax0 < ax1 ? ax1 : ax0)-pos.x+1;
242 size.h = (ay0 < ay1 ? ay1 : ay0)-pos.y+1;
243 } else {
244 pos.x = ax0;
245 pos.y = ay0;
246 size.w = ax1-ax0+1;
247 size.h = ay1-ay0+1;
251 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); }
252 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); }
253 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); }
255 void opAssign() (in auto ref GxRect rc) { pragma(inline, true); pos = rc.pos; size = rc.size; }
257 bool opEquals() (in auto ref GxRect rc) pure const { pragma(inline, true); return (pos == rc.pos && size == rc.size); }
259 int opCmp() (in auto ref GxRect p) pure const { pragma(inline, true); return pos.opCmp(p.pos); }
261 @property bool valid () pure const { pragma(inline, true); return size.valid; }
262 @property bool invalid () pure const { pragma(inline, true); return !size.valid; }
263 @property bool empty () pure const { pragma(inline, true); return size.empty; } // invalid rects are empty
265 void invalidate () { pragma(inline, true); size.w = size.h = -1; }
267 @property int left () pure const { pragma(inline, true); return pos.x; }
268 @property void left (in int v) { pragma(inline, true); pos.x = v; }
270 @property int top () pure const { pragma(inline, true); return pos.y; }
271 @property void top (in int v) { pragma(inline, true); pos.y = v; }
273 @property int right () pure const { pragma(inline, true); return pos.x+size.w-1; }
274 @property void right (in int v) { pragma(inline, true); size.w = v-pos.x+1; }
276 @property int bottom () pure const { pragma(inline, true); return pos.y+size.h-1; }
277 @property void bottom (in int v) { pragma(inline, true); size.h = v-pos.y+1; }
279 @property GxPoint lefttop () pure const { pragma(inline, true); return pos; }
280 @property GxPoint righttop () pure const { pragma(inline, true); return pos+GxSize(size.w-1, 0); }
281 @property GxPoint leftbottom () pure const { pragma(inline, true); return pos+GxSize(0, size.h-1); }
282 @property GxPoint rightbottom () pure const { pragma(inline, true); return pos+size-1; }
284 @property void lefttop() (in auto ref GxPoint p) { pragma(inline, true); setCoords!false(p, rightbottom); }
285 @property void rightbottom() (in auto ref GxPoint p) { pragma(inline, true); setCoords!false(lefttop, p); }
287 alias topleft = lefttop;
288 alias topright = righttop;
289 alias bottomleft = leftbottom;
290 alias bottomright = rightbottom;
292 @property int x0 () pure const { pragma(inline, true); return pos.x; }
293 @property int y0 () pure const { pragma(inline, true); return pos.y; }
295 @property void x0 (in int val) { pragma(inline, true); setCoords!false(val, pos.y, rightbottom); }
296 @property void y0 (in int val) { pragma(inline, true); setCoords!false(pos.x, val, rightbottom); }
298 @property int x1 () pure const { pragma(inline, true); return (width > 0 ? x0+width-1 : x0-1); }
299 @property int y1 () pure const { pragma(inline, true); return (height > 0 ? y0+height-1 : y0-1); }
301 @property void x1 (in int val) { pragma(inline, true); width = val-x0+1; }
302 @property void y1 (in int val) { pragma(inline, true); height = val-y0+1; }
304 @property int width () pure const { pragma(inline, true); return size.w; }
305 @property int height () pure const { pragma(inline, true); return size.h; }
307 @property void width (in int val) { pragma(inline, true); size.w = val; }
308 @property void height (in int val) { pragma(inline, true); size.h = val; }
310 // is point inside this rect?
311 bool inside() (in auto ref GxPoint p) pure const {
312 pragma(inline, true);
313 return (width > 0 && height > 0 ? (p.x >= x0 && p.y >= y0 && p.x < x0+width && p.y < y0+height) : false);
316 // is point inside this rect?
317 bool inside (in int ax, in int ay) pure const {
318 pragma(inline, true);
319 return (width > 0 && height > 0 ? (ax >= x0 && ay >= y0 && ax < x0+width && ay < y0+height) : false);
322 // is `r` inside `this`?
323 bool contains() (in auto ref GxRect r) pure const {
324 pragma(inline, true);
325 return
326 width > 0 && height > 0 &&
327 r.width > 0 && r.height > 0 &&
328 r.x0 >= x0 && r.y0 >= y0 &&
329 r.x0+r.width <= x0+width && r.y0+r.height <= y0+height;
332 // does `r` and `this` overlap?
333 bool overlaps() (in auto ref GxRect r) pure const {
334 pragma(inline, true);
335 return
336 width > 0 && height > 0 &&
337 r.width > 0 && r.height > 0 &&
338 x0 < r.x0+r.width && r.x0 < x0+width &&
339 y0 < r.y0+r.height && r.y0 < y0+height;
342 // extend `this` so it will include `p`
343 void include() (in auto ref GxPoint p) {
344 pragma(inline, true);
345 if (empty) {
346 x0 = p.x;
347 y0 = p.y;
348 width = 1;
349 height = 1;
350 } else {
351 if (p.x < x0) x0 = p.x0;
352 if (p.y < y0) y0 = p.y0;
353 if (p.x1 > x1) x1 = p.x1;
354 if (p.y1 > y1) y1 = p.y1;
358 // extend `this` so it will include `r`
359 void include() (in auto ref GxRect r) {
360 pragma(inline, true);
361 if (!r.empty) {
362 if (empty) {
363 x0 = r.x;
364 y0 = r.y;
365 width = r.width;
366 height = r.height;
367 } else {
368 if (r.x < x0) x0 = r.x0;
369 if (r.y < y0) y0 = r.y0;
370 if (r.x1 > x1) x1 = r.x1;
371 if (r.y1 > y1) y1 = r.y1;
376 // clip `this` so it will not be larger than `r`
377 // returns `false` if the resulting rect (this) is empty or invalid
378 bool intersect (in int rx0, in int ry0, in int rwdt, in int rhgt) {
379 if (rwdt < 0 || rhgt < 0 || invalid) { size.w = size.h = -1; return false; }
380 if (rwdt == 0 || rhgt == 0 || empty) { size.w = size.h = 0; return false; }
381 immutable int rx1 = rx0+rwdt-1;
382 immutable int ry1 = ry0+rhgt-1;
383 if (ry1 < y0 || rx1 < x0 || rx0 > x1 || ry0 > y1) { size.w = size.h = 0; return false; }
384 // rc is at least partially inside this rect
385 if (x0 < rx0) x0 = rx0;
386 if (y0 < ry0) y0 = ry0;
387 if (x1 > rx1) x1 = rx1;
388 if (y1 > ry1) y1 = ry1;
389 assert(!empty); // yeah, always
390 return true;
393 // clip `this` so it will not be larger than `r`
394 // returns `false` if the resulting rect (this) is empty or invalid
395 bool intersect (in int rwdt, in int rhgt) {
396 pragma(inline, true);
397 return intersect(0, 0, rwdt, rhgt);
400 // clip `this` so it will not be larger than `r`
401 // returns `false` if the resulting rect (this) is empty or invalid
402 bool intersect() (in auto ref GxRect r) {
403 pragma(inline, true);
404 return intersect(r.x0, r.y0, r.width, r.height);
407 void shrinkBy (in int dx, in int dy) {
408 pragma(inline, true);
409 if ((dx|dy) && valid) {
410 pos.x += dx;
411 pos.y += dy;
412 size.w -= dx<<1;
413 size.h -= dy<<1;
417 void shrinkBy() (in auto ref GxSize sz) { pragma(inline, true); shrinkBy(sz.w, sz.h); }
419 void growBy (in int dx, in int dy) {
420 pragma(inline, true);
421 if ((dx|dy) && valid) {
422 pos.x -= dx;
423 pos.y -= dy;
424 size.w += dx<<1;
425 size.h += dy<<1;
429 void growBy() (in auto ref GxSize sz) { pragma(inline, true); growBy(sz.w, sz.h); }
431 void set (in int ax0, in int ay0, in int awidth, in int aheight) {
432 pragma(inline, true);
433 x0 = ax0;
434 y0 = ay0;
435 width = awidth;
436 height = aheight;
439 void set() (in auto ref GxPoint p0, in int awidth, in int aheight) { pragma(inline, true); set(p0.x, p0.y, awidth, aheight); }
440 void set() (in auto ref GxPoint p0, in auto ref GxSize asize) { pragma(inline, true); set(p0.x, p0.y, asize.w, asize.h); }
441 void set() (in int ax0, in int ay0, in auto ref GxSize asize) { pragma(inline, true); set(ax0, ay0, asize.w, asize.h); }
443 void moveLeftTopBy (in int dx, in int dy) {
444 pragma(inline, true);
445 pos.x += dx;
446 pos.y += dy;
447 size.w -= dx;
448 size.h -= dy;
451 void moveLeftTopBy() (in auto ref GxPoint p) { pragma(inline, true); moveLeftTopBy(p.x, p.y); }
453 alias moveTopLeftBy = moveLeftTopBy;
455 void moveRightBottomBy (in int dx, in int dy) {
456 pragma(inline, true);
457 size.w += dx;
458 size.h += dy;
461 void moveRightBottomBy() (in auto ref GxPoint p) { pragma(inline, true); moveRightBottomBy(p.x, p.y); }
463 alias moveBottomRightBy = moveRightBottomBy;
465 void moveBy (in int dx, in int dy) {
466 pragma(inline, true);
467 pos.x += dx;
468 pos.y += dy;
471 void moveBy() (in auto ref GxPoint p) { pragma(inline, true); moveBy(p.x, p.y); }
473 void moveTo (in int nx, in int ny) {
474 pragma(inline, true);
475 x0 = nx;
476 y0 = ny;
479 void moveTo() (in auto ref GxPoint p) { pragma(inline, true); moveTo(p.x, p.y); }
482 * clip (x,y,wdt) stripe to this rect
484 * Params:
485 * x = stripe start (not relative to rect)
486 * y = stripe start (not relative to rect)
487 * wdt = stripe length
489 * Returns:
490 * x = fixed x (invalid if result is false)
491 * wdt = fixed length (invalid if result is false)
492 * leftSkip = how much cells skipped at the left side (invalid if result is false)
493 * result = false if stripe is completely clipped out
495 bool clipHStripe (ref int x, int y, ref int wdt, int* leftSkip=null) const @trusted {
496 if (empty) return false;
497 if (wdt <= 0 || y < y0 || y >= y0+height || x >= x0+width) return false;
498 if (x < x0) {
499 // left clip
500 immutable int dx = x0-x;
501 if (dx >= wdt) return false; // avoid overflow
502 if (leftSkip !is null) *leftSkip = dx;
503 wdt -= dx;
504 x = x0;
505 assert(wdt > 0); // yeah, always
506 } else {
507 if (leftSkip !is null) *leftSkip = 0;
509 if (wdt > width) wdt = width; // avoid overflow
510 if (x+wdt > x0+width) {
511 // right clip
512 wdt = x0+width-x;
513 assert(wdt > 0); // yeah, always
515 return true;
518 bool clipHStripe (ref GxPoint p, ref int wdt, int* leftSkip=null) const @trusted {
519 pragma(inline, true);
520 return clipHStripe(ref p.x, p.y, ref wdt, leftSkip);
524 * clip (x,y,hgt) stripe to this rect
526 * Params:
527 * x = stripe start (not relative to rect)
528 * y = stripe start (not relative to rect)
529 * hgt = stripe length
531 * Returns:
532 * y = fixed y (invalid if result is false)
533 * hgt = fixed length (invalid if result is false)
534 * topSkip = how much cells skipped at the top side (invalid if result is false)
535 * result = false if stripe is completely clipped out
537 bool clipVStripe (int x, ref int y, ref int hgt, int* topSkip=null) const @trusted {
538 if (empty) return false;
539 if (hgt <= 0 || x < x0 || x >= x0+width || y >= y0+height) return false;
540 if (y < y0) {
541 // top clip
542 immutable int dy = y0-y;
543 if (dy >= hgt) return false; // avoid overflow
544 if (topSkip !is null) *topSkip = dy;
545 hgt -= dy;
546 y = y0;
547 assert(hgt > 0); // yeah, always
548 } else {
549 if (topSkip !is null) *topSkip = 0;
551 if (hgt > height) hgt = height; // avoid overflow
552 if (y+hgt > y0+height) {
553 // bottom clip
554 hgt = y0+height-y;
555 assert(hgt > 0); // yeah, always
557 return true;
560 bool clipVStripe (ref GxPoint p, ref int hgt, int* topSkip=null) const @trusted {
561 pragma(inline, true);
562 return clipVStripe(p.x, ref p.y, ref hgt, topSkip);
565 bool clipHVStripes (ref int x, ref int y, ref int wdt, ref int hgt, int* leftSkip=null, int* topSkip=null) const @trusted {
566 if (empty || wdt <= 0 || hgt <= 0) return false;
567 if (y >= y0+height || x >= x0+width) return false;
568 // use dummy `x` and `y` for horizontal and vertical clippers, because they are only checked for validity there
569 if (!clipHStripe(ref x, y0, ref wdt, leftSkip)) return false;
570 return clipVStripe(x0, ref y, ref hgt, topSkip);
573 bool clipHVStripes (ref GxPoint p, ref int wdt, ref int hgt, int* leftSkip=null, int* topSkip=null) const @trusted {
574 pragma(inline, true);
575 return clipHVStripes(ref p.x, ref p.y, ref wdt, ref hgt, leftSkip, topSkip);
578 bool clipHVStripes (ref GxPoint p, ref GxSize sz, int* leftSkip=null, int* topSkip=null) const @trusted {
579 pragma(inline, true);
580 return clipHVStripes(ref p.x, ref p.y, ref sz.w, ref sz.h, leftSkip, topSkip);
583 bool clipHVStripes (ref int x, ref int y, ref GxSize sz, int* leftSkip=null, int* topSkip=null) const @trusted {
584 pragma(inline, true);
585 return clipHVStripes(ref x, ref y, ref sz.w, ref sz.h, leftSkip, topSkip);
590 // ////////////////////////////////////////////////////////////////////////// //
591 public alias GxColor = uint;
593 public align(1) union GxColorU {
594 align(1):
595 uint clr;
596 ubyte[4] bgra;
597 align(1) struct {
598 align(1):
599 ubyte b, g, r, a;
601 this (in GxColor c) pure nothrow @safe @nogc { pragma(inline, true); clr = c; }
603 static assert(GxColorU.sizeof == GxColor.sizeof);
606 public enum gxSolidBlack = 0xff000000u;
607 public enum gxSolidWhite = 0xffffffffu;
609 public enum gxTransparent = 0x00000000u;
610 public enum gxColorMask = 0x00ffffffu;
611 public enum gxAlphaMask = 0xff000000u;
613 public enum gxUnknown = 0x00010203u;
615 public bool gxIsTransparent (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return !(clr&gxAlphaMask); }
616 public bool gxIsSolid (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return ((clr&gxAlphaMask) == gxAlphaMask); }
618 public bool gxIsSolidBlack (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return (clr == gxAlphaMask); }
620 public ubyte gxGetBlue (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)clr; }
621 public ubyte gxGetGreen (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)(clr>>8); }
622 public ubyte gxGetRed (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)(clr>>16); }
623 public ubyte gxGetAlpha (in uint clr) pure nothrow @safe @nogc { pragma(inline, true); return cast(ubyte)(clr>>24); }
626 public uint gxColMix (in uint dc, in uint clr) pure nothrow @trusted @nogc {
627 pragma(inline, true);
628 if (gxIsSolid(clr)) return clr;
629 else if (gxIsTransparent(clr)) return dc|0xff_00_00_00u;
630 else {
631 uint res = void;
632 mixin(GxColMixMixin!("res", "dc", "clr"));
633 return res;
638 // ////////////////////////////////////////////////////////////////////////// //
639 private template isGoodRGBInt(T) {
640 import std.traits : Unqual;
641 alias TT = Unqual!T;
642 enum isGoodRGBInt =
643 is(TT == ubyte) ||
644 is(TT == short) || is(TT == ushort) ||
645 is(TT == int) || is(TT == uint) ||
646 is(TT == long) || is(TT == ulong);
650 // ////////////////////////////////////////////////////////////////////////// //
651 public uint gxrgb(T0, T1, T2) (T0 r, T1 g, T2 b) pure nothrow @trusted @nogc
652 if (isGoodRGBInt!T0 && isGoodRGBInt!T1 && isGoodRGBInt!T2)
654 pragma(inline, true);
655 return (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b)|0xff000000u;
658 public uint gxrgba(T0, T1, T2, T3) (T0 r, T1 g, T2 b, T3 a) pure nothrow @trusted @nogc
659 if (isGoodRGBInt!T0 && isGoodRGBInt!T1 && isGoodRGBInt!T2 && isGoodRGBInt!T3)
661 pragma(inline, true);
662 return (clampToByte(a)<<24)|(clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
666 public enum gxRGB(int r, int g, int b) = (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b)|0xff000000u;
667 public enum gxRGBA(int r, int g, int b, int a) = (clampToByte(a)<<24)|(clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
670 // ////////////////////////////////////////////////////////////////////////// //
671 // namespace
672 public struct GxColors {
673 enum k8orange = gxRGB!(255, 127, 0);
675 enum aliceblue = gxRGB!(240, 248, 255);
676 enum antiquewhite = gxRGB!(250, 235, 215);
677 enum aqua = gxRGB!(0, 255, 255);
678 enum aquamarine = gxRGB!(127, 255, 212);
679 enum azure = gxRGB!(240, 255, 255);
680 enum beige = gxRGB!(245, 245, 220);
681 enum bisque = gxRGB!(255, 228, 196);
682 enum black = gxRGB!(0, 0, 0); // basic color
683 enum blanchedalmond = gxRGB!(255, 235, 205);
684 enum blue = gxRGB!(0, 0, 255); // basic color
685 enum blueviolet = gxRGB!(138, 43, 226);
686 enum brown = gxRGB!(165, 42, 42);
687 enum burlywood = gxRGB!(222, 184, 135);
688 enum cadetblue = gxRGB!(95, 158, 160);
689 enum chartreuse = gxRGB!(127, 255, 0);
690 enum chocolate = gxRGB!(210, 105, 30);
691 enum coral = gxRGB!(255, 127, 80);
692 enum cornflowerblue = gxRGB!(100, 149, 237);
693 enum cornsilk = gxRGB!(255, 248, 220);
694 enum crimson = gxRGB!(220, 20, 60);
695 enum cyan = gxRGB!(0, 255, 255); // basic color
696 enum darkblue = gxRGB!(0, 0, 139);
697 enum darkcyan = gxRGB!(0, 139, 139);
698 enum darkgoldenrod = gxRGB!(184, 134, 11);
699 enum darkgray = gxRGB!(169, 169, 169);
700 enum darkgreen = gxRGB!(0, 100, 0);
701 enum darkgrey = gxRGB!(169, 169, 169);
702 enum darkkhaki = gxRGB!(189, 183, 107);
703 enum darkmagenta = gxRGB!(139, 0, 139);
704 enum darkolivegreen = gxRGB!(85, 107, 47);
705 enum darkorange = gxRGB!(255, 140, 0);
706 enum darkorchid = gxRGB!(153, 50, 204);
707 enum darkred = gxRGB!(139, 0, 0);
708 enum darksalmon = gxRGB!(233, 150, 122);
709 enum darkseagreen = gxRGB!(143, 188, 143);
710 enum darkslateblue = gxRGB!(72, 61, 139);
711 enum darkslategray = gxRGB!(47, 79, 79);
712 enum darkslategrey = gxRGB!(47, 79, 79);
713 enum darkturquoise = gxRGB!(0, 206, 209);
714 enum darkviolet = gxRGB!(148, 0, 211);
715 enum deeppink = gxRGB!(255, 20, 147);
716 enum deepskyblue = gxRGB!(0, 191, 255);
717 enum dimgray = gxRGB!(105, 105, 105);
718 enum dimgrey = gxRGB!(105, 105, 105);
719 enum dodgerblue = gxRGB!(30, 144, 255);
720 enum firebrick = gxRGB!(178, 34, 34);
721 enum floralwhite = gxRGB!(255, 250, 240);
722 enum forestgreen = gxRGB!(34, 139, 34);
723 enum fuchsia = gxRGB!(255, 0, 255);
724 enum gainsboro = gxRGB!(220, 220, 220);
725 enum ghostwhite = gxRGB!(248, 248, 255);
726 enum gold = gxRGB!(255, 215, 0);
727 enum goldenrod = gxRGB!(218, 165, 32);
728 enum gray = gxRGB!(128, 128, 128); // basic color
729 enum green = gxRGB!(0, 128, 0); // basic color
730 enum greenyellow = gxRGB!(173, 255, 47);
731 enum grey = gxRGB!(128, 128, 128); // basic color
732 enum honeydew = gxRGB!(240, 255, 240);
733 enum hotpink = gxRGB!(255, 105, 180);
734 enum indianred = gxRGB!(205, 92, 92);
735 enum indigo = gxRGB!(75, 0, 130);
736 enum ivory = gxRGB!(255, 255, 240);
737 enum khaki = gxRGB!(240, 230, 140);
738 enum lavender = gxRGB!(230, 230, 250);
739 enum lavenderblush = gxRGB!(255, 240, 245);
740 enum lawngreen = gxRGB!(124, 252, 0);
741 enum lemonchiffon = gxRGB!(255, 250, 205);
742 enum lightblue = gxRGB!(173, 216, 230);
743 enum lightcoral = gxRGB!(240, 128, 128);
744 enum lightcyan = gxRGB!(224, 255, 255);
745 enum lightgoldenrodyellow = gxRGB!(250, 250, 210);
746 enum lightgray = gxRGB!(211, 211, 211);
747 enum lightgreen = gxRGB!(144, 238, 144);
748 enum lightgrey = gxRGB!(211, 211, 211);
749 enum lightpink = gxRGB!(255, 182, 193);
750 enum lightsalmon = gxRGB!(255, 160, 122);
751 enum lightseagreen = gxRGB!(32, 178, 170);
752 enum lightskyblue = gxRGB!(135, 206, 250);
753 enum lightslategray = gxRGB!(119, 136, 153);
754 enum lightslategrey = gxRGB!(119, 136, 153);
755 enum lightsteelblue = gxRGB!(176, 196, 222);
756 enum lightyellow = gxRGB!(255, 255, 224);
757 enum lime = gxRGB!(0, 255, 0);
758 enum limegreen = gxRGB!(50, 205, 50);
759 enum linen = gxRGB!(250, 240, 230);
760 enum magenta = gxRGB!(255, 0, 255); // basic color
761 enum maroon = gxRGB!(128, 0, 0);
762 enum mediumaquamarine = gxRGB!(102, 205, 170);
763 enum mediumblue = gxRGB!(0, 0, 205);
764 enum mediumorchid = gxRGB!(186, 85, 211);
765 enum mediumpurple = gxRGB!(147, 112, 219);
766 enum mediumseagreen = gxRGB!(60, 179, 113);
767 enum mediumslateblue = gxRGB!(123, 104, 238);
768 enum mediumspringgreen = gxRGB!(0, 250, 154);
769 enum mediumturquoise = gxRGB!(72, 209, 204);
770 enum mediumvioletred = gxRGB!(199, 21, 133);
771 enum midnightblue = gxRGB!(25, 25, 112);
772 enum mintcream = gxRGB!(245, 255, 250);
773 enum mistyrose = gxRGB!(255, 228, 225);
774 enum moccasin = gxRGB!(255, 228, 181);
775 enum navajowhite = gxRGB!(255, 222, 173);
776 enum navy = gxRGB!(0, 0, 128);
777 enum oldlace = gxRGB!(253, 245, 230);
778 enum olive = gxRGB!(128, 128, 0);
779 enum olivedrab = gxRGB!(107, 142, 35);
780 enum orange = gxRGB!(255, 165, 0);
781 enum orangered = gxRGB!(255, 69, 0);
782 enum orchid = gxRGB!(218, 112, 214);
783 enum palegoldenrod = gxRGB!(238, 232, 170);
784 enum palegreen = gxRGB!(152, 251, 152);
785 enum paleturquoise = gxRGB!(175, 238, 238);
786 enum palevioletred = gxRGB!(219, 112, 147);
787 enum papayawhip = gxRGB!(255, 239, 213);
788 enum peachpuff = gxRGB!(255, 218, 185);
789 enum peru = gxRGB!(205, 133, 63);
790 enum pink = gxRGB!(255, 192, 203);
791 enum plum = gxRGB!(221, 160, 221);
792 enum powderblue = gxRGB!(176, 224, 230);
793 enum purple = gxRGB!(128, 0, 128);
794 enum red = gxRGB!(255, 0, 0); // basic color
795 enum rosybrown = gxRGB!(188, 143, 143);
796 enum royalblue = gxRGB!(65, 105, 225);
797 enum saddlebrown = gxRGB!(139, 69, 19);
798 enum salmon = gxRGB!(250, 128, 114);
799 enum sandybrown = gxRGB!(244, 164, 96);
800 enum seagreen = gxRGB!(46, 139, 87);
801 enum seashell = gxRGB!(255, 245, 238);
802 enum sienna = gxRGB!(160, 82, 45);
803 enum silver = gxRGB!(192, 192, 192);
804 enum skyblue = gxRGB!(135, 206, 235);
805 enum slateblue = gxRGB!(106, 90, 205);
806 enum slategray = gxRGB!(112, 128, 144);
807 enum slategrey = gxRGB!(112, 128, 144);
808 enum snow = gxRGB!(255, 250, 250);
809 enum springgreen = gxRGB!(0, 255, 127);
810 enum steelblue = gxRGB!(70, 130, 180);
811 enum tan = gxRGB!(210, 180, 140);
812 enum teal = gxRGB!(0, 128, 128);
813 enum thistle = gxRGB!(216, 191, 216);
814 enum tomato = gxRGB!(255, 99, 71);
815 enum turquoise = gxRGB!(64, 224, 208);
816 enum violet = gxRGB!(238, 130, 238);
817 enum wheat = gxRGB!(245, 222, 179);
818 enum white = gxRGB!(255, 255, 255); // basic color
819 enum whitesmoke = gxRGB!(245, 245, 245);
820 enum yellow = gxRGB!(255, 255, 0); // basic color
821 enum yellowgreen = gxRGB!(154, 205, 50);
825 // A-HSL color
826 public align(1) struct GxColorHSL {
827 align(1):
828 float h=0.0f, s=0.0f, l=1.0f, a=1.0f;
830 string toString () const nothrow {
831 import core.stdc.stdio : snprintf;
832 char[64] buf = void;
833 if (a == 1.0f) {
834 immutable len = snprintf(buf.ptr, buf.length, "HSL(%g,%g,%g)", h, s, l);
835 return buf[0..len].idup;
836 } else {
837 immutable len = snprintf(buf.ptr, buf.length, "HSL(%g,%g,%g,%g)", h, s, l, a);
838 return buf[0..len].idup;
842 public static T clampval(T) (in T a, in T mn, in T mx) pure nothrow @trusted @nogc { pragma(inline, true); return (a < mn ? mn : a > mx ? mx : a); }
844 nothrow @safe @nogc:
845 private:
846 static float calchue (float h, in float m1, in float m2) pure nothrow @safe @nogc {
847 if (h < 0) h += 1;
848 if (h > 1) h -= 1;
849 if (h < 1.0f/6.0f) return m1+(m2-m1)*h*6.0f;
850 if (h < 3.0f/6.0f) return m2;
851 if (h < 4.0f/6.0f) return m1+(m2-m1)*(2.0f/3.0f-h)*6.0f;
852 return m1;
855 public:
856 this (in float ah, in float as, in float al, in float aa=1.0f) pure { pragma(inline, true); h = ah; s = as; l = al; a = aa; }
858 this (in uint clr) pure { pragma(inline, true); fromColor(clr); }
860 uint asColor () const {
861 import core.stdc.math : fmodf;
862 //static if (__VERSION__ >= 2072) pragma(inline, true);
863 float xh = fmodf(h, 1.0f);
864 if (xh < 0.0f) xh += 1.0f;
865 immutable float xs = clampval(s, 0.0f, 1.0f);
866 immutable float xl = clampval(l, 0.0f, 1.0f);
867 immutable m2 = (xl <= 0.5f ? xl*(1+xs) : xl+xs-xl*xs);
868 immutable m1 = 2*xl-m2;
869 return
870 (clampToByte(cast(int)(clampval(calchue(xh+1.0f/3.0f, m1, m2), 0.0f, 1.0f)*255.0f))<<16)|
871 (clampToByte(cast(int)(clampval(calchue(xh, m1, m2), 0.0f, 1.0f)*255.0f))<<8)|
872 clampToByte(cast(int)(clampval(calchue(xh-1.0f/3.0f, m1, m2), 0.0f, 1.0f)*255.0f))|
873 (clampToByte(cast(int)(clampval(a, 0.0, 1.0f)*255.0f))<<24);
876 // taken from Adam's arsd.color
877 /* Converts an RGB color into an HSL triplet.
878 * [useWeightedLightness] will try to get a better value for luminosity for the human eye,
879 * which is more sensitive to green than red and more to red than blue.
880 * If it is false, it just does average of the rgb. */
881 void fromColor (in uint c, bool useWeightedLightness=false) pure @trusted {
882 this.a = gxGetAlpha(c)/255.0f;
883 immutable float r1 = gxGetRed(c)/255.0f;
884 immutable float g1 = gxGetGreen(c)/255.0f;
885 immutable float b1 = gxGetBlue(c)/255.0f;
887 float maxColor = r1;
888 if (g1 > maxColor) maxColor = g1;
889 if (b1 > maxColor) maxColor = b1;
890 float minColor = r1;
891 if (g1 < minColor) minColor = g1;
892 if (b1 < minColor) minColor = b1;
894 this.l = (maxColor+minColor)*0.5f;
895 if (useWeightedLightness) {
896 // the colors don't affect the eye equally
897 // this is a little more accurate than plain HSL numbers
898 this.l = 0.2126*r1+0.7152*g1+0.0722*b1;
900 if (maxColor != minColor) {
901 if (this.l < 0.5f) {
902 this.s = (maxColor-minColor)/(maxColor+minColor);
903 } else {
904 this.s = (maxColor-minColor)/(2.0f-maxColor-minColor);
906 if (r1 == maxColor) {
907 this.h = (g1-b1)/(maxColor-minColor);
908 } else if (g1 == maxColor) {
909 this.h = 2.0f+(b1-r1)/(maxColor-minColor);
910 } else {
911 this.h = 4.0f+(r1-g1)/(maxColor-minColor);
915 this.h = this.h*60.0f;
916 if (this.h < 0.0f) this.h += 360.0f;
917 this.h /= 360.0f;
922 // ////////////////////////////////////////////////////////////////////////// //
923 // current clip rect
924 public __gshared GxRect gxClipRect = GxRect(65535, 65535);
926 public void gxWithSavedClip(DG) (scope DG dg)
927 if (is(typeof((inout int=0) { DG dg = void; dg(); })))
929 pragma(inline, true);
930 if (dg !is null) {
931 immutable rc = gxClipRect;
932 scope(exit) gxClipRect = rc;
933 dg();
937 public void gxClipReset () nothrow @trusted @nogc {
938 pragma(inline, true);
939 gxClipRect = GxRect(VBufWidth, VBufHeight);
943 // ////////////////////////////////////////////////////////////////////////// //
944 public void gxClearScreen (uint clr) nothrow @trusted @nogc {
945 clr &= gxColorMask; // so we could blit OpenGL texture with blending
946 memFillDW(vglTexBuf, clr, VBufWidth*VBufHeight);
950 public void gxPutPixel (in int x, in int y, in uint c) nothrow @trusted @nogc {
951 pragma(inline, true);
952 if (x >= 0 && y >= 0 && x < VBufWidth && y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(x, y)) {
953 uint* dp = cast(uint*)(cast(ubyte*)vglTexBuf)+y*VBufWidth+x;
954 *dp = gxColMix(*dp, c);
959 public void gxPutPixel() (in auto ref GxPoint p, in uint c) nothrow @trusted @nogc {
960 pragma(inline, true);
961 if (p.x >= 0 && p.y >= 0 && p.x < VBufWidth && p.y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(p)) {
962 uint* dp = cast(uint*)(cast(ubyte*)vglTexBuf)+p.y*VBufWidth+p.x;
963 *dp = gxColMix(*dp, c);
968 public void gxSetPixel (in int x, in int y, in uint c) nothrow @trusted @nogc {
969 pragma(inline, true);
970 if (x >= 0 && y >= 0 && x < VBufWidth && y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(x, y)) {
971 *(cast(uint*)(cast(ubyte*)vglTexBuf)+y*VBufWidth+x) = c|gxAlphaMask;
976 public void gxSetPixel() (in auto ref GxPoint p, in uint c) nothrow @trusted @nogc {
977 pragma(inline, true);
978 if (p.x >= 0 && p.y >= 0 && p.x < VBufWidth && p.y < VBufHeight && !gxIsTransparent(c) && gxClipRect.inside(p)) {
979 *(cast(uint*)(cast(ubyte*)vglTexBuf)+p.y*VBufWidth+p.x) = c|gxAlphaMask;
984 // ////////////////////////////////////////////////////////////////////////// //
985 public void gxHStripedLine (int x, int y, int w, in int stlen, in uint stclr) nothrow @trusted @nogc {
986 if (stlen < 1 || w < 1) return;
987 if (gxIsTransparent(stclr)) return;
988 immutable int xstart = x;
989 if (!gxClipRect.clipHStripe(x, y, w)) return;
990 if (!GxRect(VBufWidth, VBufHeight).clipHStripe(x, y, w)) return;
991 immutable uint stFull = cast(uint)stlen<<1;
992 int sofs = cast(uint)(x-xstart)%stFull;
993 uint* dptr = vglTexBuf+y*VBufWidth+x;
994 if (gxIsSolid(stclr)) {
995 if (sofs) {
996 // fill
997 uint nlen = void;
998 if (sofs < stlen) {
999 nlen = cast(uint)(stlen-sofs);
1000 if (nlen >= cast(uint)w) {
1001 memFillDW(dptr, stclr, w);
1002 return;
1004 dptr = memFillDW(dptr, stclr, cast(int)nlen);
1005 w -= nlen;
1006 sofs = stlen;
1008 // skip
1009 assert(sofs >= stlen && sofs < cast(int)stFull);
1010 nlen = stFull-cast(uint)sofs;
1011 if (nlen >= cast(uint)w) return;
1012 dptr += nlen;
1013 w -= cast(int)nlen;
1014 // done
1015 sofs = 0;
1017 // rest
1018 while (w > 0) {
1019 if (stlen >= w) {
1020 memFillDW(dptr, stclr, w);
1021 return;
1023 memFillDW(dptr, stclr, stlen);
1024 if ((w -= stlen) <= 0) return;
1025 w -= stlen;
1026 dptr += stFull;
1028 } else {
1029 if (sofs) {
1030 // fill
1031 uint nlen = void;
1032 if (sofs < stlen) {
1033 nlen = cast(uint)(stlen-sofs);
1034 if (nlen >= cast(uint)w) {
1035 //dptr[0..cast(uint)w] = stclr;
1036 memBlendColor(dptr, stclr, w);
1037 return;
1039 //dptr[0..nlen] = stclr;
1040 dptr = memBlendColor(dptr, stclr, cast(int)nlen);
1041 w -= nlen;
1042 sofs = stlen;
1044 // skip
1045 assert(sofs >= stlen && sofs < cast(int)stFull);
1046 nlen = stFull-cast(uint)sofs;
1047 if (nlen >= cast(uint)w) return;
1048 dptr += nlen;
1049 w -= cast(int)nlen;
1050 // done
1051 sofs = 0;
1053 // rest
1054 while (w > 0) {
1055 if (stlen >= w) {
1056 //dptr[0..cast(uint)w] = stclr;
1057 memBlendColor(dptr, stclr, w);
1058 return;
1060 //dptr[0..cast(uint)stlen] = stclr;
1061 memBlendColor(dptr, stclr, stlen);
1062 if ((w -= stlen) <= 0) return;
1063 w -= stlen;
1064 dptr += stFull;
1069 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); }
1072 // ////////////////////////////////////////////////////////////////////////// //
1073 public void gxHLine (int x, int y, int w, in uint clr) nothrow @trusted @nogc {
1074 if (gxIsTransparent(clr)) return;
1075 if (!gxClipRect.clipHStripe(x, y, w)) return;
1076 if (!GxRect(VBufWidth, VBufHeight).clipHStripe(x, y, w)) return;
1077 if (gxIsSolid(clr)) {
1078 memFillDW(vglTexBuf+y*VBufWidth+x, clr, w);
1079 } else {
1080 memBlendColor(vglTexBuf+y*VBufWidth+x, clr, w);
1084 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); }
1086 public void gxVLine (int x, int y, int h, in uint clr) nothrow @trusted @nogc {
1087 if (gxIsTransparent(clr)) return;
1088 if (!gxClipRect.clipVStripe(x, y, h)) return;
1089 if (!GxRect(VBufWidth, VBufHeight).clipVStripe(x, y, h)) return;
1090 uint* dptr = vglTexBuf+y*VBufWidth+x;
1091 if (gxIsSolid(clr)) {
1092 while (h-- > 0) { *dptr = clr; dptr += VBufWidth; }
1093 } else {
1094 while (h-- > 0) { mixin(GxColMixMixin!("*dptr", "*dptr", "clr")); dptr += VBufWidth; }
1098 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); }
1101 // ////////////////////////////////////////////////////////////////////////// //
1102 public void gxDashRect (int x, int y, int w, int h, in uint clr) nothrow @trusted @nogc {
1103 if (gxIsTransparent(clr)) return;
1104 if (!gxClipRect.clipHVStripes(x, y, w, h)) return;
1105 if (!GxRect(VBufWidth, VBufHeight).clipHVStripes(x, y, w, h)) return;
1106 uint* dptr = vglTexBuf+y*VBufWidth+x;
1107 immutable uint dinc = VBufWidth-w;
1108 if (gxIsSolid(clr)) {
1109 if (w == 1) {
1110 while (h-- > 0) {
1111 *dptr = clr;
1112 dptr += VBufWidth<<1;
1113 --h;
1115 } else {
1116 while (h-- > 0) {
1117 dptr = memFillDWDash(dptr, clr, w)+dinc;
1118 if (h-- > 0) dptr = memFillDWDash(dptr+1, clr, w-1)+dinc;
1121 } else {
1122 if (w == 1) {
1123 while (h-- > 0) {
1124 mixin(GxColMixMixin!("*dptr", "*dptr", "clr"));
1125 dptr += VBufWidth<<1;
1126 --h;
1128 } else {
1129 while (h-- > 0) {
1130 dptr = memBlendColorDash(dptr, clr, w)+dinc;
1131 if (h-- > 0) dptr = memBlendColorDash(dptr+1, clr, w-1)+dinc;
1137 public void gxDashRect() (in auto ref GxRect rc, in uint clr) nothrow @trusted @nogc {
1138 pragma(inline, true);
1139 gxDashRect(rc.x0, rc.y0, rc.width, rc.height, clr);
1143 // ////////////////////////////////////////////////////////////////////////// //
1144 public void gxFillRect (int x, int y, int w, int h, in uint clr) nothrow @trusted @nogc {
1145 if (gxIsTransparent(clr)) return;
1146 if (!gxClipRect.clipHVStripes(x, y, w, h)) return;
1147 if (!GxRect(VBufWidth, VBufHeight).clipHVStripes(x, y, w, h)) return;
1148 if (w == 1) { gxVLine(x, y, h, clr); return; }
1149 if (h == 1) { gxHLine(x, y, w, clr); return; }
1150 uint* dptr = vglTexBuf+y*VBufWidth+x;
1151 immutable uint dinc = VBufWidth-w;
1152 if (gxIsSolid(clr)) {
1153 while (h-- > 0) dptr = memFillDW(dptr, clr, w)+dinc;
1154 } else {
1155 while (h-- > 0) dptr = memBlendColor(dptr, clr, w)+dinc;
1159 public void gxFillRect() (in auto ref GxRect rc, in uint clr) nothrow @trusted @nogc {
1160 pragma(inline, true);
1161 gxFillRect(rc.x0, rc.y0, rc.width, rc.height, clr);
1165 public void gxDrawRect (in int x, in int y, in int w, in int h, in uint clr) nothrow @trusted @nogc {
1166 if (w < 1 || h < 1 || gxIsTransparent(clr)) return;
1167 gxHLine(x, y, w, clr);
1168 if (h > 1) gxHLine(x, y+h-1, w, clr);
1169 if (h > 2) {
1170 gxVLine(x, y+1, h-2, clr);
1171 if (w > 1) gxVLine(x+w-1, y+1, h-2, clr);
1175 public void gxDrawRect() (in auto ref GxRect rc, in uint clr) nothrow @trusted @nogc {
1176 pragma(inline, true);
1177 gxDrawRect(rc.x0, rc.y0, rc.width, rc.height, clr);
1181 // ////////////////////////////////////////////////////////////////////////// //
1182 // use clip region as boundaries
1183 public void gxDrawShadow (in GxRect winrect, int size, in uint clrshadow=gxRGBA!(0, 0, 0, 127), bool shadowdash=false) nothrow @trusted @nogc {
1184 if (size < 1 || winrect.empty || gxIsTransparent(clrshadow)) return;
1185 gxWithSavedClip{
1186 gxClipReset();
1187 if (!shadowdash) {
1188 gxFillRect(winrect.x1+1, winrect.y0+size, size, winrect.height-size, clrshadow);
1189 gxFillRect(winrect.x0+size, winrect.y1+1, winrect.width, size, clrshadow);
1190 } else {
1191 gxDashRect(winrect.x1+1, winrect.y0+size, size, winrect.height-size, clrshadow);
1192 //FIXME: this renders wrong shadow for odd window sizes
1193 gxDashRect(winrect.x0+size, winrect.y1+1, winrect.width, size, clrshadow);
1199 public void gxDrawWindow (GxRect winrect,
1200 const(char)[] title, in uint framecolor,
1201 in uint titlecolor, in uint titlebackcolor, in uint windowcolor,
1202 in uint shadowcolor, int shadowsize=8, bool shadowdash=false) nothrow @trusted
1204 import iv.egra.gfx.text;
1206 if (winrect.empty) return;
1207 gxDrawShadow(winrect, shadowsize, shadowcolor, shadowdash);
1209 gxDrawRect(winrect, framecolor);
1210 if (winrect.width <= 2 || winrect.height <= 2) return;
1211 winrect.shrinkBy(1, 1);
1213 if (title is null) {
1214 gxFillRect(winrect, windowcolor);
1215 return;
1218 gxWithSavedClip{
1219 immutable int hgt = (gxTextHeightUtf < 10 ? 10 : gxTextHeightUtf+1);
1220 immutable int oh = winrect.size.h;
1221 if (hgt < winrect.size.h) winrect.size.h = hgt;
1222 if (gxClipRect.intersect(winrect)) {
1223 gxFillRect(gxClipRect, titlebackcolor);
1224 gxDrawTextUtf(winrect.x0+(winrect.width-gxTextWidthUtf(title))/2, winrect.y0+(hgt-gxTextHeightUtf)/2, title, titlecolor);
1226 winrect.pos.y += hgt;
1227 winrect.size.h = oh-hgt;
1229 gxFillRect(winrect, windowcolor);
1233 // ////////////////////////////////////////////////////////////////////////// //
1234 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); }
1236 public void gxDrawScrollBar() (in auto ref GxRect r, int min, int max, int value) nothrow @trusted @nogc {
1237 enum FrameColor = gxRGB!(220, 220, 220);
1238 enum EmptyColor = gxRGB!(0, 0, 0);
1239 enum FullColor = gxRGB!(160, 160, 160);
1240 if (r.empty) return;
1241 if (max <= min) min = max = value = 0;
1242 // move it to 0
1243 //conwriteln("00: min=", min, "; max=", max, "; value=", value);
1244 max -= min;
1245 value -= min;
1246 if (value < 0) value = 0; else if (value > max) value = max;
1247 //conwriteln("01: min=", min, "; max=", max, "; value=", value);
1248 int sx0 = r.x0+1;
1249 int sy0 = r.y0+1;
1250 int wdt = r.width-2;
1251 int hgt = r.height-2;
1252 bool vert = (r.width < r.height);
1253 // frame
1254 if ((vert && wdt > 1) || (!vert && hgt > 1)) {
1255 gxHLine(r.x0+1, r.y0+0, wdt, FrameColor);
1256 gxVLine(r.x0+0, r.y0+1, hgt, FrameColor);
1257 gxVLine(r.x1+0, r.y0+1, hgt, FrameColor);
1258 gxHLine(r.x0+1, r.y1+0, wdt, FrameColor);
1259 } else {
1260 sx0 -= 1;
1261 sy0 -= 1;
1262 wdt += 2;
1263 hgt += 2;
1265 if (max <= 0) {
1266 gxFillRect(sx0, sy0, wdt, hgt, FullColor);
1267 return;
1269 if (vert) {
1270 int pix = hgt*value/max;
1271 if (pix > hgt) pix = hgt; // just in case
1272 gxFillRect(sx0, sy0, wdt, pix, FullColor);
1273 gxFillRect(sx0, sy0+pix, wdt, hgt-pix, EmptyColor);
1274 } else {
1275 int pix = wdt*value/max;
1276 if (pix > wdt) pix = wdt; // just in case
1277 gxFillRect(sx0, sy0, pix, hgt, FullColor);
1278 gxFillRect(sx0+pix, sy0, wdt-pix, hgt, EmptyColor);
1283 // ////////////////////////////////////////////////////////////////////////// //
1284 private int abs (int a) pure nothrow @safe @nogc { pragma(inline, true); return (a < 0 ? -a : a); }
1286 public void gxCircle (in int cx, in int cy, in int radius, in uint clr) nothrow @trusted @nogc {
1287 static void plot4points (in int cx, in int cy, in int x, in int y, in uint clr) nothrow @trusted @nogc {
1288 pragma(inline, true);
1289 gxPutPixel(cx+x, cy+y, clr);
1290 if (x) gxPutPixel(cx-x, cy+y, clr);
1291 if (y) gxPutPixel(cx+x, cy-y, clr);
1292 gxPutPixel(cx-x, cy-y, clr);
1295 if (radius <= 0 || gxIsTransparent(clr)) return;
1296 if (radius == 1) { gxPutPixel(cx, cy, clr); return; }
1297 int error = -radius, x = radius, y = 0;
1298 while (x > y) {
1299 plot4points(cx, cy, x, y, clr);
1300 plot4points(cx, cy, y, x, clr);
1301 error += y*2+1;
1302 ++y;
1303 if (error >= 0) { --x; error -= x*2; }
1305 plot4points(cx, cy, x, y, clr);
1308 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); }
1311 public void gxFillCircle (in int cx, in int cy, in int radius, in uint clr) nothrow @trusted @nogc {
1312 if (radius <= 0 || gxIsTransparent(clr)) return;
1313 if (radius == 1) { gxPutPixel(cx, cy, clr); return; }
1314 int error = -radius, x = radius, y = 0;
1315 while (x >= y) {
1316 int last_y = y;
1317 error += y;
1318 ++y;
1319 error += y;
1320 gxHLine(cx-x, cy+last_y, 2*x+1, clr);
1321 if (x != 0 && last_y != 0) gxHLine(cx-x, cy-last_y, 2*x+1, clr);
1322 if (error >= 0) {
1323 if (x != last_y) {
1324 gxHLine(cx-last_y, cy+x, 2*last_y+1, clr);
1325 if (last_y != 0 && x != 0) gxHLine(cx-last_y, cy-x, 2*last_y+1, clr);
1327 error -= x;
1328 --x;
1329 error -= x;
1334 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); }
1337 public void gxEllipse (int x0, int y0, int x1, int y1, in uint clr) nothrow @trusted @nogc {
1338 if (gxIsTransparent(clr)) return;
1339 if (y0 == y1) { gxHLine(x0, y0, x1-x0+1, clr); return; }
1340 if (x0 == x1) { gxVLine(x0, y0, y1-y0+1, clr); return; }
1341 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
1342 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
1343 long err = dx+dy+b1*a*a; // error of 1.step
1344 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
1345 if (y0 > y1) y0 = y1; // ...exchange them
1346 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
1347 a *= 8*a; b1 = 8*b*b;
1348 do {
1349 long e2;
1350 gxPutPixel(x1, y0, clr); // I. Quadrant
1351 gxPutPixel(x0, y0, clr); // II. Quadrant
1352 gxPutPixel(x0, y1, clr); // III. Quadrant
1353 gxPutPixel(x1, y1, clr); // IV. Quadrant
1354 e2 = 2*err;
1355 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
1356 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
1357 } while (x0 <= x1);
1358 while (y0-y1 < b) {
1359 // too early stop of flat ellipses a=1
1360 gxPutPixel(x0-1, ++y0, clr); // complete tip of ellipse
1361 gxPutPixel(x0-1, --y1, clr);
1365 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); }
1368 public void gxFillEllipse (int x0, int y0, int x1, int y1, in uint clr) nothrow @trusted @nogc {
1369 if (gxIsTransparent(clr)) return;
1370 if (y0 == y1) { gxHLine(x0, y0, x1-x0+1, clr); return; }
1371 if (x0 == x1) { gxVLine(x0, y0, y1-y0+1, clr); return; }
1372 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
1373 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
1374 long err = dx+dy+b1*a*a; // error of 1.step
1375 int prev_y0 = -1, prev_y1 = -1;
1376 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
1377 if (y0 > y1) y0 = y1; // ...exchange them
1378 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
1379 a *= 8*a; b1 = 8*b*b;
1380 do {
1381 long e2;
1382 if (y0 != prev_y0) { gxHLine(x0, y0, x1-x0+1, clr); prev_y0 = y0; }
1383 if (y1 != y0 && y1 != prev_y1) { gxHLine(x0, y1, x1-x0+1, clr); prev_y1 = y1; }
1384 e2 = 2*err;
1385 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
1386 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
1387 } while (x0 <= x1);
1388 while (y0-y1 < b) {
1389 // too early stop of flat ellipses a=1
1390 gxPutPixel(x0-1, ++y0, clr); // complete tip of ellipse
1391 gxPutPixel(x0-1, --y1, clr);
1395 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); }
1398 // ////////////////////////////////////////////////////////////////////////// //
1399 public void gxDrawRoundedRect (int x0, int y0, int wdt, int hgt, int radius, in uint clr) nothrow @trusted @nogc {
1400 static void gxArcs (int cx, int cy, int wdt, int hgt, in int radius, in uint clr) nothrow @trusted @nogc {
1401 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 {
1402 pragma(inline, true);
1403 gxPutPixel(cx+wdt-1+x, cy+hgt-1+y, clr);
1404 gxPutPixel(cx-x, cy+hgt-1+y, clr);
1405 gxPutPixel(cx+wdt-1+x, cy-y, clr);
1406 gxPutPixel(cx-x, cy-y, clr);
1409 wdt -= (radius<<1); if (wdt <= 0) return;
1410 hgt -= (radius<<1); if (hgt <= 0) return;
1411 cx += radius; cy += radius;
1412 int error = -radius, x = radius, y = 0;
1413 while (x > y) {
1414 plot4points(radius, wdt, hgt, cx, cy, x, y, clr);
1415 plot4points(radius, wdt, hgt, cx, cy, y, x, clr);
1416 error += y*2+1;
1417 ++y;
1418 if (error >= 0) { --x; error -= x*2; }
1420 if (x || y != radius) plot4points(radius, wdt, hgt, cx, cy, x, y, clr);
1424 if (wdt < 1 || hgt < 1) return;
1425 if (radius < 1) { gxDrawRect(x0, y0, wdt, hgt, clr); return; }
1426 if (gxIsTransparent(clr)) return;
1427 if (hgt == 1) { gxHLine(x0, y0, wdt, clr); return; }
1428 if (wdt == 1) { gxVLine(x0, y0, hgt, clr); return; }
1429 // fix radius
1430 immutable int minsz = (wdt < hgt ? wdt : hgt);
1431 if (radius >= (minsz>>1)) {
1432 radius = (minsz>>1)-1;
1433 if (radius < 1) { gxDrawRect(x0, y0, wdt, hgt, clr); return; }
1435 // draw the parts of the rect
1436 gxHLine(x0+radius+1, y0, wdt-(radius<<1)-2, clr); // top
1437 gxHLine(x0+radius+1, y0+hgt-1, wdt-(radius<<1)-2, clr); // bottom
1438 gxVLine(x0, y0+radius+1, hgt-(radius<<1)-2, clr); // left
1439 gxVLine(x0+wdt-1, y0+radius+1, hgt-(radius<<1)-2, clr); // right
1440 // left arc
1441 gxArcs(x0, y0, wdt, hgt, radius, clr);
1444 public void gxDrawRoundedRect() (in auto ref GxRect rc, in int radius, in uint clr) nothrow @trusted @nogc {
1445 pragma(inline, true);
1446 gxDrawRoundedRect(rc.x0, rc.y0, rc.width, rc.height, radius, clr);
1450 // ////////////////////////////////////////////////////////////////////////// //
1451 __gshared usize frectXCoords; // array of ints
1452 __gshared usize frectXCSize; // in items
1455 /* cyclic dependency
1456 shared static ~this () {
1457 if (frectXCoords) {
1458 import core.stdc.stdlib : free;
1459 free(cast(void*)frectXCoords);
1460 frectXCoords = 0;
1466 int* ensureXCoords (int radius) nothrow @trusted @nogc {
1467 if (radius < 1) return null;
1468 if (radius > 1024*1024) return null;
1469 if (cast(usize)radius > frectXCSize) {
1470 import core.stdc.stdlib : realloc;
1471 immutable usize newsz = (cast(usize)radius|0x7fu)+1;
1472 void* np = realloc(cast(void*)frectXCoords, newsz*int.sizeof);
1473 if (np is null) return null; // out of memory
1474 frectXCSize = newsz;
1475 frectXCoords = cast(usize)np;
1477 return cast(int*)frectXCoords;
1481 // this is wrong, but i'm ok with it for now
1482 public void gxFillRoundedRect (int x0, int y0, int wdt, int hgt, int radius, in uint clr) nothrow @trusted @nogc {
1483 if (wdt < 1 || hgt < 1) return;
1484 if (radius < 1) { gxFillRect(x0, y0, wdt, hgt, clr); return; }
1485 if (gxIsTransparent(clr)) return;
1486 if (hgt == 1) { gxHLine(x0, y0, wdt, clr); return; }
1487 if (wdt == 1) { gxVLine(x0, y0, hgt, clr); return; }
1488 // fix radius
1489 immutable int minsz = (wdt < hgt ? wdt : hgt);
1490 if (radius >= (minsz>>1)) {
1491 radius = (minsz>>1)-1;
1492 if (radius < 1) { gxFillRect(x0, y0, wdt, hgt, clr); return; }
1495 // create border coords
1496 auto xpt = ensureXCoords(radius+1);
1497 if (xpt is null) { gxFillRect(x0, y0, wdt, hgt, clr); return; } // do at least something
1498 xpt[0..radius+1] = int.min;
1500 // create coords
1502 int error = -radius, x = radius, y = 0;
1503 while (x > y) {
1504 if (y <= radius && xpt[y] < x) xpt[y] = x;
1505 if (x >= 0 && x <= radius && xpt[x] < y) xpt[x] = y;
1506 error += y*2+1;
1507 ++y;
1508 if (error >= 0) { --x; error -= x*2; }
1510 if (y <= radius && xpt[y] < x) xpt[y] = x;
1511 if (x >= 0 && x <= radius && xpt[x] < y) xpt[x] = y;
1514 // draw the filled rect
1515 gxFillRect(x0, y0+radius+1, wdt, hgt-(radius<<1)-2, clr);
1517 // draw arc
1518 foreach (immutable dy; 0..radius+1) {
1519 if (xpt[dy] == int.min) continue;
1520 immutable topy = y0+radius-dy;
1521 immutable topx0 = x0+radius-xpt[dy];
1522 immutable topx1 = x0+wdt-radius-1+xpt[dy];
1523 //gxPutPixel(topx0, topy, clr);
1524 //gxPutPixel(topx1, topy, clr);
1525 gxHLine(topx0, topy, topx1-topx0+1, clr);
1526 immutable boty = y0+hgt-radius+dy-1;
1527 //gxPutPixel(topx0, boty, clr);
1528 //gxPutPixel(topx1, boty, clr);
1529 gxHLine(topx0, boty, topx1-topx0+1, clr);
1533 public void gxFillRoundedRect() (in auto ref GxRect rc, in int radius, in uint clr) nothrow @trusted @nogc {
1534 pragma(inline, true);
1535 gxFillRoundedRect(rc.x0, rc.y0, rc.width, rc.height, radius, clr);
1539 // ////////////////////////////////////////////////////////////////////////// //
1540 // bresenham with clipping
1541 // the idea is that we can simply skip the right number of steps
1542 // if the point is off the drawing area
1543 public void gxDrawLine (int x0, int y0, int x1, int y1, in uint clr, bool lastPoint=true) nothrow @trusted @nogc {
1544 enum swap(string a, string b) = "{immutable int tmp_="~a~";"~a~"="~b~";"~b~"=tmp_;}";
1546 if (gxIsTransparent(clr)) return;
1548 GxRect realClip = gxClipRect;
1549 if (!realClip.intersect(VBufWidth, VBufHeight)) return;
1551 // just a point?
1552 if (x0 == x1 && y0 == y1) {
1553 if (lastPoint) gxPutPixel(x0, y0, clr);
1554 return;
1557 // horizontal line?
1558 if (y0 == y1) {
1559 if (x0 > x1) {
1560 gxHLine(x1+(lastPoint ? 0 : 1), y0, x0-x1+(lastPoint ? 1 : 0), clr);
1561 } else {
1562 gxHLine(x0, y0, x1-x0+(lastPoint ? 1 : 0), clr);
1564 return;
1567 // clip rectange
1568 int wx0 = realClip.x0, wy0 = realClip.y0, wx1 = realClip.x1, wy1 = realClip.y1;
1569 if (wx0 > wx1 || wy0 > wy1) return; // this should not happen, but...
1571 // vertical setup; always go from top to bottom, so we'll draw the same line regardless of the starting point
1572 bool skipFirst = false;
1573 if (y0 > y1) {
1574 // swap endpoints
1575 if (!lastPoint) skipFirst = lastPoint = true;
1576 mixin(swap!("x0", "x1"));
1577 mixin(swap!("y0", "y1"));
1579 if (y0 > wy1 || y1 < wy0) return; // out of clip rectange
1580 int sty = 1; // "step sign" for x axis; we still need the var, because there is a possible swap down there
1582 // horizontal setup
1583 int stx = void; // "step sign" for x axis
1584 if (x0 < x1) {
1585 // from left to right
1586 if (x0 > wx1 || x1 < wx0) return; // out of clip rectange
1587 stx = 1; // going right
1588 } else {
1589 // from right to left
1590 if (x1 > wx1 || x0 < wx0) return; // out of clip rectange
1591 stx = -1; // going left
1592 x0 = -x0;
1593 x1 = -x1;
1594 wx0 = -wx0;
1595 wx1 = -wx1;
1596 mixin(swap!("wx0", "wx1"));
1599 int dsx = x1-x0; // "length" for x axis
1600 int dsy = y1-y0; // "length" for y axis
1601 int xd = void, yd = void; // current coord
1602 bool xyswapped = false; // if `true`, `xd` and `yd` are swapped
1603 if (dsx < dsy) {
1604 xyswapped = true;
1605 mixin(swap!("x0", "y0"));
1606 mixin(swap!("x1", "y1"));
1607 mixin(swap!("dsx", "dsy"));
1608 mixin(swap!("wx0", "wy0"));
1609 mixin(swap!("wx1", "wy1"));
1610 mixin(swap!("stx", "sty"));
1612 xd = x0;
1613 yd = y0;
1614 int dx2 = 2*dsx; // "double length" for x axis
1615 int dy2 = 2*dsy; // "double length" for y axis
1616 int e = 2*dsy-dsx; // "error" (as in bresenham algo)
1617 int term = x1; // termination point
1618 bool xfixed = false; // will be set if we properly fixed x0 coord while fixing the y0
1619 // note that clipping can overflow for insane coords
1620 // if you are completely sure that it can't happen, you can use `int` instead of `long`
1621 if (y0 < wy0) {
1622 // clip at top
1623 immutable long temp = cast(long)dx2*(wy0-y0)-dsx;
1624 xd += cast(int)(temp/dy2);
1625 if (xd > wx1) return; // x is moved out of clipping rect, nothing to do
1626 immutable int rem = cast(int)(temp%dy2);
1627 if (xd+(rem > 0 ? 1 : 0) >= wx0) {
1628 xfixed = true; // startx is inside the clipping rect, no need to perform left clip
1629 yd = wy0;
1630 e -= rem+dsx;
1631 if (rem > 0) { ++xd; e += dy2; }
1634 if (!xfixed && x0 < wx0) {
1635 // clip at left
1636 immutable long temp = cast(long)dy2*(wx0-x0);
1637 yd += cast(int)(temp/dx2);
1638 immutable int rem = cast(int)(temp%dx2);
1639 if (yd > wy1 || (yd == wy1 && rem >= dsx)) return; // y is moved out of clipping rect, nothing to do
1640 xd = wx0;
1641 e += rem;
1642 if (rem >= dsx) { ++yd; e -= dx2; }
1644 if (y1 > wy1) {
1645 // clip at bottom
1646 immutable long temp = cast(long)dx2*(wy1-y0)+dsx;
1647 term = x0+cast(int)(temp/dy2);
1648 // it should be safe to decrement here
1649 if (cast(int)(temp%dy2) == 0) --term;
1651 if (term > wx1) term = wx1; // clip at right
1653 if (sty == -1) yd = -yd;
1654 if (stx == -1) { xd = -xd; term = -term; }
1655 dx2 -= dy2;
1657 if (lastPoint) term += stx;
1658 if (skipFirst) {
1659 if (term == xd) return;
1660 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
1661 xd += stx;
1664 // draw it; `putPixel()` can omit checks
1665 if (gxIsSolid(clr)) {
1666 while (xd != term) {
1667 // inlined putpixel
1668 *cast(uint*)((cast(ubyte*)vglTexBuf)+(xyswapped ? xd*VBufWidth+yd : yd*VBufWidth+xd)) = clr;
1669 // done drawing, move coords
1670 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
1671 xd += stx;
1673 } else {
1674 while (xd != term) {
1675 // inlined putpixel
1676 uint* dp = cast(uint*)((cast(ubyte*)vglTexBuf)+(xyswapped ? xd*VBufWidth+yd : yd*VBufWidth+xd));
1677 mixin(GxColMixMixin!("*dp", "*dp", "clr"));
1678 // done drawing, move coords
1679 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
1680 xd += stx;
1685 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); }
1686 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); }
1687 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); }