2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module egfx
.base
is aliced
;
21 import arsd
.simpledisplay
;
27 // ////////////////////////////////////////////////////////////////////////// //
28 public __gshared
int VBufWidth
= 2;//740*2;
29 public __gshared
int VBufHeight
= 2;//520*2;
30 public __gshared
ubyte vbufEffScale
= 1; // effective (current) window scale
32 public __gshared
uint* vglTexBuf
; // OpenGL texture buffer
35 // ////////////////////////////////////////////////////////////////////////// //
36 public @property int winWidth () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
; }
37 public @property int winHeight () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
; }
39 public @property int winWidthScaled () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
*vbufEffScale
; }
40 public @property int winHeightScaled () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
*vbufEffScale
; }
43 // ////////////////////////////////////////////////////////////////////////// //
44 public struct GxPoint
{
48 pure nothrow @safe @nogc:
49 this() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; } ///
50 this (int ax
, int ay
) { pragma(inline
, true); x
= ax
; y
= ay
; } ///
51 void opAssign() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; } ///
52 bool opEquals() (in auto ref GxPoint p
) const { pragma(inline
, true); return (p
.x
== x
&& p
.y
== y
); } ///
54 int opCmp() (in auto ref GxPoint p
) const {
56 if (auto d0
= y
-p
.y
) return (d0
< 0 ?
-1 : 1);
57 else if (auto d1
= x
-p
.x
) return (d1
< 0 ?
-1 : 1);
62 public struct GxRect
{
65 int width
= -1; // <0: invalid rect
66 int height
= -1; // <0: invalid rect
71 alias bottom
= y1
; ///
74 string
toString () const @trusted nothrow {
76 import core
.stdc
.stdio
: snprintf
;
78 return buf
[0..snprintf(buf
.ptr
, buf
.length
, "(%d,%d)-(%d,%d)", x0
, y0
, x0
+width
-1, y0
+height
-1)].idup
;
80 return "(invalid-rect)";
84 pure nothrow @safe @nogc:
86 this() (in auto ref GxRect rc
) { pragma(inline
, true); x0
= rc
.x0
; y0
= rc
.y0
; width
= rc
.width
; height
= rc
.height
; } ///
89 this (int ax0
, int ay0
, int awidth
, int aheight
) {
90 //pragma(inline, true);
98 this() (in auto ref GxPoint xy0
, int awidth
, int aheight
) {
107 this() (in auto ref GxPoint xy0
, in auto ref GxPoint xy1
) {
108 pragma(inline
, true);
111 width
= xy1
.x
-xy0
.x
+1;
112 height
= xy1
.y
-xy0
.y
+1;
115 void opAssign() (in auto ref GxRect rc
) { pragma(inline
, true); x0
= rc
.x0
; y0
= rc
.y0
; width
= rc
.width
; height
= rc
.height
; } ///
116 bool opEquals() (in auto ref GxRect rc
) const { pragma(inline
, true); return (rc
.x0
== x0
&& rc
.y0
== y0
&& rc
.width
== width
&& rc
.height
== height
); } ///
118 int opCmp() (in auto ref GxRect p
) const {
119 if (auto d0
= y0
-rc
.y0
) return (d0
< 0 ?
-1 : 1);
120 if (auto d1
= x0
-rc
.x0
) return (d1
< 0 ?
-1 : 1);
121 if (auto d2
= width
*height
-rc
.width
*rc
.height
) return (d2
< 0 ?
-1 : 1);
125 @property bool valid () const { pragma(inline
, true); return (width
>= 0 && height
>= 0); } ///
126 @property bool invalid () const { pragma(inline
, true); return (width
< 0 || height
< 0); } ///
127 @property bool empty () const { pragma(inline
, true); return (width
<= 0 || height
<= 0); } /// invalid rects are empty
129 void invalidate () { pragma(inline
, true); width
= height
= -1; } ///
131 @property GxPoint
lefttop () const { pragma(inline
, true); return GxPoint(x0
, y0
); } ///
132 @property GxPoint
righttop () const { pragma(inline
, true); return GxPoint(x0
+width
-1, y0
); } ///
133 @property GxPoint
leftbottom () const { pragma(inline
, true); return GxPoint(x0
, y0
+height
-1); } ///
134 @property GxPoint
rightbottom () const { pragma(inline
, true); return GxPoint(x0
+width
-1, y0
+height
-1); } ///
136 alias topleft
= lefttop
; ///
137 alias topright
= righttop
; ///
138 alias bottomleft
= leftbottom
; ///
139 alias bottomright
= rightbottom
; ///
141 @property int x1 () const { pragma(inline
, true); return (width
> 0 ? x0
+width
-1 : x0
-1); } ///
142 @property int y1 () const { pragma(inline
, true); return (height
> 0 ? y0
+height
-1 : y0
-1); } ///
144 @property void x1 (in int val
) { pragma(inline
, true); width
= val
-x0
+1; } ///
145 @property void y1 (in int val
) { pragma(inline
, true); height
= val
-y0
+1; } ///
147 GxPoint
translateToGlobal() (in auto ref GxPoint lpt
) const {
148 pragma(inline
, true);
149 return GxPoint(lpt
.x
+x0
, lpt
.y
+y0
);
152 GxRect
translateToGlobal() (in auto ref GxRect lrc
) const {
153 pragma(inline
, true);
154 return GxRect(lrc
.x0
+x0
, lrc
.y0
+y0
, lrc
.width
, lrc
.height
);
158 bool inside() (in auto ref GxPoint p
) const {
159 pragma(inline
, true);
160 return (width
> 0 && height
> 0 ?
(p
.x
>= x0
&& p
.y
>= y0
&& p
.x
< x0
+width
&& p
.y
< y0
+height
) : false);
164 bool inside (in int ax
, in int ay
) const {
165 pragma(inline
, true);
166 return (width
> 0 && height
> 0 ?
(ax
>= x0
&& ay
>= y0
&& ax
< x0
+width
&& ay
< y0
+height
) : false);
169 /// is `r` inside `this`?
170 bool contains() (in auto ref GxRect r
) const {
171 pragma(inline
, true);
173 width
> 0 && height
> 0 &&
174 r
.width
> 0 && r
.height
> 0 &&
175 r
.x0
>= x0
&& r
.y0
>= y0
&&
176 r
.x0
+r
.width
<= x0
+width
&& r
.y0
+r
.height
<= y0
+height
;
179 /// is `r` and `this` overlaps?
180 bool overlaps() (in auto ref GxRect r
) const {
181 pragma(inline
, true);
183 width
> 0 && height
> 0 &&
184 r
.width
> 0 && r
.height
> 0 &&
185 x0
< r
.x0
+r
.width
&& r
.x0
< x0
+width
&&
186 y0
< r
.y0
+r
.height
&& r
.y0
< y0
+height
;
189 /// extend `this` so it will include `r`
190 void include() (in auto ref GxRect r
) {
191 pragma(inline
, true);
199 if (r
.x
< x0
) x0
= r
.x0
;
200 if (r
.y
< y0
) y0
= r
.y0
;
201 if (r
.x1
> x1
) x1
= r
.x1
;
202 if (r
.y1
> y1
) y1
= r
.y1
;
207 /// clip `this` so it will not be larger than `r`
208 bool intersect() (in auto ref GxRect r
) {
209 if (r
.invalid || invalid
) { width
= height
= -1; return false; }
210 if (r
.empty || empty
) { width
= height
= 0; return false; }
211 if (r
.y1
< y0 || r
.x1
< x0 || r
.x0
> x1 || r
.y0
> y1
) { width
= height
= 0; return false; }
212 // rc is at least partially inside this rect
213 if (x0
< r
.x0
) x0
= r
.x0
;
214 if (y0
< r
.y0
) y0
= r
.y0
;
215 if (x1
> r
.x1
) x1
= r
.x1
;
216 if (y1
> r
.y1
) y1
= r
.y1
;
217 assert(!empty
); // yeah, always
222 void shrinkBy (int dx
, int dy
) {
223 pragma(inline
, true);
224 if ((dx || dy
) && valid
) {
233 void growBy (int dx
, int dy
) {
234 pragma(inline
, true);
235 if ((dx || dy
) && valid
) {
244 void set (int ax0
, int ay0
, int awidth
, int aheight
) {
245 pragma(inline
, true);
253 void moveLeftTopBy (int dx
, int dy
) {
254 pragma(inline
, true);
261 alias moveTopLeftBy
= moveLeftTopBy
; /// ditto
264 void moveRightBottomBy (int dx
, int dy
) {
265 pragma(inline
, true);
270 alias moveBottomRightBy
= moveRightBottomBy
; /// ditto
273 void moveBy (int dx
, int dy
) {
274 pragma(inline
, true);
280 void moveTo (int nx
, int ny
) {
281 pragma(inline
, true);
287 * clip (x,y,len) stripe to this rect
290 * x = stripe start (not relative to rect)
291 * y = stripe start (not relative to rect)
292 * len = stripe length
295 * x = fixed x (invalid if result is false)
296 * len = fixed length (invalid if result is false)
297 * leftSkip = how much cells skipped at the left side (invalid if result is false)
298 * result = false if stripe is completely clipped out
303 bool clipHStripe (ref int x
, int y
, ref int len
, int* leftSkip
=null) const @trusted {
304 if (empty
) return false;
305 if (len
<= 0 || y
< y0 || y
>= y0
+height || x
>= x0
+width
) return false;
308 if (x
+len
<= x0
) return false;
309 immutable int dx
= x0
-x
;
310 if (leftSkip
!is null) *leftSkip
= dx
;
313 assert(len
> 0); // yeah, always
315 if (x
+len
> x0
+width
) {
318 assert(len
> 0); // yeah, always
324 * clip (x,y,hgt) stripe to this rect
327 * x = stripe start (not relative to rect)
328 * y = stripe start (not relative to rect)
329 * hgt = stripe length
332 * y = fixed y (invalid if result is false)
333 * hgt = fixed length (invalid if result is false)
334 * topSkip = how much cells skipped at the top side (invalid if result is false)
335 * result = false if stripe is completely clipped out
340 bool clipVStripe (int x
, ref int y
, ref int hgt
, int* topSkip
=null) const @trusted {
341 if (empty
) return false;
342 if (hgt
<= 0 || x
< x0 || x
>= x0
+width || y
>= y0
+height
) return false;
345 if (y
+hgt
<= y0
) return false;
346 immutable int dy
= y0
-y
;
347 if (topSkip
!is null) *topSkip
= dy
;
350 assert(hgt
> 0); // yeah, always
352 if (y
+hgt
> y0
+height
) {
355 assert(hgt
> 0); // yeah, always
361 bool clipHVStripes (ref int x
, ref int y
, ref int wdt
, ref int hgt
, int* leftSkip
=null, int* topSkip
=null) const @trusted {
362 if (empty || wdt
<= 0 || hgt
<= 0) return false;
363 if (y
>= y0
+height || x
>= x0
+width
) return false;
366 if (x
+wdt
<= x0
) return false;
367 immutable int dx
= x0
-x
;
368 if (leftSkip
!is null) *leftSkip
= dx
;
371 assert(wdt
> 0); // yeah, always
373 if (x
+wdt
> x0
+width
) {
376 assert(wdt
> 0); // yeah, always
381 if (y
+hgt
<= y0
) return false;
382 immutable int dy
= y0
-y
;
383 if (topSkip
!is null) *topSkip
= dy
;
386 assert(hgt
> 0); // yeah, always
388 if (y
+hgt
> y0
+height
) {
391 assert(hgt
> 0); // yeah, always
399 // ////////////////////////////////////////////////////////////////////////// //
400 // mix dc with ARGB (or ABGR) col; dc A is ignored (removed)
401 enum GxColMixMixin(string destvar
, string dcvar
, string colvar
) =
402 "{immutable uint col_ = "~colvar
~";\n"~
403 " immutable uint dc_ = ("~dcvar
~")&0xffffff;\n"~
404 " immutable uint a_ = 256-(col_>>24); /* to not loose bits */\n"~
405 " immutable uint srb_ = (col_&0xff00ff);\n"~
406 " immutable uint sg_ = (col_&0x00ff00);\n"~
407 " immutable uint drb_ = (dc_&0xff00ff);\n"~
408 " immutable uint dg_ = (dc_&0x00ff00);\n"~
409 " immutable uint orb_ = (drb_+(((srb_-drb_)*a_+0x800080)>>8))&0xff00ff;\n"~
410 " immutable uint og_ = (dg_+(((sg_-dg_)*a_+0x008000)>>8))&0x00ff00;\n"~
411 " ("~destvar
~") = orb_|og_;}";
413 public uint gxColMix (uint dc
, uint col
) pure nothrow @trusted @nogc {
414 pragma(inline
, true);
415 immutable uint a
= 256-(col
>>24); // to not loose bits
416 //immutable uint dc = (da)&0xffffff;
418 immutable uint srb
= (col
&0xff00ff);
419 immutable uint sg
= (col
&0x00ff00);
420 immutable uint drb
= (dc
&0xff00ff);
421 immutable uint dg
= (dc
&0x00ff00);
422 immutable uint orb
= (drb
+(((srb
-drb
)*a
+0x800080)>>8))&0xff00ff;
423 immutable uint og
= (dg
+(((sg
-dg
)*a
+0x008000)>>8))&0x00ff00;
428 // ////////////////////////////////////////////////////////////////////////// //
429 public bool gxIsTransparent (uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return ((clr
&0xff000000) == 0xff000000); }
430 public bool gxIsSolid (uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return ((clr
&0xff000000) == 0x00_000000); }
432 public enum gxTransparent
= 0xff000000;
435 // ////////////////////////////////////////////////////////////////////////// //
436 private template isGoodRGBInt(T
) {
437 import std
.traits
: Unqual
;
441 is(TT
== short) ||
is(TT
== ushort) ||
442 is(TT
== int) ||
is(TT
== uint) ||
443 is(TT
== long) ||
is(TT
== ulong);
447 // ////////////////////////////////////////////////////////////////////////// //
448 public uint gxrgb(T0
, T1
, T2
) (T0 r
, T1 g
, T2 b
) pure nothrow @trusted @nogc if (isGoodRGBInt
!T0
&& isGoodRGBInt
!T1
&& isGoodRGBInt
!T2
) {
449 pragma(inline
, true);
450 return (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
454 public template gxRGB(int r
, int g
, int b
) {
455 enum gxRGB
= (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
458 public template gxRGBA(int r
, int g
, int b
, int a
) {
459 enum gxRGBA
= (clampToByte(a
)<<24)|
(clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
463 // ////////////////////////////////////////////////////////////////////////// //
464 public __gshared GxRect gxClipRect
= GxRect(0, 0, 65535, 65535);
466 private struct ClipSave
{
468 ~this () const nothrow @trusted @nogc {
469 pragma(inline
, true);
474 public ClipSave
gxClipSave () nothrow @trusted @nogc {
475 pragma(inline
, true);
476 return ClipSave(gxClipRect
);
479 public void gxClipRestore() (in auto ref ClipSave cs
) nothrow @trusted @nogc {
480 pragma(inline
, true);
484 public void gxClipReset () nothrow @trusted @nogc {
485 pragma(inline
, true);
486 gxClipRect
= GxRect(0, 0, 65535, 65535);
490 // ////////////////////////////////////////////////////////////////////////// //
491 public void gxClearScreen (uint clr
) nothrow @trusted @nogc {
492 pragma(inline
, true);
493 vglTexBuf
[0..VBufWidth
*VBufHeight
+4] = clr
;
497 public void gxPutPixel (int x
, int y
, uint c
) nothrow @trusted @nogc {
498 pragma(inline
, true);
499 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(x
, y
)) {
500 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
;
501 *dp
= gxColMix(*dp
, c
);
506 public void gxPutPixel() (in auto ref GxPoint p
, uint c
) nothrow @trusted @nogc {
507 pragma(inline
, true);
508 if (p
.x
>= 0 && p
.y
>= 0 && p
.x
< VBufWidth
&& p
.y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(p
)) {
509 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+p
.y
*VBufWidth
+p
.x
;
510 *dp
= ((c
&0xff000000) == 0 ? c
: gxColMix(*dp
, c
));
515 public void gxSetPixel (int x
, int y
, uint c
) nothrow @trusted @nogc {
516 pragma(inline
, true);
517 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(x
, y
)) {
518 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
;
524 public void gxSetPixel() (in auto ref GxPoint p
, uint c
) nothrow @trusted @nogc {
525 pragma(inline
, true);
526 if (p
.x
>= 0 && p
.y
>= 0 && p
.x
< VBufWidth
&& p
.y
< VBufHeight
&& (c
&0xff000000) != 0xff000000 && gxClipRect
.inside(p
)) {
527 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+p
.y
*VBufWidth
+p
.x
;
533 // ////////////////////////////////////////////////////////////////////////// //
534 public void gxHLine (int x
, int y
, int w
, uint clr
) nothrow @trusted @nogc {
535 if (gxIsTransparent(clr
)) return;
536 if (!gxClipRect
.clipHStripe(x
, y
, w
)) return;
537 if (!GxRect(0, 0, VBufWidth
, VBufHeight
).clipHStripe(x
, y
, w
)) return;
538 if (gxIsSolid(clr
)) {
539 immutable uint addr
= y
*VBufWidth
+x
;
540 vglTexBuf
[addr
..addr
+w
] = clr
;
542 //while (w-- > 0) gxPutPixel(x++, y, clr);
543 uint* dptr
= vglTexBuf
+y
*VBufWidth
+x
;
545 mixin(GxColMixMixin
!("*dptr++", "*dptr", "clr"));
550 public void gxHLine() (in auto ref GxPoint p
, int w
, uint clr
) nothrow @trusted @nogc { gxHLine(p
.x
, p
.y
, w
, clr
); }
552 public void gxVLine (int x
, int y
, int h
, uint clr
) nothrow @trusted @nogc {
553 if (gxIsTransparent(clr
)) return;
554 if (!gxClipRect
.clipVStripe(x
, y
, h
)) return;
555 if (!GxRect(0, 0, VBufWidth
, VBufHeight
).clipVStripe(x
, y
, h
)) return;
556 uint* dptr
= vglTexBuf
+y
*VBufWidth
+x
;
557 if (gxIsSolid(clr
)) {
558 while (h
-- > 0) { *dptr
= clr
; dptr
+= VBufWidth
; }
560 while (h
-- > 0) { mixin(GxColMixMixin
!("*dptr", "*dptr", "clr")); dptr
+= VBufWidth
; }
564 public void gxVLine() (in auto ref GxPoint p
, int h
, uint clr
) nothrow @trusted @nogc { gxVLine(p
.x
, p
.y
, h
, clr
); }
567 // ////////////////////////////////////////////////////////////////////////// //
568 public void gxFillRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
569 if (gxIsTransparent(clr
)) return;
570 if (!gxClipRect
.clipHVStripes(x
, y
, w
, h
)) return;
571 if (!GxRect(0, 0, VBufWidth
, VBufHeight
).clipHVStripes(x
, y
, w
, h
)) return;
572 if (gxIsSolid(clr
)) {
573 uint addr
= y
*VBufWidth
+x
;
575 vglTexBuf
[addr
..addr
+w
] = clr
;
579 //while (h-- > 0) gxHLine(x, y++, w, clr);
580 uint* dptr
= vglTexBuf
+y
*VBufWidth
+x
;
581 immutable uint dinc
= VBufWidth
-w
;
583 foreach (immutable _
; 0..w
) {
584 mixin(GxColMixMixin
!("*dptr++", "*dptr", "clr"));
591 public void gxFillRect() (in auto ref GxRect rc
, uint clr
) nothrow @trusted @nogc {
592 gxFillRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
595 public void gxDrawRect (int x
, int y
, int w
, int h
, uint clr
) nothrow @trusted @nogc {
596 //if (w < 1 || h < 1 || x >= VBufWidth || y >= VBufHeight) return;
598 if (gxIsTransparent(clr)) return;
599 if (!gxClipRect.clipHVStripes(x, y, w, h)) return;
600 if (!GxRect(0, 0, VBufWidth, VBufHeight).clipHVStripes(x, y, w, h)) return;
602 gxHLine(x
, y
, w
, clr
);
603 gxHLine(x
, y
+h
-1, w
, clr
);
604 gxVLine(x
, y
+1, h
-2, clr
);
605 gxVLine(x
+w
-1, y
+1, h
-2, clr
);
608 public void gxDrawRect() (in auto ref GxRect rc
, uint clr
) nothrow @trusted @nogc {
609 gxDrawRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
613 // ////////////////////////////////////////////////////////////////////////// //
614 // use clip region as boundaries
615 public void gxDrawShadow () nothrow @trusted @nogc {
616 auto sv
= gxClipSave();
618 gxFillRect(sv
.rc
.x1
+1, sv
.rc
.y0
+8, 8, sv
.rc
.height
-8, gxRGBA
!(0, 0, 0, 127));
619 gxFillRect(sv
.rc
.x0
+8, sv
.rc
.y1
+1, sv
.rc
.width
, 8, gxRGBA
!(0, 0, 0, 127));
623 public void gxDrawWindow (const(char)[] title
, uint framecolor
, uint titlecolor
, uint titlebackcolor
, uint windowcolor
) nothrow @trusted {
628 gxFillRect(gxClipRect
, windowcolor
);
629 gxDrawRect(gxClipRect
, framecolor
);
631 if (title
!is null) {
632 auto sv
= gxClipSave();
633 int hgt
= (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+1);
634 gxClipRect
.intersect(GxRect(sv
.rc
.x0
+1, sv
.rc
.y0
+1, sv
.rc
.width
-2, hgt
));
635 if (!gxClipRect
.empty
) {
636 gxFillRect(gxClipRect
, titlebackcolor
);
637 gxDrawTextUtf(gxClipRect
.x0
+(gxClipRect
.width
-gxTextWidthUtf(title
))/2, gxClipRect
.y0
+(hgt
-gxTextHeightUtf
)/2, title
, titlecolor
);
643 // ////////////////////////////////////////////////////////////////////////// //
644 public void gxDrawScrollBar() (in auto ref GxRect r
, int max
, int value
) nothrow @trusted @nogc { gxDrawScrollBar(r
, 0, max
, value
); }
646 public void gxDrawScrollBar() (in auto ref GxRect r
, int min
, int max
, int value
) nothrow @trusted @nogc {
647 enum FrameColor
= gxRGB
!(220, 220, 220);
648 enum EmptyColor
= gxRGB
!(0, 0, 0);
649 enum FullColor
= gxRGB
!(160, 160, 160);
651 if (max
<= min
) min
= max
= value
= 0;
653 //conwriteln("00: min=", min, "; max=", max, "; value=", value);
656 if (value
< 0) value
= 0; else if (value
> max
) value
= max
;
657 //conwriteln("01: min=", min, "; max=", max, "; value=", value);
661 int hgt
= r
.height
-2;
662 bool vert
= (r
.width
< r
.height
);
664 if ((vert
&& wdt
> 1) ||
(!vert
&& hgt
> 1)) {
665 gxHLine(r
.x0
+1, r
.y0
+0, wdt
, FrameColor
);
666 gxVLine(r
.x0
+0, r
.y0
+1, hgt
, FrameColor
);
667 gxVLine(r
.x1
+0, r
.y0
+1, hgt
, FrameColor
);
668 gxHLine(r
.x0
+1, r
.y1
+0, wdt
, FrameColor
);
676 gxFillRect(sx0
, sy0
, wdt
, hgt
, FullColor
);
680 int pix
= hgt
*value
/max
;
681 if (pix
> hgt
) pix
= hgt
; // just in case
682 gxFillRect(sx0
, sy0
, wdt
, pix
, FullColor
);
683 gxFillRect(sx0
, sy0
+pix
, wdt
, hgt
-pix
, EmptyColor
);
685 int pix
= wdt
*value
/max
;
686 if (pix
> wdt
) pix
= wdt
; // just in case
687 gxFillRect(sx0
, sy0
, pix
, hgt
, FullColor
);
688 gxFillRect(sx0
+pix
, sy0
, wdt
-pix
, hgt
, EmptyColor
);