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*/;
26 import iv
.egra
.gfx
.config
;
27 import iv
.egra
.gfx
.lowlevel
;
30 // ////////////////////////////////////////////////////////////////////////// //
31 package(iv
.egra
) __gshared
int VBufWidth
= 740*2;
32 package(iv
.egra
) __gshared
int VBufHeight
= 520*2;
33 package __gshared
ubyte vbufEffScale
= 1; // effective (current) window scale
35 // framebuffer, BGRA format
36 package(iv
.egra
) __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 // ////////////////////////////////////////////////////////////////////////// //
57 // ////////////////////////////////////////////////////////////////////////// //
58 public struct GxPoint
{
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 {
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
== "/") {
85 mixin("x"~op
~"=v; y"~op
~"=v;");
88 void opOpAssign(string op
) (in auto ref GxPoint pt
) if (op
== "+" || op
== "-") {
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
== "-") {
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
== "-") {
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
{
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
; }
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;");
158 mixin("h "~op
~"= v;");
165 // ////////////////////////////////////////////////////////////////////////// //
166 public struct GxRect
{
169 GxSize size
= GxSize(-1, -1); // <0: invalid rect
171 string
toString () const @trusted nothrow {
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
;
177 return "(invalid-rect)";
182 this() (in auto ref GxRect rc
) pure {
183 pragma(inline
, true);
188 this (in int ax0
, in int ay0
, in int awidth
, in int aheight
) pure {
189 pragma(inline
, true);
196 this() (in int ax0
, in int ay0
, in auto ref GxSize asize
) pure {
197 pragma(inline
, true);
203 this() (in auto ref GxPoint xy0
, in int awidth
, in int aheight
) pure {
204 pragma(inline
, true);
210 this() (in auto ref GxPoint xy0
, in auto ref GxSize asize
) pure {
211 pragma(inline
, true);
216 this() (in auto ref GxPoint xy0
, in auto ref GxPoint xy1
) pure {
217 pragma(inline
, true);
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);
230 this() (in auto ref GxSize asize
) pure {
231 pragma(inline
, true);
236 void setCoords(bool doSwap
=true) (in int ax0
, in int ay0
, in int ax1
, in int ay1
) {
237 pragma(inline
, true);
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;
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);
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);
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);
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);
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
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
) {
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
) {
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);
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);
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);
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);
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);
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
485 * x = stripe start (not relative to rect)
486 * y = stripe start (not relative to rect)
487 * wdt = stripe length
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;
500 immutable int dx
= x0
-x
;
501 if (dx
>= wdt
) return false; // avoid overflow
502 if (leftSkip
!is null) *leftSkip
= dx
;
505 assert(wdt
> 0); // yeah, always
507 if (leftSkip
!is null) *leftSkip
= 0;
509 if (wdt
> width
) wdt
= width
; // avoid overflow
510 if (x
+wdt
> x0
+width
) {
513 assert(wdt
> 0); // yeah, always
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
527 * x = stripe start (not relative to rect)
528 * y = stripe start (not relative to rect)
529 * hgt = stripe length
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;
542 immutable int dy
= y0
-y
;
543 if (dy
>= hgt
) return false; // avoid overflow
544 if (topSkip
!is null) *topSkip
= dy
;
547 assert(hgt
> 0); // yeah, always
549 if (topSkip
!is null) *topSkip
= 0;
551 if (hgt
> height
) hgt
= height
; // avoid overflow
552 if (y
+hgt
> y0
+height
) {
555 assert(hgt
> 0); // yeah, always
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
{
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;
632 mixin(GxColMixMixin
!("res", "dc", "clr"));
638 // ////////////////////////////////////////////////////////////////////////// //
639 private template isGoodRGBInt(T
) {
640 import std
.traits
: Unqual
;
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 // ////////////////////////////////////////////////////////////////////////// //
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);
826 public align(1) struct GxColorHSL
{
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
;
834 immutable len
= snprintf(buf
.ptr
, buf
.length
, "HSL(%g,%g,%g)", h
, s
, l
);
835 return buf
[0..len
].idup
;
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
); }
846 static float calchue (float h
, in float m1
, in float m2
) pure nothrow @safe @nogc {
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;
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
;
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;
888 if (g1
> maxColor
) maxColor
= g1
;
889 if (b1
> maxColor
) maxColor
= b1
;
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
) {
902 this.s
= (maxColor
-minColor
)/(maxColor
+minColor
);
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
);
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;
922 // ////////////////////////////////////////////////////////////////////////// //
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);
931 immutable rc
= gxClipRect
;
932 scope(exit
) gxClipRect
= rc
;
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
)) {
999 nlen
= cast(uint)(stlen
-sofs
);
1000 if (nlen
>= cast(uint)w
) {
1001 memFillDW(dptr
, stclr
, w
);
1004 dptr
= memFillDW(dptr
, stclr
, cast(int)nlen
);
1009 assert(sofs
>= stlen
&& sofs
< cast(int)stFull
);
1010 nlen
= stFull
-cast(uint)sofs
;
1011 if (nlen
>= cast(uint)w
) return;
1020 memFillDW(dptr
, stclr
, w
);
1023 memFillDW(dptr
, stclr
, stlen
);
1024 if ((w
-= stlen
) <= 0) return;
1033 nlen
= cast(uint)(stlen
-sofs
);
1034 if (nlen
>= cast(uint)w
) {
1035 //dptr[0..cast(uint)w] = stclr;
1036 memBlendColor(dptr
, stclr
, w
);
1039 //dptr[0..nlen] = stclr;
1040 dptr
= memBlendColor(dptr
, stclr
, cast(int)nlen
);
1045 assert(sofs
>= stlen
&& sofs
< cast(int)stFull
);
1046 nlen
= stFull
-cast(uint)sofs
;
1047 if (nlen
>= cast(uint)w
) return;
1056 //dptr[0..cast(uint)w] = stclr;
1057 memBlendColor(dptr
, stclr
, w
);
1060 //dptr[0..cast(uint)stlen] = stclr;
1061 memBlendColor(dptr
, stclr
, stlen
);
1062 if ((w
-= stlen
) <= 0) return;
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
);
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
; }
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
)) {
1112 dptr
+= VBufWidth
<<1;
1117 dptr
= memFillDWDash(dptr
, clr
, w
)+dinc
;
1118 if (h
-- > 0) dptr
= memFillDWDash(dptr
+1, clr
, w
-1)+dinc
;
1124 mixin(GxColMixMixin
!("*dptr", "*dptr", "clr"));
1125 dptr
+= VBufWidth
<<1;
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
;
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
);
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;
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
);
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
);
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;
1243 //conwriteln("00: min=", min, "; max=", max, "; value=", value);
1246 if (value
< 0) value
= 0; else if (value
> max
) value
= max
;
1247 //conwriteln("01: min=", min, "; max=", max, "; value=", value);
1250 int wdt
= r
.width
-2;
1251 int hgt
= r
.height
-2;
1252 bool vert
= (r
.width
< r
.height
);
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
);
1266 gxFillRect(sx0
, sy0
, wdt
, hgt
, FullColor
);
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
);
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;
1299 plot4points(cx
, cy
, x
, y
, clr
);
1300 plot4points(cx
, cy
, y
, x
, clr
);
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;
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
);
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
);
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
;
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
1355 if (e2
>= dx
) { ++x0
; --x1
; err
+= dx
+= b1
; } // x step
1356 if (e2
<= dy
) { ++y0
; --y1
; err
+= dy
+= a
; } // y step
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
;
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
; }
1385 if (e2
>= dx
) { ++x0
; --x1
; err
+= dx
+= b1
; } // x step
1386 if (e2
<= dy
) { ++y0
; --y1
; err
+= dy
+= a
; } // y step
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;
1414 plot4points(radius
, wdt
, hgt
, cx
, cy
, x
, y
, clr
);
1415 plot4points(radius
, wdt
, hgt
, cx
, cy
, y
, x
, clr
);
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; }
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
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 () {
1458 import core.stdc.stdlib : free;
1459 free(cast(void*)frectXCoords);
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; }
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
;
1502 int error
= -radius
, x
= radius
, y
= 0;
1504 if (y
<= radius
&& xpt
[y
] < x
) xpt
[y
] = x
;
1505 if (x
>= 0 && x
<= radius
&& xpt
[x
] < y
) xpt
[x
] = 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
);
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;
1552 if (x0
== x1
&& y0
== y1
) {
1553 if (lastPoint
) gxPutPixel(x0
, y0
, clr
);
1560 gxHLine(x1
+(lastPoint ?
0 : 1), y0
, x0
-x1
+(lastPoint ?
1 : 0), clr
);
1562 gxHLine(x0
, y0
, x1
-x0
+(lastPoint ?
1 : 0), clr
);
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;
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
1583 int stx
= void; // "step sign" for x axis
1585 // from left to right
1586 if (x0
> wx1 || x1
< wx0
) return; // out of clip rectange
1587 stx
= 1; // going right
1589 // from right to left
1590 if (x1
> wx1 || x0
< wx0
) return; // out of clip rectange
1591 stx
= -1; // going left
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
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"));
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`
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
1631 if (rem
> 0) { ++xd
; e
+= dy2
; }
1634 if (!xfixed
&& x0
< wx0
) {
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
1642 if (rem
>= dsx
) { ++yd
; e
-= dx2
; }
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
; }
1657 if (lastPoint
) term
+= stx
;
1659 if (term
== xd
) return;
1660 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
1664 // draw it; `putPixel()` can omit checks
1665 if (gxIsSolid(clr
)) {
1666 while (xd
!= term
) {
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
; }
1674 while (xd
!= term
) {
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
; }
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
); }