2 * Pixel Graphics Library
3 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
4 * Understanding is not required. Only obedience.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 3 of the License ONLY.
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 iv
.sdpy
.gfxbuf
/*is aliced*/;
21 import iv
.sdpy
.compat
;
25 import iv
.sdpy
.region
;
28 // ////////////////////////////////////////////////////////////////////////// //
29 static assert(VColor
.sizeof
== uint.sizeof
);
32 // ////////////////////////////////////////////////////////////////////////// //
35 // to avoid importing `std.math`
36 static int abs (int n
) pure nothrow @safe @nogc { static if (__VERSION__
> 2067) pragma(inline
, true); return (n
>= 0 ? n
: -n
); }
39 nothrow @trusted @nogc:
41 int w
, h
; // vscreen size
42 int rc
= -1; // refcount; <0: this is vlVScr
43 Region reg
; // here, to keep struct size small
47 // "real" cliprect, always inside buf
48 int mRClipX0
, mRClipY0
;
49 int mRClipX1
, mRClipY1
;
56 alias stringc
= const(char)[];
59 usize mVScrS
= 0; // this is actually `VScr*`
61 nothrow @trusted @nogc:
63 if (mVScrS
== 0) return;
64 auto vscr
= cast(VScr
*)mVScrS
;
65 if (vscr
.rc
> 0) ++vscr
.rc
; // !vlVScr buf
69 if (mVScrS
== 0) return;
70 auto vscr
= cast(VScr
*)mVScrS
;
71 if (vscr
.rc
< 0) return; // vlVScr buf
73 import core
.stdc
.stdlib
: free
;
74 if (vscr
.buf
!is null) free(vscr
.buf
);
77 if (vscr
.rc
< 0) assert(0);
82 void createVBuf (int wdt
, int hgt
) {
83 //import core.exception : onOutOfMemoryError;
84 import core
.stdc
.stdlib
: malloc
, realloc
, free
;
85 import core
.stdc
.string
: memcpy
;
88 if (wdt
> 32767 || hgt
> 32767) assert(0, "invalid GfxBuf dimensions");
89 auto vs
= cast(VScr
*)malloc(VScr
.sizeof
);
90 if (vs
is null) assert(0, "GfxBuf: out of memory");
91 static immutable VScr initr
= VScr
.init
;
92 memcpy(vs
, &initr
, VScr
.sizeof
);
94 vs
.buf
= cast(VColor
*)malloc((wdt
&& hgt ? wdt
*hgt
: 1)*VColor
.sizeof
);
95 if (vs
.buf
is null) { free(vs
); assert(0, "GfxBuf: out of memory"); }
96 mVScrS
= cast(usize
)vs
;
101 vs
.reg
.setSize(wdt
, hgt
);
104 @property inout(VScr
)* vscr () inout pure {
105 static if (__VERSION__
> 2067) pragma(inline
, true);
106 return cast(VScr
*)mVScrS
;
109 // to create GfxBuf for vlVScr
110 this (VScr
* vs
) { mVScrS
= cast(usize
)vs
; }
112 __gshared VScr vsbuf
;
114 static void fixVSBuf () {
115 vsbuf
.buf
= cast(VColor
*)vlVScr
;
116 if (vsbuf
.w
!= vlWidth || vsbuf
.h
!= vlHeight
) {
119 vsbuf
.rc
= -1; // special mark
120 vsbuf
.mClipX0
= vsbuf
.mClipY0
= vsbuf
.mRClipX0
= vsbuf
.mRClipY0
= 0;
121 vsbuf
.mXOfs
= vsbuf
.mYOfs
= 0;
122 vsbuf
.mClipX1
= vsbuf
.mRClipX1
= vlWidth
-1;
123 vsbuf
.mClipY1
= vsbuf
.mRClipY1
= vlHeight
-1;
124 vsbuf
.reg
.setSize(vlWidth
, vlHeight
);
128 package static void updateVScr () { fixVSBuf(); }
131 this (int wdt
, int hgt
) { createVBuf(wdt
, hgt
); }
132 ~this () { vscrDecRef(); }
133 this (this) { vscrIncRef(); }
135 void setSize (int wdt
, int hgt
) {
137 if (wdt
== vscr
.w
&& hgt
== vscr
.h
) return;
138 if (vscr
.rc
< 0) assert(0, "GfxBuf: double init");
141 createVBuf(wdt
, hgt
);
144 static GfxBuf
vlVScrBuf () {
146 return GfxBuf(&vsbuf
);
149 @property VColor
* vbuf () pure { static if (__VERSION__
> 2067) pragma(inline
, true); return vscr
.buf
; }
151 @property int width () pure { static if (__VERSION__
> 2067) pragma(inline
, true); return vscr
.w
; }
152 @property int height () pure { static if (__VERSION__
> 2067) pragma(inline
, true); return vscr
.h
; }
154 VColor
* scanline (usize idx
) pure { static if (__VERSION__
> 2067) pragma(inline
, true); return (idx
< height ? vbuf
+idx
*width
: null); }
157 * Draw (possibly semi-transparent) pixel onto virtual screen; mix colors.
167 @gcc_inline void putPixel() (int x
, int y
, VColor col
) {
168 static if (__VERSION__
> 2067) pragma(inline
, true);
169 if (!col
.isTransparent
&& vscr
.reg
.visible(x
, y
)) {
170 //TODO: overflow check
174 if (x
>= vs
.mRClipX0
&& y
>= vs
.mRClipY0
&& x
<= vs
.mRClipX1
&& y
<= vs
.mRClipY1
) {
175 uint* da = cast(uint*)vs
.buf
+y
*vs
.w
+x
;
179 mixin(VColor
.ColorBlendMixinStr
!("col.u32", "*da"));
186 * Draw pixel onto virtual screen; don't mix colors.
196 @gcc_inline void setPixel() (int x
, int y
, VColor col
) {
197 static if (__VERSION__
> 2067) pragma(inline
, true);
198 //TODO: overflow check
199 if (vscr
.reg
.visible(x
, y
)) {
203 if (x
>= vs
.mRClipX0
&& y
>= vs
.mRClipY0
&& x
<= vs
.mRClipX1
&& y
<= vs
.mRClipY1
) {
204 *(vs
.buf
+y
*vs
.w
+x
) = col
;
209 // //////////////////////////////////////////////////////////////////// //
211 bool isEmptyClip () const pure {
212 static if (__VERSION__
> 2067) pragma(inline
, true);
214 return (vs
.mRClipX0
> vs
.mRClipX1 || vs
.mRClipY0
> vs
.mRClipY1 || vs
.reg
.empty
);
217 void resetClipOfs () {
219 vs
.mXOfs
= vs
.mYOfs
= 0;
220 vs
.mClipX0
= vs
.mClipY0
= vs
.mRClipX0
= vs
.mRClipY0
= 0;
221 vs
.mClipX1
= vs
.mRClipX1
= vs
.w
-1;
222 vs
.mClipY1
= vs
.mRClipY1
= vs
.h
-1;
225 void resetOfs () { vscr
.mXOfs
= vscr
.mYOfs
= 0; }
229 vs
.mClipX0
= vs
.mClipY0
= vs
.mRClipX0
= vs
.mRClipY0
= 0;
230 vs
.mClipX1
= vs
.mRClipX1
= vs
.w
-1;
231 vs
.mClipY1
= vs
.mRClipY1
= vs
.h
-1;
234 @property int xofs () const pure { static if (__VERSION__
> 2067) pragma(inline
, true); return vscr
.mXOfs
; }
235 @property void xofs (int v
) { static if (__VERSION__
> 2067) pragma(inline
, true); vscr
.mXOfs
= v
; }
237 @property int yofs () const pure { static if (__VERSION__
> 2067) pragma(inline
, true); return vscr
.mYOfs
; }
238 @property void yofs (int v
) { static if (__VERSION__
> 2067) pragma(inline
, true); vscr
.mYOfs
= v
; }
240 static struct Clip
{ int x
, y
, w
, h
; }
242 @property Clip
clip () pure {
247 res
.w
= vs
.mClipX1
-vs
.mClipX0
+1;
248 res
.h
= vs
.mClipY1
-vs
.mClipY0
+1;
249 if (res
.w
< 0) res
.w
= 0;
250 if (res
.h
< 0) res
.h
= 0;
254 @property void clip() (in auto ref Clip c
) {
256 vs
.mClipX0
= vs
.mRClipX0
= c
.x
;
257 vs
.mClipY0
= vs
.mRClipY0
= c
.y
;
258 vs
.mClipX1
= vs
.mRClipX1
= c
.x
+c
.w
-1;
259 vs
.mClipY1
= vs
.mRClipY1
= c
.y
+c
.h
-1;
260 if (vs
.mRClipX0
< 0) vs
.mRClipX0
= 0;
261 if (vs
.mRClipX1
< 0) vs
.mRClipX1
= 0;
262 if (vs
.mRClipY0
< 0) vs
.mRClipY0
= 0;
263 if (vs
.mRClipY1
< 0) vs
.mRClipY1
= 0;
264 if (vs
.mRClipX0
>= vs
.w
) vs
.mRClipX0
= vs
.w
-1;
265 if (vs
.mRClipX1
>= vs
.w
) vs
.mRClipX1
= vs
.w
-1;
266 if (vs
.mRClipY0
>= vs
.h
) vs
.mRClipY0
= vs
.h
-1;
267 if (vs
.mRClipY1
>= vs
.h
) vs
.mRClipY1
= vs
.h
-1;
268 if (vs
.mRClipX1
< 0 || vs
.mRClipX0
>= vs
.w || vs
.mRClipY1
< 0 || vs
.mRClipY0
>= vs
.h
) {
269 vs
.mRClipX0
= vs
.mRClipY0
= 1;
270 vs
.mRClipX1
= vs
.mRClipY1
= 0;
274 // //////////////////////////////////////////////////////////////////// //
276 @property ref Region
region () { static if (__VERSION__
> 2067) pragma(inline
, true); return vscr
.reg
; }
278 // //////////////////////////////////////////////////////////////////////// //
281 * Draw character onto virtual screen in KOI8 encoding.
289 * col = foreground color
290 * bkcol = background color
295 void drawCharWdt (int x
, int y
, int wdt
, int shift
, char ch
, VColor col
, VColor bkcol
=VColor
.transparent
) {
297 if (wdt
< 1 || shift
>= 8) return;
298 if (col
.isTransparent
&& bkcol
.isTransparent
) return;
299 if (isEmptyClip
) return;
300 if (wdt
> 8) wdt
= 8;
301 if (shift
< 0) shift
= 0;
302 foreach (immutable int dy
; 0..8) {
303 ubyte b
= cast(ubyte)(vlFont6
[pos
++]<<shift
);
304 foreach (immutable int dx
; 0..wdt
) {
305 VColor c
= (b
&0x80 ? col
: bkcol
);
306 if (!c
.isTransparent
) putPixel(x
+dx
, y
+dy
, c
);
318 OutLU
= 0x10, // left-up
319 OutRU
= 0x20, // right-up
320 OutLD
= 0x40, // left-down
321 OutRD
= 0x80, // right-down
326 * Draw outlined character onto virtual screen in KOI8 encoding.
334 * col = foreground color
335 * outcol = outline color
336 * ot = outline type, OutXXX, ored
341 void drawCharWdtOut (int x
, int y
, int wdt
, int shift
, char ch
, VColor col
, VColor outcol
=VColor
.transparent
, ubyte ot
=0) {
342 if (col
.isTransparent
&& outcol
.isTransparent
) return;
343 if (ot
== 0 || outcol
.isTransparent
) {
344 // no outline? simple draw
345 drawCharWdt(x
, y
, wdt
, shift
, ch
, col
, VColor
.transparent
);
349 if (wdt
< 1 || shift
>= 8) return;
350 if (wdt
> 8) wdt
= 8;
351 if (shift
< 0) shift
= 0;
352 ubyte[8+2][8+2] bmp
= 0; // char bitmap; 0: empty; 1: char; 2: outline
353 foreach (immutable dy
; 1..9) {
354 ubyte b
= cast(ubyte)(vlFont6
[pos
++]<<shift
);
355 foreach (immutable dx
; 1..wdt
+1) {
360 if ((ot
&OutUp
) && bmp
[dy
-1][dx
] == 0) bmp
[dy
-1][dx
] = 2;
361 if ((ot
&OutDown
) && bmp
[dy
+1][dx
] == 0) bmp
[dy
+1][dx
] = 2;
362 if ((ot
&OutLeft
) && bmp
[dy
][dx
-1] == 0) bmp
[dy
][dx
-1] = 2;
363 if ((ot
&OutRight
) && bmp
[dy
][dx
+1] == 0) bmp
[dy
][dx
+1] = 2;
364 if ((ot
&OutLU
) && bmp
[dy
-1][dx
-1] == 0) bmp
[dy
-1][dx
-1] = 2;
365 if ((ot
&OutRU
) && bmp
[dy
-1][dx
+1] == 0) bmp
[dy
-1][dx
+1] = 2;
366 if ((ot
&OutLD
) && bmp
[dy
+1][dx
-1] == 0) bmp
[dy
+1][dx
-1] = 2;
367 if ((ot
&OutRD
) && bmp
[dy
+1][dx
+1] == 0) bmp
[dy
+1][dx
+1] = 2;
375 foreach (immutable int dy
; 0..10) {
376 foreach (immutable int dx
; 0..10) {
377 if (auto t
= bmp
[dy
][dx
]) putPixel(x
+dx
, y
+dy
, (t
== 1 ? col
: outcol
));
383 * Draw 6x8 character onto virtual screen in KOI8 encoding.
389 * col = foreground color
390 * bkcol = background color
395 void drawChar (int x
, int y
, char ch
, VColor col
, VColor bkcol
=VColor
.transparent
) {
396 drawCharWdt(x
, y
, 6, 0, ch
, col
, bkcol
);
399 void drawCharOut (int x
, int y
, char ch
, VColor col
, VColor outcol
=VColor
.transparent
, ubyte ot
=OutAll
) {
400 drawCharWdtOut(x
, y
, 6, 0, ch
, col
, outcol
, ot
);
403 void drawText (int x
, int y
, stringc
str, VColor col
, VColor bkcol
=VColor
.transparent
) {
404 if (col
.isTransparent
&& bkcol
.isTransparent
) return;
405 if (isEmptyClip
) return;
406 foreach (immutable char ch
; str) {
407 drawChar(x
, y
, ch
, col
, bkcol
);
412 void drawTextOut (int x
, int y
, stringc
str, VColor col
, VColor outcol
=VColor
.transparent
, ubyte ot
=OutAll
) {
413 if (isEmptyClip
) return;
414 foreach (immutable char ch
; str) {
415 drawCharOut(x
, y
, ch
, col
, outcol
, ot
);
420 static @property int fontHeight () pure { static if (__VERSION__
> 2067) pragma(inline
, true); return 8; }
421 static int charWidthProp (char ch
) pure { static if (__VERSION__
> 2067) pragma(inline
, true); return (vlFontPropWidth
[ch
]&0x0f); }
422 static int textWidth (stringc
str) pure { static if (__VERSION__
> 2067) pragma(inline
, true); return cast(int)str.length
*6; }
423 static int textWidthProp (stringc
str) {
425 foreach (immutable char ch
; str) wdt
+= (vlFontPropWidth
[ch
]&0x0f)+1;
426 if (wdt
> 0) --wdt
; // don't count last empty pixel
430 int drawCharProp (int x
, int y
, char ch
, VColor col
, VColor bkcol
=VColor
.transparent
) {
431 immutable int wdt
= (vlFontPropWidth
[ch
]&0x0f);
432 drawCharWdt(x
, y
, wdt
, vlFontPropWidth
[ch
]>>4, ch
, col
, bkcol
);
436 int drawCharPropOut (int x
, int y
, char ch
, VColor col
, VColor outcol
=VColor
.transparent
, ubyte ot
=OutAll
) {
437 immutable int wdt
= (vlFontPropWidth
[ch
]&0x0f);
438 drawCharWdtOut(x
, y
, wdt
, vlFontPropWidth
[ch
]>>4, ch
, col
, outcol
, ot
);
442 int drawTextProp (int x
, int y
, stringc
str, VColor col
, VColor bkcol
=VColor
.transparent
) {
445 foreach (immutable char ch
; str) {
447 if (!bkcol
.isTransparent
) foreach (int dy
; 0..8) putPixel(x
, y
+dy
, bkcol
);
451 x
+= drawCharProp(x
, y
, ch
, col
, bkcol
);
456 int drawTextPropOut (int x
, int y
, stringc
str, VColor col
, VColor outcol
=VColor
.transparent
, ubyte ot
=OutAll
) {
458 foreach (immutable char ch
; str) x
+= drawCharPropOut(x
, y
, ch
, col
, outcol
, ot
)+1;
459 if (x
> sx
) --x
; // don't count last empty pixel
463 // ////////////////////////////////////////////////////////////////////////// //
464 void clear (VColor col
) {
466 if (vs
.w
&& vs
.h
&& !vs
.reg
.empty
) {
467 col
.u32
&= ~VColor
.AMask
;
469 vs
.buf
[0..vs
.w
*vs
.h
] = col
;
472 foreach (immutable y
; 0..vs
.h
) {
473 vs
.reg
.spans
!true(y
, 0, vs
.w
-1, (int sx
, int ex
) @trusted {
474 //{ import iv.writer; writeln("y=", y, "; sx=", sx, "; ex=", ex); }
483 void hline (int x0
, int y0
, int len
, VColor col
) {
484 if (len
< 1 || col
.isTransparent || isEmptyClip
) return;
485 if (len
== 1) { putPixel(x0
, y0
, col
); return; }
490 if (y0
< vs
.mRClipY0 || y0
> vs
.mRClipY1 || ex
< vs
.mRClipX0 || x0
> vs
.mRClipX1
) return;
491 if (x0
< vs
.mRClipX0
) x0
= vs
.mRClipX0
;
492 if (x0
> vs
.mRClipX1
) x0
= vs
.mRClipX1
;
493 if (ex
< vs
.mRClipX0
) ex
= vs
.mRClipX0
;
494 if (ex
> vs
.mRClipX1
) ex
= vs
.mRClipX1
;
497 vs
.reg
.spans
!true(y0
-vs
.mYOfs
, vs
.mXOfs
, x0
, ex
, (int sx
, int ex
) @trusted {
499 vs
.buf
[adr
+sx
..adr
+ex
+1] = col
;
501 uint* da = cast(uint*)vs
.buf
+adr
+sx
;
503 mixin(VColor
.ColorBlendMixinStr
!("col.u32", "*da"));
510 void vline (int x0
, int y0
, int len
, VColor col
) {
511 if (len
< 1 || col
.isTransparent || isEmptyClip
) return;
512 while (len
-- > 0) putPixel(x0
, y0
++, col
);
516 // the idea is that we can simply skip the right number of steps
517 // if the point is off the drawing area
518 void line (int x0
, int y0
, int x1
, int y1
, immutable VColor col
, bool lastPoint
=true, in bool alwaysFromTop
=true) {
519 enum swap(string a
, string b
) = "{int tmp_="~a
~";"~a
~"="~b
~";"~b
~"=tmp_;}";
521 if (col
.isTransparent || isEmptyClip
) return;
523 if (x0
== x1
&& y0
== y1
) {
524 if (lastPoint
) putPixel(x0
, y0
, col
);
531 hline(x1
+(lastPoint ?
0 : 1), y0
, x0
-x1
+(lastPoint ?
1 : 0), col
);
533 hline(x0
, y0
, x1
-x0
+(lastPoint ?
1 : 0), col
);
545 int wx0
= vs
.mRClipX0
, wy0
= vs
.mRClipY0
, wx1
= vs
.mRClipX1
, wy1
= vs
.mRClipY1
;
546 if (wx0
> wx1 || wy0
> wy1
) return; // this should not happen, but...
548 // vertical setup; always go from top to bottom, so we'll draw the same line regardless of the starting point
549 bool skipFirst
= false;
553 if (!lastPoint
) skipFirst
= lastPoint
= true;
554 mixin(swap
!("x0", "x1"));
555 mixin(swap
!("y0", "y1"));
557 if (y0
> wy1 || y1
< wy0
) return; // out of clip rectange
560 if (y0
> wy1 || y1
< wy0
) return; // out of clip rectange
562 if (y1
> wy1 || y0
< wy0
) return; // out of clip rectange
565 int sty
= 1; // "step sign" for x axis; we still need the var, because there is a possible swap down there
568 int stx
= void; // "step sign" for x axis
570 // from left to right
571 if (x0
> wx1 || x1
< wx0
) return; // out of clip rectange
572 stx
= 1; // going right
574 // from right to left
575 if (x1
> wx1 || x0
< wx0
) return; // out of clip rectange
576 stx
= -1; // going left
581 mixin(swap
!("wx0", "wx1"));
585 if (!alwaysFromTop
&& y0
> y1
) {
586 // from bottom to top
587 sty
= -1; // going up
592 mixin(swap
!("wy0", "wy1"));
595 int dsx
= x1
-x0
; // "length" for x axis
596 int dsy
= y1
-y0
; // "length" for y axis
597 int xd
= void, yd
= void; // current coord
598 bool xyswapped
= false; // if `true`, `xd` and `yd` are swapped
601 mixin(swap
!("x0", "y0"));
602 mixin(swap
!("x1", "y1"));
603 mixin(swap
!("dsx", "dsy"));
604 mixin(swap
!("wx0", "wy0"));
605 mixin(swap
!("wx1", "wy1"));
606 mixin(swap
!("stx", "sty"));
610 int dx2
= 2*dsx
; // "double length" for x axis
611 int dy2
= 2*dsy
; // "double length" for y axis
612 int e
= 2*dsy
-dsx
; // "error" (as in bresenham algo)
613 int term
= x1
; // termination point
614 bool xfixed
= false; // will be set if we properly fixed x0 coord while fixing the y0
616 // note that clipping can overflow for insane coords
617 // if you are completely sure that it can't happen, you can use `int` instead of `long`
620 immutable long temp
= cast(long)dx2
*(wy0
-y0
)-dsx
;
621 xd
+= cast(int)(temp
/dy2
);
622 if (xd
> wx1
) return; // x is moved out of clipping rect, nothing to do
623 immutable int rem
= cast(int)(temp
%dy2
);
624 if (xd
+(rem
> 0 ?
1 : 0) >= wx0
) {
625 xfixed
= true; // startx is inside the clipping rect, no need to perform left clip
628 if (rem
> 0) { ++xd
; e
+= dy2
; }
631 if (!xfixed
&& x0
< wx0
) {
633 immutable long temp
= cast(long)dy2
*(wx0
-x0
);
634 yd
+= cast(int)(temp
/dx2
);
635 immutable int rem
= cast(int)(temp
%dx2
);
636 if (yd
> wy1 ||
(yd
== wy1
&& rem
>= dsx
)) return; // y is moved out of clipping rect, nothing to do
639 if (rem
>= dsx
) { ++yd
; e
-= dx2
; }
643 immutable long temp
= cast(long)dx2
*(wy1
-y0
)+dsx
;
644 term
= x0
+cast(int)(temp
/dy2
);
645 // it should be safe to decrement here
646 if (cast(int)(temp
%dy2
) == 0) --term
;
648 if (term
> wx1
) term
= wx1
; // clip at right
650 if (sty
== -1) yd
= -yd
;
651 if (stx
== -1) { xd
= -xd
; term
= -term
; }
654 if (lastPoint
) term
+= stx
;
656 if (term
== xd
) return;
657 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
661 // draw it; `putPixel()` can omit checks
664 // inlined `putPixel(*d0, *d1, col)`
666 if (vs
.reg
.visible(yd
-vs
.mXOfs
, xd
-vs
.mYOfs
)) {
667 *(cast(uint*)vs
.buf
+xd
*vs
.w
+yd
) = col
.u32
;
670 if (vs
.reg
.visible(xd
-vs
.mXOfs
, yd
-vs
.mYOfs
)) {
671 *(cast(uint*)vs
.buf
+yd
*vs
.w
+xd
) = col
.u32
;
674 // done drawing, move coords
675 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
680 // inlined `putPixel(*d0, *d1, col)`
682 if (vs
.reg
.visible(yd
-vs
.mXOfs
, xd
-vs
.mYOfs
)) {
683 uint* da = cast(uint*)vs
.buf
+xd
*vs
.w
+yd
;
684 mixin(VColor
.ColorBlendMixinStr
!("col.u32", "*da"));
687 if (vs
.reg
.visible(xd
-vs
.mXOfs
, yd
-vs
.mYOfs
)) {
688 uint* da = cast(uint*)vs
.buf
+yd
*vs
.w
+xd
;
689 mixin(VColor
.ColorBlendMixinStr
!("col.u32", "*da"));
692 // done drawing, move coords
693 if (e
>= 0) { yd
+= sty
; e
-= dx2
; } else { e
+= dy2
; }
699 void fillRect (int x
, int y
, int w
, int h
, VColor col
) {
700 if (col
.isTransparent || isEmptyClip || w
< 1 || h
< 1) return;
706 if (x
> vs
.mRClipX1 || y
> vs
.mRClipY1 || ex
< vs
.mRClipX0 || ey
< vs
.mRClipY0
) return;
707 if (y
< vs
.mRClipY0
) y
= vs
.mRClipY0
;
708 if (ey
> vs
.mRClipY1
) ey
= vs
.mRClipY1
;
711 foreach (int dy
; y
-vs
.mYOfs
..ey
-vs
.mYOfs
+1) hline(x
, dy
, w
, col
);
714 void rect (int x
, int y
, int w
, int h
, VColor col
) {
715 if (w
> 0 && h
> 0) {
722 hline(x
, y
+h
-1, w
, col
);
726 vline(x
+w
-1, y
, h
, col
);
732 void selectionRect (int phase
, int x0
, int y0
, int wdt
, int hgt
, VColor col0
, VColor col1
=VColor
.transparent
) {
733 if (wdt
> 0 && hgt
> 0) {
735 if (wdt
> 1) foreach (immutable f
; x0
..x0
+wdt
) { putPixel(f
, y0
, ((phase
%= 4) < 2 ? col0
: col1
)); ++phase
; }
736 if (hgt
== 1) return;
738 foreach (immutable f
; y0
+1..y0
+hgt
) { putPixel(x0
+wdt
-1, f
, ((phase
%= 4) < 2 ? col0
: col1
)); ++phase
; }
739 if (wdt
== 1) return;
741 foreach_reverse (immutable f
; x0
..x0
+wdt
-1) { putPixel(f
, y0
+hgt
-1, ((phase
%= 4) < 2 ? col0
: col1
)); ++phase
; }
743 foreach_reverse (immutable f
; y0
..y0
+hgt
-1) { putPixel(x0
, f
, ((phase
%= 4) < 2 ? col0
: col1
)); ++phase
; }
747 private void plot4points() (int cx
, int cy
, int x
, int y
, VColor col
) {
748 //static if (__VERSION__ > 2067) pragma(inline, true); // alas, dmd inliner sux again
749 putPixel(cx
+x
, cy
+y
, col
);
750 if (x
!= 0) putPixel(cx
-x
, cy
+y
, col
);
751 if (y
!= 0) putPixel(cx
+x
, cy
-y
, col
);
752 putPixel(cx
-x
, cy
-y
, col
);
755 void circle (int cx
, int cy
, int radius
, VColor col
) {
756 if (radius
> 0 && !col
.isTransparent
&& !isEmptyClip
) {
757 int error
= -radius
, x
= radius
, y
= 0;
758 if (radius
== 1) { putPixel(cx
, cy
, col
); return; }
760 plot4points(cx
, cy
, x
, y
, col
);
761 plot4points(cx
, cy
, y
, x
, col
);
764 if (error
>= 0) { --x
; error
-= x
*2; }
766 plot4points(cx
, cy
, x
, y
, col
);
770 void fillCircle (int cx
, int cy
, int radius
, VColor col
) {
771 if (radius
> 0 && !col
.isTransparent
&& !isEmptyClip
) {
772 int error
= -radius
, x
= radius
, y
= 0;
773 if (radius
== 1) { putPixel(cx
, cy
, col
); return; }
779 hline(cx
-x
, cy
+last_y
, 2*x
+1, col
);
780 if (x
!= 0 && last_y
!= 0) hline(cx
-x
, cy
-last_y
, 2*x
+1, col
);
783 hline(cx
-last_y
, cy
+x
, 2*last_y
+1, col
);
784 if (last_y
!= 0 && x
!= 0) hline(cx
-last_y
, cy
-x
, 2*last_y
+1, col
);
794 void ellipse (int x0
, int y0
, int x1
, int y1
, VColor col
) {
795 if (col
.isTransparent || isEmptyClip
) return;
796 int a
= abs(x1
-x0
), b
= abs(y1
-y0
), b1
= b
&1; // values of diameter
797 long dx
= 4*(1-a
)*b
*b
, dy
= 4*(b1
+1)*a
*a
; // error increment
798 long err
= dx
+dy
+b1
*a
*a
; // error of 1.step
799 if (x0
> x1
) { x0
= x1
; x1
+= a
; } // if called with swapped points...
800 if (y0
> y1
) y0
= y1
; // ...exchange them
801 y0
+= (b
+1)/2; y1
= y0
-b1
; // starting pixel
802 a
*= 8*a
; b1
= 8*b
*b
;
805 putPixel(x1
, y0
, col
); // I. Quadrant
806 putPixel(x0
, y0
, col
); // II. Quadrant
807 putPixel(x0
, y1
, col
); // III. Quadrant
808 putPixel(x1
, y1
, col
); // IV. Quadrant
810 if (e2
>= dx
) { ++x0
; --x1
; err
+= dx
+= b1
; } // x step
811 if (e2
<= dy
) { ++y0
; --y1
; err
+= dy
+= a
; } // y step
814 // too early stop of flat ellipses a=1
815 putPixel(x0
-1, ++y0
, col
); // complete tip of ellipse
816 putPixel(x0
-1, --y1
, col
);
820 void fillEllipse (int x0
, int y0
, int x1
, int y1
, VColor col
) {
821 if (col
.isTransparent || isEmptyClip
) return;
822 int a
= abs(x1
-x0
), b
= abs(y1
-y0
), b1
= b
&1; // values of diameter
823 long dx
= 4*(1-a
)*b
*b
, dy
= 4*(b1
+1)*a
*a
; // error increment
824 long err
= dx
+dy
+b1
*a
*a
; // error of 1.step
825 int prev_y0
= -1, prev_y1
= -1;
826 if (x0
> x1
) { x0
= x1
; x1
+= a
; } // if called with swapped points...
827 if (y0
> y1
) y0
= y1
; // ...exchange them
828 y0
+= (b
+1)/2; y1
= y0
-b1
; // starting pixel
829 a
*= 8*a
; b1
= 8*b
*b
;
832 if (y0
!= prev_y0
) { hline(x0
, y0
, x1
-x0
+1, col
); prev_y0
= y0
; }
833 if (y1
!= y0
&& y1
!= prev_y1
) { hline(x0
, y1
, x1
-x0
+1, col
); prev_y1
= y1
; }
835 if (e2
>= dx
) { ++x0
; --x1
; err
+= dx
+= b1
; } // x step
836 if (e2
<= dy
) { ++y0
; --y1
; err
+= dy
+= a
; } // y step
839 // too early stop of flat ellipses a=1
840 putPixel(x0
-1, ++y0
, col
); // complete tip of ellipse
841 putPixel(x0
-1, --y1
, col
);
845 // blit overlay to buffer, possibly with alpha
846 // destbuf should not overlap with vscr.buf
847 // `reg` starting at `(sofsx, sofsy)`
848 void blitRectTo(string btype
="NoSrcAlpha") (
849 VColor
* destbuf
, int destw
, int desth
,
850 int sofsx
, int sofsy
, int sw
, int sh
,
853 in auto ref Region reg
)
855 static assert(btype
== "NoSrcAlpha" || btype
== "SrcAlpha");
857 if (destbuf
is null || destw
< 1 || desth
< 1 || reg
.empty || alpha
== 255 ||
858 sw
< 1 || sh
< 1 || vs
.w
< 1 || vs
.h
< 1 ||
859 sofsx
>= vs
.w || sofsy
>= vs
.h || sofsx
+sw
<= 0 || sofsy
+sh
<= 0 ||
860 xd
>= destw || yd
>= desth
)
864 int sx
= sofsx
, ex
= sx
+sw
-1;
865 int sy
= sofsy
, ey
= sy
+sh
-1;
866 // sanitize source rect
867 if (sx
< 0) { xd
+= -sx
; sx
= 0; }
868 if (sy
< 0) { yd
+= -sy
; sy
= 0; }
869 if (ex
>= vs
.w
) ex
= vs
.w
-1;
870 if (ey
>= vs
.h
) ey
= vs
.h
-1;
871 if (sx
> ex || sy
> ey
) return; // completely clipped out
872 // clip source rect against dest rect
874 if ((sx
+= -xd
) > ex
) return;
877 if (xd
+(ex
-sx
+1) > destw
) {
878 if ((ex
= sx
+destw
-xd
-1) < sx
) return;
881 if ((sy
+= -yd
) > ey
) return;
884 if (yd
+(ey
-sy
+1) > desth
) {
885 if ((ey
= sy
+desth
-yd
-1) < sy
) return;
887 if (sx
> ex || sy
> ey
) return;
888 assert(sx
>= 0 && ex
< vs
.w
&& sx
<= ex
);
889 assert(sy
>= 0 && ey
< vs
.h
&& sy
<= ey
);
890 // now we can put spans
891 uint* sba
= cast(uint*)vs
.buf
+sy
*vs
.w
;
892 uint* dba
= cast(uint*)destbuf
+yd
*destw
+xd
;
893 static if (btype
== "NoSrcAlpha") {
897 reg
.spans
!true(sy
-sofsy
, sofsx
, sx
, ex
, (int x0
, int x1
) @trusted {
898 import core
.stdc
.string
: memcpy
;
899 memcpy(dba
+x0
-sx
, sba
+x0
, (x1
-x0
+1)*VColor
.sizeof
);
910 static if (btype
== "NoSrcAlpha") immutable uint a
= (alpha
<<VColor
.AShift
);
912 vs
.reg
.spans
!true(sy
-sofsy
, sofsx
, sx
, ex
, (int x0
, int x1
) @trusted {
914 uint* dst
= dba
+x0
-sx
;
917 static if (btype
== "SrcAlpha") {
918 s
= s
&~VColor
.AMask|
(clampToByte(alpha
+((s
>>VColor
.AShift
)&0xff)));
920 s
= s
&~VColor
.AMask|a
;
922 mixin(VColor
.ColorBlendMixinStr
!("s", "*dst"));
933 void blitTo(string btype
="NoSrcAlpha") (ref GfxBuf dest
, int xd
, int yd
, ubyte alpha
, in auto ref Region reg
) {
934 blitRectTo
!btype(dest
.vscr
.buf
, dest
.width
, dest
.height
, 0, 0, vscr
.w
, vscr
.h
, xd
, yd
, alpha
, reg
);
936 void blitTo(string btype
="NoSrcAlpha") (ref GfxBuf dest
, int xd
, int yd
, ubyte alpha
=0) { blitTo
!btype(dest
, xd
, yd
, alpha
, vscr
.reg
); }
938 void blitToVScr(string btype
="NoSrcAlpha") (int xd
, int yd
, ubyte alpha
, in auto ref Region reg
) {
939 blitRectTo
!btype(cast(VColor
*)vlVScr
, vlWidth
, vlHeight
, 0, 0, vscr
.w
, vscr
.h
, xd
, yd
, alpha
, reg
);
941 void blitToVScr(string btype
="NoSrcAlpha") (int xd
, int yd
, ubyte alpha
=0) { blitToVScr
!btype(xd
, yd
, alpha
, vscr
.reg
); }