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
;
29 // ////////////////////////////////////////////////////////////////////////// //
30 package __gshared
int VBufWidth
= 740*2;
31 package __gshared
int VBufHeight
= 520*2;
32 package __gshared
ubyte vbufEffScale
= 1; // effective (current) window scale
34 package __gshared
uint* vglTexBuf
; // OpenGL texture buffer
37 // ////////////////////////////////////////////////////////////////////////// //
38 public @property int screenEffScale () nothrow @trusted @nogc { pragma(inline
, true); return vbufEffScale
; }
39 public @property void screenEffScale (int scale
) nothrow @trusted @nogc { pragma(inline
, true); if (scale
< 1) scale
= 1; if (scale
> 32) scale
= 32; vbufEffScale
= cast(ubyte)scale
; }
41 public @property int screenWidth () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
; }
42 public @property int screenHeight () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
; }
44 public @property int screenWidthScaled () nothrow @trusted @nogc { pragma(inline
, true); return VBufWidth
*vbufEffScale
; }
45 public @property int screenHeightScaled () nothrow @trusted @nogc { pragma(inline
, true); return VBufHeight
*vbufEffScale
; }
48 // ////////////////////////////////////////////////////////////////////////// //
55 // ////////////////////////////////////////////////////////////////////////// //
56 public struct GxPoint
{
61 this() (in auto ref GxPoint p
) pure { pragma(inline
, true); x
= p
.x
; y
= p
.y
; }
62 this (in int ax
, in int ay
) pure { pragma(inline
, true); x
= ax
; y
= ay
; }
64 bool inside() (in auto ref GxRect rc
) pure const { pragma(inline
, true); return rc
.inside(this); }
65 bool inside() (in auto ref GxSize sz
) pure const { pragma(inline
, true); return sz
.inside(this); }
67 bool opEquals() (in auto ref GxPoint p
) pure const { pragma(inline
, true); return ((p
.x^x
)|
(p
.y^y
)); }
69 int opCmp() (in auto ref GxPoint p
) pure const {
79 void opAssign() (in auto ref GxPoint p
) { pragma(inline
, true); x
= p
.x
; y
= p
.y
; }
81 void opOpAssign(string op
) (in int v
) if (op
== "+" || op
== "-" || op
== "*" || op
== "/") {
83 mixin("x"~op
~"=v; y"~op
~"=v;");
86 void opOpAssign(string op
) (in auto ref GxPoint pt
) if (op
== "+" || op
== "-") {
88 mixin("x"~op
~"=pt.x; y"~op
~"=pt.y;");
91 GxPoint
opBinary(string op
) (in auto ref GxPoint pt
) pure const if (op
== "+" || op
== "-") {
93 mixin("return GxPoint(x"~op
~"pt.x, y"~op
~"pt.y);");
96 GxPoint
opBinary(string op
) (in auto ref GxSize sz
) pure const if (op
== "+" || op
== "-") {
98 mixin("return GxPoint(x"~op
~"sz.w, y"~op
~"sz.h);");
101 GxPoint
opBinary(string op
) (in int v
) pure const if (op
== "+" || op
== "-") {
102 pragma(inline
, true);
103 mixin("return GxPoint(x"~op
~"v, y"~op
~"v);");
106 int opIndex (in GxDir dir
) pure const { pragma(inline
, true); return (dir
== GxDir
.Horiz ? x
: y
); }
108 void opIndexAssign (in int v
, in GxDir dir
) { pragma(inline
, true); if (dir
== GxDir
.Horiz
) x
= v
; else y
= v
; }
110 void opIndexOpAssign(string op
) (in int v
, in GxDir dir
) if (op
== "+" || op
== "-") {
111 pragma(inline
, true);
112 if (dir
== GxDir
.Horiz
) mixin("x "~op
~"= v;"); else mixin("y "~op
~"= v;");
117 // ////////////////////////////////////////////////////////////////////////// //
118 public struct GxSize
{
123 @property bool valid () pure const { pragma(inline
, true); return (w
>= 0 && h
>= 0); }
124 @property bool empty () pure const { pragma(inline
, true); return (w
< 1 || h
< 1); }
126 bool inside() (in auto ref GxPoint pt
) pure const { pragma(inline
, true); return (pt
.x
>= 0 && pt
.y
>= 0 && pt
.x
< w
&& pt
.y
< h
); }
128 void sanitize () { pragma(inline
, true); if (w
< 0) w
= 0; if (h
< 0) h
= 0; }
130 void opOpAssign(string op
) (in int v
) if (op
== "+" || op
== "-" || op
== "*" || op
== "/") {
131 pragma(inline
, true);
132 mixin("w"~op
~"=v; h"~op
~"=v;");
135 void opOpAssign(string op
) (in auto ref GxSize sz
) if (op
== "+" || op
== "-") {
136 pragma(inline
, true);
137 mixin("w"~op
~"=sz.w; h"~op
~"=sz.h;");
140 GxSize
opBinary(string op
) (in auto ref GxSize sz
) pure const if (op
== "+" || op
== "-") {
141 pragma(inline
, true);
142 mixin("return GxSize(w"~op
~"sz.w, h"~op
~"sz.h);");
145 int opIndex (in GxDir dir
) pure const { pragma(inline
, true); return (dir
== GxDir
.Horiz ? w
: h
); }
147 void opIndexAssign (in int v
, in GxDir dir
) { pragma(inline
, true); if (dir
== GxDir
.Horiz
) w
= v
; else h
= v
; }
150 void opIndexOpAssign(string op
) (in int v
, in GxDir dir
) if (op
== "+" || op
== "-") {
151 pragma(inline
, true);
152 if (dir
== GxDir
.Horiz
) {
153 mixin("w "~op
~"= v;");
156 mixin("h "~op
~"= v;");
163 // ////////////////////////////////////////////////////////////////////////// //
164 public struct GxRect
{
167 GxSize size
= GxSize(-1, -1); // <0: invalid rect
169 string
toString () const @trusted nothrow {
171 import core
.stdc
.stdio
: snprintf
;
172 char[128] buf
= void;
173 return buf
[0..snprintf(buf
.ptr
, buf
.length
, "(%d,%d)-(%d,%d)", pos
.x
, pos
.y
, pos
.x
+size
.w
-1, pos
.y
+size
.h
-1)].idup
;
175 return "(invalid-rect)";
180 this() (in auto ref GxRect rc
) pure {
181 pragma(inline
, true);
186 this (in int ax0
, in int ay0
, in int awidth
, in int aheight
) pure {
187 pragma(inline
, true);
194 this() (in int ax0
, in int ay0
, in auto ref GxSize asize
) pure {
195 pragma(inline
, true);
201 this() (in auto ref GxPoint xy0
, in int awidth
, in int aheight
) pure {
202 pragma(inline
, true);
208 this() (in auto ref GxPoint xy0
, in auto ref GxSize asize
) pure {
209 pragma(inline
, true);
214 this() (in auto ref GxPoint xy0
, in auto ref GxPoint xy1
) pure {
215 pragma(inline
, true);
217 size
.w
= xy1
.x
-xy0
.x
+1;
218 size
.h
= xy1
.y
-xy0
.y
+1;
221 this (in int awidth
, in int aheight
) pure {
222 pragma(inline
, true);
228 this() (in auto ref GxSize asize
) pure {
229 pragma(inline
, true);
234 void setCoords(bool doSwap
=true) (in int ax0
, in int ay0
, in int ax1
, in int ay1
) {
235 pragma(inline
, true);
237 pos
.x
= (ax0
< ax1 ? ax0
: ax1
);
238 pos
.y
= (ay0
< ay1 ? ay0
: ay1
);
239 size
.w
= (ax0
< ax1 ? ax1
: ax0
)-pos
.x
+1;
240 size
.h
= (ay0
< ay1 ? ay1
: ay0
)-pos
.y
+1;
249 void setCoords(bool doSwap
=true) (in auto ref GxPoint p0
, in int ax1
, in int ay1
) { pragma(inline
, true); setCoords
!doSwap(p0
.x
, p0
.y
, ax1
, ay1
); }
250 void setCoords(bool doSwap
=true) (in int ax0
, in int ay0
, in auto ref GxPoint p1
) { pragma(inline
, true); setCoords
!doSwap(ax0
, ay0
, p1
.x
, p1
.y
); }
251 void setCoords(bool doSwap
=true) (in auto ref GxPoint p0
, in auto ref GxPoint p1
) { pragma(inline
, true); setCoords
!doSwap(p0
.x
, p0
.y
, p1
.x
, p1
.y
); }
253 void opAssign() (in auto ref GxRect rc
) { pragma(inline
, true); pos
= rc
.pos
; size
= rc
.size
; }
255 bool opEquals() (in auto ref GxRect rc
) pure const { pragma(inline
, true); return (pos
== rc
.pos
&& size
== rc
.size
); }
257 int opCmp() (in auto ref GxRect p
) pure const { pragma(inline
, true); return pos
.opCmp(p
.pos
); }
259 @property bool valid () pure const { pragma(inline
, true); return size
.valid
; }
260 @property bool invalid () pure const { pragma(inline
, true); return !size
.valid
; }
261 @property bool empty () pure const { pragma(inline
, true); return size
.empty
; } // invalid rects are empty
263 void invalidate () { pragma(inline
, true); size
.w
= size
.h
= -1; }
265 @property int left () pure const { pragma(inline
, true); return pos
.x
; }
266 @property void left (in int v
) { pragma(inline
, true); pos
.x
= v
; }
268 @property int top () pure const { pragma(inline
, true); return pos
.y
; }
269 @property void top (in int v
) { pragma(inline
, true); pos
.y
= v
; }
271 @property int right () pure const { pragma(inline
, true); return pos
.x
+size
.w
-1; }
272 @property void right (in int v
) { pragma(inline
, true); size
.w
= v
-pos
.x
+1; }
274 @property int bottom () pure const { pragma(inline
, true); return pos
.y
+size
.h
-1; }
275 @property void bottom (in int v
) { pragma(inline
, true); size
.h
= v
-pos
.y
+1; }
277 @property GxPoint
lefttop () pure const { pragma(inline
, true); return pos
; }
278 @property GxPoint
righttop () pure const { pragma(inline
, true); return pos
+GxSize(size
.w
-1, 0); }
279 @property GxPoint
leftbottom () pure const { pragma(inline
, true); return pos
+GxSize(0, size
.h
-1); }
280 @property GxPoint
rightbottom () pure const { pragma(inline
, true); return pos
+size
-1; }
282 @property void lefttop() (in auto ref GxPoint p
) { pragma(inline
, true); setCoords
!false(p
, rightbottom
); }
283 @property void rightbottom() (in auto ref GxPoint p
) { pragma(inline
, true); setCoords
!false(lefttop
, p
); }
285 alias topleft
= lefttop
;
286 alias topright
= righttop
;
287 alias bottomleft
= leftbottom
;
288 alias bottomright
= rightbottom
;
290 @property int x0 () pure const { pragma(inline
, true); return pos
.x
; }
291 @property int y0 () pure const { pragma(inline
, true); return pos
.y
; }
293 @property void x0 (in int val
) { pragma(inline
, true); setCoords
!false(val
, pos
.y
, rightbottom
); }
294 @property void y0 (in int val
) { pragma(inline
, true); setCoords
!false(pos
.x
, val
, rightbottom
); }
296 @property int x1 () pure const { pragma(inline
, true); return (width
> 0 ? x0
+width
-1 : x0
-1); }
297 @property int y1 () pure const { pragma(inline
, true); return (height
> 0 ? y0
+height
-1 : y0
-1); }
299 @property void x1 (in int val
) { pragma(inline
, true); width
= val
-x0
+1; }
300 @property void y1 (in int val
) { pragma(inline
, true); height
= val
-y0
+1; }
302 @property int width () pure const { pragma(inline
, true); return size
.w
; }
303 @property int height () pure const { pragma(inline
, true); return size
.h
; }
305 @property void width (in int val
) { pragma(inline
, true); size
.w
= val
; }
306 @property void height (in int val
) { pragma(inline
, true); size
.h
= val
; }
308 // is point inside this rect?
309 bool inside() (in auto ref GxPoint p
) pure const {
310 pragma(inline
, true);
311 return (width
> 0 && height
> 0 ?
(p
.x
>= x0
&& p
.y
>= y0
&& p
.x
< x0
+width
&& p
.y
< y0
+height
) : false);
314 // is point inside this rect?
315 bool inside (in int ax
, in int ay
) pure const {
316 pragma(inline
, true);
317 return (width
> 0 && height
> 0 ?
(ax
>= x0
&& ay
>= y0
&& ax
< x0
+width
&& ay
< y0
+height
) : false);
320 // is `r` inside `this`?
321 bool contains() (in auto ref GxRect r
) pure const {
322 pragma(inline
, true);
324 width
> 0 && height
> 0 &&
325 r
.width
> 0 && r
.height
> 0 &&
326 r
.x0
>= x0
&& r
.y0
>= y0
&&
327 r
.x0
+r
.width
<= x0
+width
&& r
.y0
+r
.height
<= y0
+height
;
330 // does `r` and `this` overlap?
331 bool overlaps() (in auto ref GxRect r
) pure const {
332 pragma(inline
, true);
334 width
> 0 && height
> 0 &&
335 r
.width
> 0 && r
.height
> 0 &&
336 x0
< r
.x0
+r
.width
&& r
.x0
< x0
+width
&&
337 y0
< r
.y0
+r
.height
&& r
.y0
< y0
+height
;
340 // extend `this` so it will include `p`
341 void include() (in auto ref GxPoint p
) {
342 pragma(inline
, true);
349 if (p
.x
< x0
) x0
= p
.x0
;
350 if (p
.y
< y0
) y0
= p
.y0
;
351 if (p
.x1
> x1
) x1
= p
.x1
;
352 if (p
.y1
> y1
) y1
= p
.y1
;
356 // extend `this` so it will include `r`
357 void include() (in auto ref GxRect r
) {
358 pragma(inline
, true);
366 if (r
.x
< x0
) x0
= r
.x0
;
367 if (r
.y
< y0
) y0
= r
.y0
;
368 if (r
.x1
> x1
) x1
= r
.x1
;
369 if (r
.y1
> y1
) y1
= r
.y1
;
374 // clip `this` so it will not be larger than `r`
375 // returns `false` if the resulting rect (this) is empty or invalid
376 bool intersect (in int rx0
, in int ry0
, in int rwdt
, in int rhgt
) {
377 if (rwdt
< 0 || rhgt
< 0 || invalid
) { size
.w
= size
.h
= -1; return false; }
378 if (rwdt
== 0 || rhgt
== 0 || empty
) { size
.w
= size
.h
= 0; return false; }
379 immutable int rx1
= rx0
+rwdt
-1;
380 immutable int ry1
= ry0
+rhgt
-1;
381 if (ry1
< y0 || rx1
< x0 || rx0
> x1 || ry0
> y1
) { size
.w
= size
.h
= 0; return false; }
382 // rc is at least partially inside this rect
383 if (x0
< rx0
) x0
= rx0
;
384 if (y0
< ry0
) y0
= ry0
;
385 if (x1
> rx1
) x1
= rx1
;
386 if (y1
> ry1
) y1
= ry1
;
387 assert(!empty
); // yeah, always
391 // clip `this` so it will not be larger than `r`
392 // returns `false` if the resulting rect (this) is empty or invalid
393 bool intersect (in int rwdt
, in int rhgt
) {
394 pragma(inline
, true);
395 return intersect(0, 0, rwdt
, rhgt
);
398 // clip `this` so it will not be larger than `r`
399 // returns `false` if the resulting rect (this) is empty or invalid
400 bool intersect() (in auto ref GxRect r
) {
401 pragma(inline
, true);
402 return intersect(r
.x0
, r
.y0
, r
.width
, r
.height
);
405 void shrinkBy (in int dx
, in int dy
) {
406 pragma(inline
, true);
407 if ((dx|dy
) && valid
) {
415 void shrinkBy() (in auto ref GxSize sz
) { pragma(inline
, true); shrinkBy(sz
.w
, sz
.h
); }
417 void growBy (in int dx
, in int dy
) {
418 pragma(inline
, true);
419 if ((dx|dy
) && valid
) {
427 void growBy() (in auto ref GxSize sz
) { pragma(inline
, true); growBy(sz
.w
, sz
.h
); }
429 void set (in int ax0
, in int ay0
, in int awidth
, in int aheight
) {
430 pragma(inline
, true);
437 void set() (in auto ref GxPoint p0
, in int awidth
, in int aheight
) { pragma(inline
, true); set(p0
.x
, p0
.y
, awidth
, aheight
); }
438 void set() (in auto ref GxPoint p0
, in auto ref GxSize asize
) { pragma(inline
, true); set(p0
.x
, p0
.y
, asize
.w
, asize
.h
); }
439 void set() (in int ax0
, in int ay0
, in auto ref GxSize asize
) { pragma(inline
, true); set(ax0
, ay0
, asize
.w
, asize
.h
); }
441 void moveLeftTopBy (in int dx
, in int dy
) {
442 pragma(inline
, true);
449 void moveLeftTopBy() (in auto ref GxPoint p
) { pragma(inline
, true); moveLeftTopBy(p
.x
, p
.y
); }
451 alias moveTopLeftBy
= moveLeftTopBy
;
453 void moveRightBottomBy (in int dx
, in int dy
) {
454 pragma(inline
, true);
459 void moveRightBottomBy() (in auto ref GxPoint p
) { pragma(inline
, true); moveRightBottomBy(p
.x
, p
.y
); }
461 alias moveBottomRightBy
= moveRightBottomBy
;
463 void moveBy (in int dx
, in int dy
) {
464 pragma(inline
, true);
469 void moveBy() (in auto ref GxPoint p
) { pragma(inline
, true); moveBy(p
.x
, p
.y
); }
471 void moveTo (in int nx
, in int ny
) {
472 pragma(inline
, true);
477 void moveTo() (in auto ref GxPoint p
) { pragma(inline
, true); moveTo(p
.x
, p
.y
); }
480 * clip (x,y,wdt) stripe to this rect
483 * x = stripe start (not relative to rect)
484 * y = stripe start (not relative to rect)
485 * wdt = stripe length
488 * x = fixed x (invalid if result is false)
489 * wdt = fixed length (invalid if result is false)
490 * leftSkip = how much cells skipped at the left side (invalid if result is false)
491 * result = false if stripe is completely clipped out
493 bool clipHStripe (ref int x
, int y
, ref int wdt
, int* leftSkip
=null) const @trusted {
494 if (empty
) return false;
495 if (wdt
<= 0 || y
< y0 || y
>= y0
+height || x
>= x0
+width
) return false;
498 immutable int dx
= x0
-x
;
499 if (dx
>= wdt
) return false; // avoid overflow
500 if (leftSkip
!is null) *leftSkip
= dx
;
503 assert(wdt
> 0); // yeah, always
505 if (wdt
> width
) wdt
= width
; // avoid overflow
506 if (x
+wdt
> x0
+width
) {
509 assert(wdt
> 0); // yeah, always
514 bool clipHStripe (ref GxPoint p
, ref int wdt
, int* leftSkip
=null) const @trusted {
515 pragma(inline
, true);
516 return clipHStripe(ref p
.x
, p
.y
, ref wdt
, leftSkip
);
520 * clip (x,y,hgt) stripe to this rect
523 * x = stripe start (not relative to rect)
524 * y = stripe start (not relative to rect)
525 * hgt = stripe length
528 * y = fixed y (invalid if result is false)
529 * hgt = fixed length (invalid if result is false)
530 * topSkip = how much cells skipped at the top side (invalid if result is false)
531 * result = false if stripe is completely clipped out
533 bool clipVStripe (int x
, ref int y
, ref int hgt
, int* topSkip
=null) const @trusted {
534 if (empty
) return false;
535 if (hgt
<= 0 || x
< x0 || x
>= x0
+width || y
>= y0
+height
) return false;
538 immutable int dy
= y0
-y
;
539 if (dy
>= hgt
) return false; // avoid overflow
540 if (topSkip
!is null) *topSkip
= dy
;
543 assert(hgt
> 0); // yeah, always
545 if (hgt
> height
) hgt
= height
; // avoid overflow
546 if (y
+hgt
> y0
+height
) {
549 assert(hgt
> 0); // yeah, always
554 bool clipVStripe (ref GxPoint p
, ref int hgt
, int* topSkip
=null) const @trusted {
555 pragma(inline
, true);
556 return clipVStripe(p
.x
, ref p
.y
, ref hgt
, topSkip
);
559 bool clipHVStripes (ref int x
, ref int y
, ref int wdt
, ref int hgt
, int* leftSkip
=null, int* topSkip
=null) const @trusted {
560 if (empty || wdt
<= 0 || hgt
<= 0) return false;
561 if (y
>= y0
+height || x
>= x0
+width
) return false;
562 // use dummy `x` and `y` for horizontal and vertical clippers, because they are only checked for validity there
563 if (!clipHStripe(ref x
, y0
, ref wdt
, leftSkip
)) return false;
564 return clipVStripe(x0
, ref y
, ref hgt
, topSkip
);
567 bool clipHVStripes (ref GxPoint p
, ref int wdt
, ref int hgt
, int* leftSkip
=null, int* topSkip
=null) const @trusted {
568 pragma(inline
, true);
569 return clipHVStripes(ref p
.x
, ref p
.y
, ref wdt
, ref hgt
, leftSkip
, topSkip
);
572 bool clipHVStripes (ref GxPoint p
, ref GxSize sz
, int* leftSkip
=null, int* topSkip
=null) const @trusted {
573 pragma(inline
, true);
574 return clipHVStripes(ref p
.x
, ref p
.y
, ref sz
.w
, ref sz
.h
, leftSkip
, topSkip
);
577 bool clipHVStripes (ref int x
, ref int y
, ref GxSize sz
, int* leftSkip
=null, int* topSkip
=null) const @trusted {
578 pragma(inline
, true);
579 return clipHVStripes(ref x
, ref y
, ref sz
.w
, ref sz
.h
, leftSkip
, topSkip
);
584 // ////////////////////////////////////////////////////////////////////////// //
585 public bool gxIsTransparent (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return ((clr
&0xff000000u
) == 0x00000000u
); }
586 public bool gxIsSolid (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return ((clr
&0xff000000u
) == 0xff000000u
); }
588 public bool gxIsSolidBlack (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return (clr
== 0xff000000u
); }
590 public ubyte gxGetRed (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return cast(ubyte)(clr
>>16); }
591 public ubyte gxGetGreen (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return cast(ubyte)(clr
>>8); }
592 public ubyte gxGetBlue (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return cast(ubyte)clr
; }
593 public ubyte gxGetAlpha (in uint clr
) pure nothrow @safe @nogc { pragma(inline
, true); return cast(ubyte)(clr
>>24); }
595 public enum gxSolidBlack
= 0xff000000u
;
596 public enum gxSolidWhite
= 0xffffffffu
;
598 public enum gxTransparent
= 0x00000000u
;
599 public enum gxColorMask
= 0x00ffffffu
;
601 public enum gxUnknown
= 0x00010203u
;
604 // ////////////////////////////////////////////////////////////////////////// //
605 // mix dc with ARGB (or ABGR) clr; dc A is ignored (removed)
606 // main code never calls this with solid or transparent `colvar`
607 enum GxColMixMixin(string destvar
, string dcvar
, string colvar
) = `{
608 immutable uint col_ = `~colvar
~`;
609 immutable uint dc_ = (`~dcvar
~`)&0xffffff;
610 /*immutable uint a_ = 256-(col_>>24);*/ /* to not loose bits */
611 immutable uint a_ = (col_>>24);
612 immutable uint srb_ = (col_&0xff00ff);
613 immutable uint sg_ = (col_&0x00ff00);
614 immutable uint drb_ = (dc_&0xff00ff);
615 immutable uint dg_ = (dc_&0x00ff00);
616 immutable uint orb_ = (drb_+(((srb_-drb_)*a_+0x800080)>>8))&0xff00ff;
617 immutable uint og_ = (dg_+(((sg_-dg_)*a_+0x008000)>>8))&0x00ff00;
618 (`~destvar
~`) = orb_|og_;
621 public uint gxColMix (in uint dc
, in uint clr
) pure nothrow @trusted @nogc {
622 pragma(inline
, true);
623 if (gxIsSolid(clr
)) return clr
;
624 else if (gxIsTransparent(clr
)) return dc
;
627 mixin(GxColMixMixin
!("res", "dc", "clr"));
633 // ////////////////////////////////////////////////////////////////////////// //
634 private template isGoodRGBInt(T
) {
635 import std
.traits
: Unqual
;
639 is(TT
== short) ||
is(TT
== ushort) ||
640 is(TT
== int) ||
is(TT
== uint) ||
641 is(TT
== long) ||
is(TT
== ulong);
645 // ////////////////////////////////////////////////////////////////////////// //
646 public uint gxrgb(T0
, T1
, T2
) (T0 r
, T1 g
, T2 b
) pure nothrow @trusted @nogc
647 if (isGoodRGBInt
!T0
&& isGoodRGBInt
!T1
&& isGoodRGBInt
!T2
)
649 pragma(inline
, true);
650 return (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
)|
0xff000000u
;
653 public uint gxrgba(T0
, T1
, T2
, T3
) (T0 r
, T1 g
, T2 b
, T3 a
) pure nothrow @trusted @nogc
654 if (isGoodRGBInt
!T0
&& isGoodRGBInt
!T1
&& isGoodRGBInt
!T2
&& isGoodRGBInt
!T3
)
656 pragma(inline
, true);
657 return (clampToByte(a
)<<24)|
(clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
661 public enum gxRGB(int r
, int g
, int b
) = (clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
)|
0xff000000u
;
662 public enum gxRGBA(int r
, int g
, int b
, int a
) = (clampToByte(a
)<<24)|
(clampToByte(r
)<<16)|
(clampToByte(g
)<<8)|
clampToByte(b
);
665 // ////////////////////////////////////////////////////////////////////////// //
667 public __gshared GxRect gxClipRect
= GxRect(65535, 65535);
669 public void gxWithSavedClip(DG
) (scope DG dg
)
670 if (is(typeof((inout int=0) { DG dg
= void; dg(); })))
672 pragma(inline
, true);
674 immutable rc
= gxClipRect
;
675 scope(exit
) gxClipRect
= rc
;
680 public void gxClipReset () nothrow @trusted @nogc {
681 pragma(inline
, true);
682 gxClipRect
= GxRect(65535, 65535);
686 // ////////////////////////////////////////////////////////////////////////// //
687 public void gxClearScreen (uint clr
) nothrow @trusted @nogc {
688 clr
&= gxColorMask
; // only solid color matters here anyway
690 import core
.stdc
.string
: memset
;
691 memset(vglTexBuf
, 0, (VBufWidth
*VBufHeight
)<<2);
693 vglTexBuf
[0..VBufWidth
*VBufHeight
] = clr
;
698 public void gxPutPixel (in int x
, in int y
, in uint c
) nothrow @trusted @nogc {
699 pragma(inline
, true);
700 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& !gxIsTransparent(c
) && gxClipRect
.inside(x
, y
)) {
701 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
;
702 *dp
= gxColMix(*dp
, c
);
707 public void gxPutPixel() (in auto ref GxPoint p
, in uint c
) nothrow @trusted @nogc {
708 pragma(inline
, true);
709 if (p
.x
>= 0 && p
.y
>= 0 && p
.x
< VBufWidth
&& p
.y
< VBufHeight
&& !gxIsTransparent(c
) && gxClipRect
.inside(p
)) {
710 uint* dp
= cast(uint*)(cast(ubyte*)vglTexBuf
)+p
.y
*VBufWidth
+p
.x
;
711 *dp
= (gxIsSolid(c
) ?
(c
&gxColorMask
) : gxColMix(*dp
, c
));
716 public void gxSetPixel (in int x
, in int y
, in uint c
) nothrow @trusted @nogc {
717 pragma(inline
, true);
718 if (x
>= 0 && y
>= 0 && x
< VBufWidth
&& y
< VBufHeight
&& !gxIsTransparent(c
) && gxClipRect
.inside(x
, y
)) {
719 *(cast(uint*)(cast(ubyte*)vglTexBuf
)+y
*VBufWidth
+x
) = c
&gxColorMask
;
724 public void gxSetPixel() (in auto ref GxPoint p
, in uint c
) nothrow @trusted @nogc {
725 pragma(inline
, true);
726 if (p
.x
>= 0 && p
.y
>= 0 && p
.x
< VBufWidth
&& p
.y
< VBufHeight
&& !gxIsTransparent(c
) && gxClipRect
.inside(p
)) {
727 *(cast(uint*)(cast(ubyte*)vglTexBuf
)+p
.y
*VBufWidth
+p
.x
) = c
&gxColorMask
;
732 // ////////////////////////////////////////////////////////////////////////// //
733 public void gxHLine (int x
, int y
, int w
, in uint clr
) nothrow @trusted @nogc {
734 if (gxIsTransparent(clr
)) return;
735 if (!gxClipRect
.clipHStripe(x
, y
, w
)) return;
736 if (!GxRect(VBufWidth
, VBufHeight
).clipHStripe(x
, y
, w
)) return;
737 if (gxIsSolid(clr
)) {
738 immutable uint addr
= y
*VBufWidth
+x
;
739 if (gxIsSolidBlack(clr
)) {
740 import core
.stdc
.string
: memset
;
741 memset(vglTexBuf
+addr
, 0, w
<<2);
743 vglTexBuf
[addr
..addr
+w
] = clr
;
746 uint* dptr
= vglTexBuf
+y
*VBufWidth
+x
;
748 mixin(GxColMixMixin
!("*dptr++", "*dptr", "clr"));
753 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
); }
755 public void gxVLine (int x
, int y
, int h
, in uint clr
) nothrow @trusted @nogc {
756 if (gxIsTransparent(clr
)) return;
757 if (!gxClipRect
.clipVStripe(x
, y
, h
)) return;
758 if (!GxRect(VBufWidth
, VBufHeight
).clipVStripe(x
, y
, h
)) return;
759 uint* dptr
= vglTexBuf
+y
*VBufWidth
+x
;
760 if (gxIsSolid(clr
)) {
761 while (h
-- > 0) { *dptr
= clr
; dptr
+= VBufWidth
; }
763 while (h
-- > 0) { mixin(GxColMixMixin
!("*dptr", "*dptr", "clr")); dptr
+= VBufWidth
; }
767 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
); }
770 // ////////////////////////////////////////////////////////////////////////// //
771 public void gxFillRect (int x
, int y
, int w
, int h
, in uint clr
) nothrow @trusted @nogc {
772 if (gxIsTransparent(clr
)) return;
773 if (!gxClipRect
.clipHVStripes(x
, y
, w
, h
)) return;
774 if (!GxRect(VBufWidth
, VBufHeight
).clipHVStripes(x
, y
, w
, h
)) return;
775 if (gxIsSolid(clr
)) {
776 uint addr
= y
*VBufWidth
+x
;
777 if (gxIsSolidBlack(clr
)) {
778 import core
.stdc
.string
: memset
;
780 memset(vglTexBuf
+addr
, 0, w
<<2);
785 vglTexBuf
[addr
..addr
+w
] = clr
;
790 uint* dptr
= vglTexBuf
+y
*VBufWidth
+x
;
791 immutable uint dinc
= VBufWidth
-w
;
793 foreach (immutable _
; 0..w
) {
794 mixin(GxColMixMixin
!("*dptr++", "*dptr", "clr"));
801 public void gxFillRect() (in auto ref GxRect rc
, in uint clr
) nothrow @trusted @nogc {
802 pragma(inline
, true);
803 gxFillRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
806 public void gxDrawRect (in int x
, in int y
, in int w
, in int h
, in uint clr
) nothrow @trusted @nogc {
807 if (w
< 1 || h
< 1 ||
gxIsTransparent(clr
)) return;
808 gxHLine(x
, y
, w
, clr
);
809 if (h
> 1) gxHLine(x
, y
+h
-1, w
, clr
);
811 gxVLine(x
, y
+1, h
-2, clr
);
812 if (w
> 1) gxVLine(x
+w
-1, y
+1, h
-2, clr
);
816 public void gxDrawRect() (in auto ref GxRect rc
, in uint clr
) nothrow @trusted @nogc {
817 pragma(inline
, true);
818 gxDrawRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, clr
);
822 // ////////////////////////////////////////////////////////////////////////// //
823 // use clip region as boundaries
824 public void gxDrawShadow (in GxRect winrect
) nothrow @trusted @nogc {
825 if (winrect
.empty
) return;
827 //immutable GxRect rc = gxClipRect;
829 gxFillRect(winrect
.x1
+1, winrect
.y0
+8, 8, winrect
.height
-8, gxRGBA
!(0, 0, 0, 127));
830 gxFillRect(winrect
.x0
+8, winrect
.y1
+1, winrect
.width
, 8, gxRGBA
!(0, 0, 0, 127));
835 public void gxDrawWindow (in GxRect winrect
,
836 const(char)[] title
, in uint framecolor
, in uint titlecolor
,
837 in uint titlebackcolor
, in uint windowcolor
) nothrow @trusted
839 import iv
.egra
.gfx
.text
;
841 if (winrect
.empty
) return;
842 gxDrawShadow(winrect
);
844 gxFillRect(winrect
, windowcolor
);
845 gxDrawRect(winrect
, framecolor
);
847 if (title
is null) return;
848 if (winrect
.width
<= 2 || winrect
.height
<= 2) return;
850 immutable int hgt
= (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+1);
851 if (gxClipRect
.intersect(winrect
.x0
+1, winrect
.y0
+1, winrect
.width
-2, hgt
)) {
852 gxFillRect(gxClipRect
, titlebackcolor
);
853 gxDrawTextUtf(winrect
.x0
+1+(winrect
.width
-2-gxTextWidthUtf(title
))/2, winrect
.y0
+1+(hgt
-gxTextHeightUtf
)/2, title
, titlecolor
);
859 // ////////////////////////////////////////////////////////////////////////// //
860 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
); }
862 public void gxDrawScrollBar() (in auto ref GxRect r
, int min
, int max
, int value
) nothrow @trusted @nogc {
863 enum FrameColor
= gxRGB
!(220, 220, 220);
864 enum EmptyColor
= gxRGB
!(0, 0, 0);
865 enum FullColor
= gxRGB
!(160, 160, 160);
867 if (max
<= min
) min
= max
= value
= 0;
869 //conwriteln("00: min=", min, "; max=", max, "; value=", value);
872 if (value
< 0) value
= 0; else if (value
> max
) value
= max
;
873 //conwriteln("01: min=", min, "; max=", max, "; value=", value);
877 int hgt
= r
.height
-2;
878 bool vert
= (r
.width
< r
.height
);
880 if ((vert
&& wdt
> 1) ||
(!vert
&& hgt
> 1)) {
881 gxHLine(r
.x0
+1, r
.y0
+0, wdt
, FrameColor
);
882 gxVLine(r
.x0
+0, r
.y0
+1, hgt
, FrameColor
);
883 gxVLine(r
.x1
+0, r
.y0
+1, hgt
, FrameColor
);
884 gxHLine(r
.x0
+1, r
.y1
+0, wdt
, FrameColor
);
892 gxFillRect(sx0
, sy0
, wdt
, hgt
, FullColor
);
896 int pix
= hgt
*value
/max
;
897 if (pix
> hgt
) pix
= hgt
; // just in case
898 gxFillRect(sx0
, sy0
, wdt
, pix
, FullColor
);
899 gxFillRect(sx0
, sy0
+pix
, wdt
, hgt
-pix
, EmptyColor
);
901 int pix
= wdt
*value
/max
;
902 if (pix
> wdt
) pix
= wdt
; // just in case
903 gxFillRect(sx0
, sy0
, pix
, hgt
, FullColor
);
904 gxFillRect(sx0
+pix
, sy0
, wdt
-pix
, hgt
, EmptyColor
);
909 // ////////////////////////////////////////////////////////////////////////// //
910 private int abs (int a
) pure nothrow @safe @nogc { pragma(inline
, true); return (a
< 0 ?
-a
: a
); }
912 public void gxCircle (in int cx
, in int cy
, in int radius
, in uint clr
) nothrow @trusted @nogc {
913 static void plot4points (in int cx
, in int cy
, in int x
, in int y
, in uint clr
) nothrow @trusted @nogc {
914 pragma(inline
, true);
915 gxPutPixel(cx
+x
, cy
+y
, clr
);
916 if (x
) gxPutPixel(cx
-x
, cy
+y
, clr
);
917 if (y
) gxPutPixel(cx
+x
, cy
-y
, clr
);
918 gxPutPixel(cx
-x
, cy
-y
, clr
);
921 if (radius
<= 0 ||
gxIsTransparent(clr
)) return;
922 if (radius
== 1) { gxPutPixel(cx
, cy
, clr
); return; }
923 int error
= -radius
, x
= radius
, y
= 0;
925 plot4points(cx
, cy
, x
, y
, clr
);
926 plot4points(cx
, cy
, y
, x
, clr
);
929 if (error
>= 0) { --x
; error
-= x
*2; }
931 plot4points(cx
, cy
, x
, y
, clr
);
934 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
); }
937 public void gxFillCircle (in int cx
, in int cy
, in int radius
, in uint clr
) nothrow @trusted @nogc {
938 if (radius
<= 0 ||
gxIsTransparent(clr
)) return;
939 if (radius
== 1) { gxPutPixel(cx
, cy
, clr
); return; }
940 int error
= -radius
, x
= radius
, y
= 0;
946 gxHLine(cx
-x
, cy
+last_y
, 2*x
+1, clr
);
947 if (x
!= 0 && last_y
!= 0) gxHLine(cx
-x
, cy
-last_y
, 2*x
+1, clr
);
950 gxHLine(cx
-last_y
, cy
+x
, 2*last_y
+1, clr
);
951 if (last_y
!= 0 && x
!= 0) gxHLine(cx
-last_y
, cy
-x
, 2*last_y
+1, clr
);
960 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
); }
963 public void gxEllipse (int x0
, int y0
, int x1
, int y1
, in uint clr
) nothrow @trusted @nogc {
964 if (gxIsTransparent(clr
)) return;
965 if (y0
== y1
) { gxHLine(x0
, y0
, x1
-x0
+1, clr
); return; }
966 if (x0
== x1
) { gxVLine(x0
, y0
, y1
-y0
+1, clr
); return; }
967 int a
= abs(x1
-x0
), b
= abs(y1
-y0
), b1
= b
&1; // values of diameter
968 long dx
= 4*(1-a
)*b
*b
, dy
= 4*(b1
+1)*a
*a
; // error increment
969 long err
= dx
+dy
+b1
*a
*a
; // error of 1.step
970 if (x0
> x1
) { x0
= x1
; x1
+= a
; } // if called with swapped points...
971 if (y0
> y1
) y0
= y1
; // ...exchange them
972 y0
+= (b
+1)/2; y1
= y0
-b1
; // starting pixel
973 a
*= 8*a
; b1
= 8*b
*b
;
976 gxPutPixel(x1
, y0
, clr
); // I. Quadrant
977 gxPutPixel(x0
, y0
, clr
); // II. Quadrant
978 gxPutPixel(x0
, y1
, clr
); // III. Quadrant
979 gxPutPixel(x1
, y1
, clr
); // IV. Quadrant
981 if (e2
>= dx
) { ++x0
; --x1
; err
+= dx
+= b1
; } // x step
982 if (e2
<= dy
) { ++y0
; --y1
; err
+= dy
+= a
; } // y step
985 // too early stop of flat ellipses a=1
986 gxPutPixel(x0
-1, ++y0
, clr
); // complete tip of ellipse
987 gxPutPixel(x0
-1, --y1
, clr
);
991 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
); }
994 public void gxFillEllipse (int x0
, int y0
, int x1
, int y1
, in uint clr
) nothrow @trusted @nogc {
995 if (gxIsTransparent(clr
)) return;
996 if (y0
== y1
) { gxHLine(x0
, y0
, x1
-x0
+1, clr
); return; }
997 if (x0
== x1
) { gxVLine(x0
, y0
, y1
-y0
+1, clr
); return; }
998 int a
= abs(x1
-x0
), b
= abs(y1
-y0
), b1
= b
&1; // values of diameter
999 long dx
= 4*(1-a
)*b
*b
, dy
= 4*(b1
+1)*a
*a
; // error increment
1000 long err
= dx
+dy
+b1
*a
*a
; // error of 1.step
1001 int prev_y0
= -1, prev_y1
= -1;
1002 if (x0
> x1
) { x0
= x1
; x1
+= a
; } // if called with swapped points...
1003 if (y0
> y1
) y0
= y1
; // ...exchange them
1004 y0
+= (b
+1)/2; y1
= y0
-b1
; // starting pixel
1005 a
*= 8*a
; b1
= 8*b
*b
;
1008 if (y0
!= prev_y0
) { gxHLine(x0
, y0
, x1
-x0
+1, clr
); prev_y0
= y0
; }
1009 if (y1
!= y0
&& y1
!= prev_y1
) { gxHLine(x0
, y1
, x1
-x0
+1, clr
); prev_y1
= y1
; }
1011 if (e2
>= dx
) { ++x0
; --x1
; err
+= dx
+= b1
; } // x step
1012 if (e2
<= dy
) { ++y0
; --y1
; err
+= dy
+= a
; } // y step
1015 // too early stop of flat ellipses a=1
1016 gxPutPixel(x0
-1, ++y0
, clr
); // complete tip of ellipse
1017 gxPutPixel(x0
-1, --y1
, clr
);
1021 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
); }
1024 // ////////////////////////////////////////////////////////////////////////// //
1025 public void gxDrawRoundedRect (int x0
, int y0
, int wdt
, int hgt
, int radius
, in uint clr
) nothrow @trusted @nogc {
1026 static void gxArcs (int cx
, int cy
, int wdt
, int hgt
, in int radius
, in uint clr
) nothrow @trusted @nogc {
1027 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 {
1028 pragma(inline
, true);
1029 gxPutPixel(cx
+wdt
-1+x
, cy
+hgt
-1+y
, clr
);
1030 gxPutPixel(cx
-x
, cy
+hgt
-1+y
, clr
);
1031 gxPutPixel(cx
+wdt
-1+x
, cy
-y
, clr
);
1032 gxPutPixel(cx
-x
, cy
-y
, clr
);
1035 wdt
-= (radius
<<1); if (wdt
<= 0) return;
1036 hgt
-= (radius
<<1); if (hgt
<= 0) return;
1037 cx
+= radius
; cy
+= radius
;
1038 int error
= -radius
, x
= radius
, y
= 0;
1040 plot4points(radius
, wdt
, hgt
, cx
, cy
, x
, y
, clr
);
1041 plot4points(radius
, wdt
, hgt
, cx
, cy
, y
, x
, clr
);
1044 if (error
>= 0) { --x
; error
-= x
*2; }
1046 if (x || y
!= radius
) plot4points(radius
, wdt
, hgt
, cx
, cy
, x
, y
, clr
);
1050 if (wdt
< 1 || hgt
< 1) return;
1051 if (radius
< 1) { gxDrawRect(x0
, y0
, wdt
, hgt
, clr
); return; }
1052 if (gxIsTransparent(clr
)) return;
1053 if (hgt
== 1) { gxHLine(x0
, y0
, wdt
, clr
); return; }
1054 if (wdt
== 1) { gxVLine(x0
, y0
, hgt
, clr
); return; }
1056 immutable int minsz
= (wdt
< hgt ? wdt
: hgt
);
1057 if (radius
>= (minsz
>>1)) {
1058 radius
= (minsz
>>1)-1;
1059 if (radius
< 1) { gxDrawRect(x0
, y0
, wdt
, hgt
, clr
); return; }
1061 // draw the parts of the rect
1062 gxHLine(x0
+radius
+1, y0
, wdt
-(radius
<<1)-2, clr
); // top
1063 gxHLine(x0
+radius
+1, y0
+hgt
-1, wdt
-(radius
<<1)-2, clr
); // bottom
1064 gxVLine(x0
, y0
+radius
+1, hgt
-(radius
<<1)-2, clr
); // left
1065 gxVLine(x0
+wdt
-1, y0
+radius
+1, hgt
-(radius
<<1)-2, clr
); // right
1067 gxArcs(x0
, y0
, wdt
, hgt
, radius
, clr
);
1070 public void gxDrawRoundedRect() (in auto ref GxRect rc
, in int radius
, in uint clr
) nothrow @trusted @nogc {
1071 pragma(inline
, true);
1072 gxDrawRoundedRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, radius
, clr
);
1076 // ////////////////////////////////////////////////////////////////////////// //
1077 __gshared usize frectXCoords
; // array of ints
1078 __gshared usize frectXCSize
; // in items
1081 /* cyclic dependency
1082 shared static ~this () {
1084 import core.stdc.stdlib : free;
1085 free(cast(void*)frectXCoords);
1092 int* ensureXCoords (int radius
) nothrow @trusted @nogc {
1093 if (radius
< 1) return null;
1094 if (radius
> 1024*1024) return null;
1095 if (cast(usize
)radius
> frectXCSize
) {
1096 import core
.stdc
.stdlib
: realloc
;
1097 immutable usize newsz
= (cast(usize
)radius|
0x7fu
)+1;
1098 void* np
= realloc(cast(void*)frectXCoords
, newsz
*int.sizeof
);
1099 if (np
is null) return null; // out of memory
1100 frectXCSize
= newsz
;
1101 frectXCoords
= cast(usize
)np
;
1103 return cast(int*)frectXCoords
;
1107 // this is wrong, but i'm ok with it for now
1108 public void gxFillRoundedRect (int x0
, int y0
, int wdt
, int hgt
, int radius
, in uint clr
) nothrow @trusted @nogc {
1109 if (wdt
< 1 || hgt
< 1) return;
1110 if (radius
< 1) { gxFillRect(x0
, y0
, wdt
, hgt
, clr
); return; }
1111 if (gxIsTransparent(clr
)) return;
1112 if (hgt
== 1) { gxHLine(x0
, y0
, wdt
, clr
); return; }
1113 if (wdt
== 1) { gxVLine(x0
, y0
, hgt
, clr
); return; }
1115 immutable int minsz
= (wdt
< hgt ? wdt
: hgt
);
1116 if (radius
>= (minsz
>>1)) {
1117 radius
= (minsz
>>1)-1;
1118 if (radius
< 1) { gxFillRect(x0
, y0
, wdt
, hgt
, clr
); return; }
1121 // create border coords
1122 auto xpt
= ensureXCoords(radius
+1);
1123 if (xpt
is null) { gxFillRect(x0
, y0
, wdt
, hgt
, clr
); return; } // do at least something
1124 xpt
[0..radius
+1] = int.min
;
1128 int error
= -radius
, x
= radius
, y
= 0;
1130 if (y
<= radius
&& xpt
[y
] < x
) xpt
[y
] = x
;
1131 if (x
>= 0 && x
<= radius
&& xpt
[x
] < y
) xpt
[x
] = y
;
1134 if (error
>= 0) { --x
; error
-= x
*2; }
1136 if (y
<= radius
&& xpt
[y
] < x
) xpt
[y
] = x
;
1137 if (x
>= 0 && x
<= radius
&& xpt
[x
] < y
) xpt
[x
] = y
;
1140 // draw the filled rect
1141 gxFillRect(x0
, y0
+radius
+1, wdt
, hgt
-(radius
<<1)-2, clr
);
1144 foreach (immutable dy
; 0..radius
+1) {
1145 if (xpt
[dy
] == int.min
) continue;
1146 immutable topy
= y0
+radius
-dy
;
1147 immutable topx0
= x0
+radius
-xpt
[dy
];
1148 immutable topx1
= x0
+wdt
-radius
-1+xpt
[dy
];
1149 //gxPutPixel(topx0, topy, clr);
1150 //gxPutPixel(topx1, topy, clr);
1151 gxHLine(topx0
, topy
, topx1
-topx0
+1, clr
);
1152 immutable boty
= y0
+hgt
-radius
+dy
-1;
1153 //gxPutPixel(topx0, boty, clr);
1154 //gxPutPixel(topx1, boty, clr);
1155 gxHLine(topx0
, boty
, topx1
-topx0
+1, clr
);
1159 public void gxFillRoundedRect() (in auto ref GxRect rc
, in int radius
, in uint clr
) nothrow @trusted @nogc {
1160 pragma(inline
, true);
1161 gxFillRoundedRect(rc
.x0
, rc
.y0
, rc
.width
, rc
.height
, radius
, clr
);
1165 // ////////////////////////////////////////////////////////////////////////// //
1166 // bresenham with clipping
1167 // the idea is that we can simply skip the right number of steps
1168 // if the point is off the drawing area
1169 public void gxDrawLine (int x0
, int y0
, int x1
, int y1
, in uint clr
, bool lastPoint
=true) nothrow @trusted @nogc {
1170 enum swap(string a
, string b
) = "{immutable int tmp_="~a
~";"~a
~"="~b
~";"~b
~"=tmp_;}";
1172 if (gxIsTransparent(clr
)) return;
1174 GxRect realClip
= gxClipRect
;
1175 if (!realClip
.intersect(VBufWidth
, VBufHeight
)) return;
1178 if (x0
== x1
&& y0
== y1
) {
1179 if (lastPoint
) gxPutPixel(x0
, y0
, clr
);
1186 gxHLine(x1
+(lastPoint ?
0 : 1), y0
, x0
-x1
+(lastPoint ?
1 : 0), clr
);
1188 gxHLine(x0
, y0
, x1
-x0
+(lastPoint ?
1 : 0), clr
);
1194 int wx0
= realClip
.x0
, wy0
= realClip
.y0
, wx1
= realClip
.x1
, wy1
= realClip
.y1
;
1195 if (wx0
> wx1 || wy0
> wy1
) return; // this should not happen, but...
1197 // vertical setup; always go from top to bottom, so we'll draw the same line regardless of the starting point
1198 bool skipFirst
= false;
1201 if (!lastPoint
) skipFirst
= lastPoint
= true;
1202 mixin(swap
!("x0", "x1"));
1203 mixin(swap
!("y0", "y1"));
1205 if (y0
> wy1 || y1
< wy0
) return; // out of clip rectange
1206 int sty
= 1; // "step sign" for x axis; we still need the var, because there is a possible swap down there
1209 int stx
= void; // "step sign" for x axis
1211 // from left to right
1212 if (x0
> wx1 || x1
< wx0
) return; // out of clip rectange
1213 stx
= 1; // going right
1215 // from right to left
1216 if (x1
> wx1 || x0
< wx0
) return; // out of clip rectange
1217 stx
= -1; // going left
1222 mixin(swap
!("wx0", "wx1"));
1225 int dsx
= x1
-x0
; // "length" for x axis
1226 int dsy
= y1
-y0
; // "length" for y axis
1227 int xd
= void, yd
= void; // current coord
1228 bool xyswapped
= false; // if `true`, `xd` and `yd` are swapped
1231 mixin(swap
!("x0", "y0"));
1232 mixin(swap
!("x1", "y1"));
1233 mixin(swap
!("dsx", "dsy"));
1234 mixin(swap
!("wx0", "wy0"));
1235 mixin(swap
!("wx1", "wy1"));
1236 mixin(swap
!("stx", "sty"));
1240 int dx2
= 2*dsx
; // "double length" for x axis
1241 int dy2
= 2*dsy
; // "double length" for y axis
1242 int e
= 2*dsy
-dsx
; // "error" (as in bresenham algo)
1243 int term
= x1
; // termination point
1244 bool xfixed
= false; // will be set if we properly fixed x0 coord while fixing the y0
1245 // note that clipping can overflow for insane coords
1246 // if you are completely sure that it can't happen, you can use `int` instead of `long`
1249 immutable long temp
= cast(long)dx2
*(wy0
-y0
)-dsx
;
1250 xd
+= cast(int)(temp
/dy2
);
1251 if (xd
> wx1
) return; // x is moved out of clipping rect, nothing to do
1252 immutable int rem
= cast(int)(temp
%dy2
);
1253 if (xd
+(rem
> 0 ?
1 : 0) >= wx0
) {
1254 xfixed
= true; // startx is inside the clipping rect, no need to perform left clip
1257 if (rem
> 0) { ++xd
; e
+= dy2
; }
1260 if (!xfixed
&& x0
< wx0
) {
1262 immutable long temp
= cast(long)dy2
*(wx0
-x0
);
1263 yd
+= cast(int)(temp
/dx2
);
1264 immutable int rem
= cast(int)(temp
%dx2
);
1265 if (yd
> wy1 ||
(yd
== wy1
&& rem
>= dsx
)) return; // y is moved out of clipping rect, nothing to do
1268 if (rem
>= dsx
) { ++yd
; e
-= dx2
; }
1272 immutable long temp
= cast(long)dx2
*(wy1
-y0
)+dsx
;
1273 term
= x0
+cast(int)(temp
/dy2
);
1274 // it should be safe to decrement here
1275 if (cast(int)(temp
%dy2
) == 0) --term
;
1277 if (term
> wx1
) term
= wx1
; // clip at right
1279 if (sty
== -1) yd
= -yd
;
1280 if (stx
== -1) { xd
= -xd
; term
= -term
; }
1283 if (lastPoint
) term
+= stx
;
1285 if (term
== xd
) return;
1286 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
1290 // draw it; `putPixel()` can omit checks
1291 if (gxIsSolid(clr
)) {
1292 while (xd
!= term
) {
1294 *cast(uint*)((cast(ubyte*)vglTexBuf
)+(xyswapped ? xd
*VBufWidth
+yd
: yd
*VBufWidth
+xd
)) = clr
;
1295 // done drawing, move coords
1296 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
1300 while (xd
!= term
) {
1302 uint* dp
= cast(uint*)((cast(ubyte*)vglTexBuf
)+(xyswapped ? xd
*VBufWidth
+yd
: yd
*VBufWidth
+xd
));
1303 mixin(GxColMixMixin
!("*dp", "*dp", "clr"));
1304 // done drawing, move coords
1305 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
1311 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
); }
1312 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
); }
1313 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
); }