egra: YesNoWindow cosmetix
[iv.d.git] / sdpy / gfxbuf.d
blob1349917f3429a5b842ffa8f92f33d2ff98c2f1e1
1 /*
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*/;
20 import iv.alice;
21 import iv.sdpy.compat;
22 import iv.sdpy.core;
23 import iv.sdpy.font6;
24 import iv.sdpy.color;
25 import iv.sdpy.region;
28 // ////////////////////////////////////////////////////////////////////////// //
29 static assert(VColor.sizeof == uint.sizeof);
32 // ////////////////////////////////////////////////////////////////////////// //
33 struct GfxBuf {
34 private:
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); }
38 static struct VScr {
39 nothrow @trusted @nogc:
40 VColor* buf;
41 int w, h; // vscreen size
42 int rc = -1; // refcount; <0: this is vlVScr
43 Region reg; // here, to keep struct size small
44 int mClipX0, mClipY0;
45 int mClipX1, mClipY1;
46 int mXOfs, mYOfs;
47 // "real" cliprect, always inside buf
48 int mRClipX0, mRClipY0;
49 int mRClipX1, mRClipY1;
51 //@disable this ();
52 @disable this (this);
55 public:
56 alias stringc = const(char)[];
58 private:
59 usize mVScrS = 0; // this is actually `VScr*`
61 nothrow @trusted @nogc:
62 void vscrIncRef () {
63 if (mVScrS == 0) return;
64 auto vscr = cast(VScr*)mVScrS;
65 if (vscr.rc > 0) ++vscr.rc; // !vlVScr buf
68 void vscrDecRef () {
69 if (mVScrS == 0) return;
70 auto vscr = cast(VScr*)mVScrS;
71 if (vscr.rc < 0) return; // vlVScr buf
72 if (--vscr.rc == 0) {
73 import core.stdc.stdlib : free;
74 if (vscr.buf !is null) free(vscr.buf);
75 vscr.buf = null;
76 } else {
77 if (vscr.rc < 0) assert(0);
79 mVScrS = 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;
86 if (wdt < 0) wdt = 0;
87 if (hgt < 0) hgt = 0;
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);
93 //(*vs).__ctor();
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;
97 vs.w = wdt;
98 vs.h = hgt;
99 vs.rc = 1;
100 resetClipOfs();
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) {
117 vsbuf.w = vlWidth;
118 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(); }
130 public:
131 this (int wdt, int hgt) { createVBuf(wdt, hgt); }
132 ~this () { vscrDecRef(); }
133 this (this) { vscrIncRef(); }
135 void setSize (int wdt, int hgt) {
136 if (mVScrS != 0) {
137 if (wdt == vscr.w && hgt == vscr.h) return;
138 if (vscr.rc < 0) assert(0, "GfxBuf: double init");
139 vscrDecRef();
141 createVBuf(wdt, hgt);
144 static GfxBuf vlVScrBuf () {
145 fixVSBuf();
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.
159 * Params:
160 * x = x coordinate
161 * y = y coordinate
162 * col = rgba color
164 * Returns:
165 * nothing
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
171 auto vs = vscr;
172 x += vs.mXOfs;
173 y += vs.mYOfs;
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;
176 if (col.isOpaque) {
177 *da = col.u32;
178 } else {
179 mixin(VColor.ColorBlendMixinStr!("col.u32", "*da"));
186 * Draw pixel onto virtual screen; don't mix colors.
188 * Params:
189 * x = x coordinate
190 * y = y coordinate
191 * col = rgb color
193 * Returns:
194 * nothing
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)) {
200 auto vs = vscr;
201 x += vs.mXOfs;
202 y += vs.mYOfs;
203 if (x >= vs.mRClipX0 && y >= vs.mRClipY0 && x <= vs.mRClipX1 && y <= vs.mRClipY1) {
204 *(vs.buf+y*vs.w+x) = col;
209 // //////////////////////////////////////////////////////////////////// //
210 // offsets and clips
211 bool isEmptyClip () const pure {
212 static if (__VERSION__ > 2067) pragma(inline, true);
213 auto vs = vscr;
214 return (vs.mRClipX0 > vs.mRClipX1 || vs.mRClipY0 > vs.mRClipY1 || vs.reg.empty);
217 void resetClipOfs () {
218 auto vs = vscr;
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; }
227 void resetClip () {
228 auto vs = vscr;
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 {
243 Clip res = void;
244 auto vs = vscr;
245 res.x = vs.mClipX0;
246 res.y = vs.mClipY0;
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;
251 return res;
254 @property void clip() (in auto ref Clip c) {
255 auto vs = vscr;
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 // //////////////////////////////////////////////////////////////////// //
275 // region
276 @property ref Region region () { static if (__VERSION__ > 2067) pragma(inline, true); return vscr.reg; }
278 // //////////////////////////////////////////////////////////////////////// //
279 // various drawing
281 * Draw character onto virtual screen in KOI8 encoding.
283 * Params:
284 * x = x coordinate
285 * y = y coordinate
286 * wdt = char width
287 * shift = shl count
288 * ch = character
289 * col = foreground color
290 * bkcol = background color
292 * Returns:
293 * nothing
295 void drawCharWdt (int x, int y, int wdt, int shift, char ch, VColor col, VColor bkcol=VColor.transparent) {
296 usize pos = ch*8;
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);
307 b = (b<<1)&0xff;
312 // outline types
313 enum : ubyte {
314 OutLeft = 0x01,
315 OutRight = 0x02,
316 OutUp = 0x04,
317 OutDown = 0x08,
318 OutLU = 0x10, // left-up
319 OutRU = 0x20, // right-up
320 OutLD = 0x40, // left-down
321 OutRD = 0x80, // right-down
322 OutAll = 0xff,
326 * Draw outlined character onto virtual screen in KOI8 encoding.
328 * Params:
329 * x = x coordinate
330 * y = y coordinate
331 * wdt = char width
332 * shift = shl count
333 * ch = character
334 * col = foreground color
335 * outcol = outline color
336 * ot = outline type, OutXXX, ored
338 * Returns:
339 * nothing
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);
346 return;
348 usize pos = ch*8;
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) {
356 if (b&0x80) {
357 // put pixel
358 bmp[dy][dx] = 1;
359 // put outlines
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;
369 b = (b<<1)&0xff;
372 // now draw it
373 --x;
374 --y;
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.
385 * Params:
386 * x = x coordinate
387 * y = y coordinate
388 * ch = character
389 * col = foreground color
390 * bkcol = background color
392 * Returns:
393 * nothing
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);
408 x += 6;
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);
416 x += 6;
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) {
424 int wdt = 0;
425 foreach (immutable char ch; str) wdt += (vlFontPropWidth[ch]&0x0f)+1;
426 if (wdt > 0) --wdt; // don't count last empty pixel
427 return wdt;
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);
433 return wdt;
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);
439 return wdt;
442 int drawTextProp (int x, int y, stringc str, VColor col, VColor bkcol=VColor.transparent) {
443 bool vline = false;
444 int sx = x;
445 foreach (immutable char ch; str) {
446 if (vline) {
447 if (!bkcol.isTransparent) foreach (int dy; 0..8) putPixel(x, y+dy, bkcol);
448 ++x;
450 vline = true;
451 x += drawCharProp(x, y, ch, col, bkcol);
453 return x-sx;
456 int drawTextPropOut (int x, int y, stringc str, VColor col, VColor outcol=VColor.transparent, ubyte ot=OutAll) {
457 int sx = x;
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
460 return x-sx;
463 // ////////////////////////////////////////////////////////////////////////// //
464 void clear (VColor col) {
465 auto vs = vscr;
466 if (vs.w && vs.h && !vs.reg.empty) {
467 col.u32 &= ~VColor.AMask;
468 if (vs.reg.solid) {
469 vs.buf[0..vs.w*vs.h] = col;
470 } else {
471 VColor* da = vs.buf;
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); }
475 da[sx..ex+1] = col;
477 da += vs.w;
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; }
486 auto vs = vscr;
487 x0 += vs.mXOfs;
488 y0 += vs.mYOfs;
489 int ex = x0+len-1;
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;
495 if (x0 > ex) return;
496 uint adr = y0*vs.w;
497 vs.reg.spans!true(y0-vs.mYOfs, vs.mXOfs, x0, ex, (int sx, int ex) @trusted {
498 if (col.isOpaque) {
499 vs.buf[adr+sx..adr+ex+1] = col;
500 } else {
501 uint* da = cast(uint*)vs.buf+adr+sx;
502 while (sx++ <= ex) {
503 mixin(VColor.ColorBlendMixinStr!("col.u32", "*da"));
504 ++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);
525 return;
528 // horizontal line?
529 if (y0 == y1) {
530 if (x0 > x1) {
531 hline(x1+(lastPoint ? 0 : 1), y0, x0-x1+(lastPoint ? 1 : 0), col);
532 } else {
533 hline(x0, y0, x1-x0+(lastPoint ? 1 : 0), col);
535 return;
538 auto vs = vscr;
539 x0 += vs.mXOfs;
540 x1 += vs.mXOfs;
541 y0 += vs.mYOfs;
542 y1 += vs.mYOfs;
544 // clip rectange
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;
550 if (alwaysFromTop) {
551 if (y0 > y1) {
552 // swap endpoints
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
558 } else {
559 if (y0 < y1) {
560 if (y0 > wy1 || y1 < wy0) return; // out of clip rectange
561 } else {
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
567 // horizontal setup
568 int stx = void; // "step sign" for x axis
569 if (x0 < x1) {
570 // from left to right
571 if (x0 > wx1 || x1 < wx0) return; // out of clip rectange
572 stx = 1; // going right
573 } else {
574 // from right to left
575 if (x1 > wx1 || x0 < wx0) return; // out of clip rectange
576 stx = -1; // going left
577 x0 = -x0;
578 x1 = -x1;
579 wx0 = -wx0;
580 wx1 = -wx1;
581 mixin(swap!("wx0", "wx1"));
584 // vertical setup
585 if (!alwaysFromTop && y0 > y1) {
586 // from bottom to top
587 sty = -1; // going up
588 y0 = -y0;
589 y1 = -y1;
590 wy0 = -wy0;
591 wy1 = -wy1;
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
599 if (dsx < dsy) {
600 xyswapped = true;
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"));
608 xd = x0;
609 yd = y0;
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`
618 if (y0 < wy0) {
619 // clip at top
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
626 yd = wy0;
627 e -= rem+dsx;
628 if (rem > 0) { ++xd; e += dy2; }
631 if (!xfixed && x0 < wx0) {
632 // clip at left
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
637 xd = wx0;
638 e += rem;
639 if (rem >= dsx) { ++yd; e -= dx2; }
641 if (y1 > wy1) {
642 // clip at bottom
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; }
652 dx2 -= dy2;
654 if (lastPoint) term += stx;
655 if (skipFirst) {
656 if (term == xd) return;
657 if (e >= 0) { yd += sty; e -= dx2; } else { e += dy2; }
658 xd += stx;
661 // draw it; `putPixel()` can omit checks
662 if (col.isOpaque) {
663 while (xd != term) {
664 // inlined `putPixel(*d0, *d1, col)`
665 if (xyswapped) {
666 if (vs.reg.visible(yd-vs.mXOfs, xd-vs.mYOfs)) {
667 *(cast(uint*)vs.buf+xd*vs.w+yd) = col.u32;
669 } else {
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; }
676 xd += stx;
678 } else {
679 while (xd != term) {
680 // inlined `putPixel(*d0, *d1, col)`
681 if (xyswapped) {
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"));
686 } else {
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; }
694 xd += stx;
699 void fillRect (int x, int y, int w, int h, VColor col) {
700 if (col.isTransparent || isEmptyClip || w < 1 || h < 1) return;
701 auto vs = vscr;
702 x += vs.mXOfs;
703 y += vs.mYOfs;
704 int ex = x+w-1;
705 int ey = y+h-1;
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;
709 w = ex-x+1;
710 x -= vs.mXOfs;
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) {
716 if (w == 1) {
717 vline(x, y, h, col);
718 } else if (h == 1) {
719 hline(x, y, w, col);
720 } else {
721 hline(x, y, w, col);
722 hline(x, y+h-1, w, col);
723 h -= 2;
724 y += 1;
725 vline(x, y, h, col);
726 vline(x+w-1, y, h, col);
731 /* 4 phases */
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) {
734 // top
735 if (wdt > 1) foreach (immutable f; x0..x0+wdt) { putPixel(f, y0, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
736 if (hgt == 1) return;
737 // right
738 foreach (immutable f; y0+1..y0+hgt) { putPixel(x0+wdt-1, f, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
739 if (wdt == 1) return;
740 // bottom
741 foreach_reverse (immutable f; x0..x0+wdt-1) { putPixel(f, y0+hgt-1, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
742 // left
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; }
759 while (x > y) {
760 plot4points(cx, cy, x, y, col);
761 plot4points(cx, cy, y, x, col);
762 error += y*2+1;
763 ++y;
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; }
774 while (x >= y) {
775 int last_y = y;
776 error += y;
777 ++y;
778 error += y;
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);
781 if (error >= 0) {
782 if (x != last_y) {
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);
786 error -= x;
787 --x;
788 error -= x;
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;
803 do {
804 long e2;
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
809 e2 = 2*err;
810 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
811 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
812 } while (x0 <= x1);
813 while (y0-y1 < b) {
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;
830 do {
831 long e2;
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; }
834 e2 = 2*err;
835 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
836 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
837 } while (x0 <= x1);
838 while (y0-y1 < b) {
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,
851 int xd, int yd,
852 ubyte alpha,
853 in auto ref Region reg)
855 static assert(btype == "NoSrcAlpha" || btype == "SrcAlpha");
856 auto vs = vscr;
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)
862 return;
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
873 if (xd < 0) {
874 if ((sx += -xd) > ex) return;
875 xd = 0;
877 if (xd+(ex-sx+1) > destw) {
878 if ((ex = sx+destw-xd-1) < sx) return;
880 if (yd < 0) {
881 if ((sy += -yd) > ey) return;
882 yd = 0;
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") {
894 if (alpha == 0) {
895 // copying
896 while (sy <= ey) {
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);
901 sba += vs.w;
902 dba += destw;
903 ++sy;
905 return;
908 // alpha mixing
910 static if (btype == "NoSrcAlpha") immutable uint a = (alpha<<VColor.AShift);
911 while (sy <= ey) {
912 vs.reg.spans!true(sy-sofsy, sofsx, sx, ex, (int x0, int x1) @trusted {
913 uint* src = sba+x0;
914 uint* dst = dba+x0-sx;
915 while (x0++ <= x1) {
916 uint s = *src++;
917 static if (btype == "SrcAlpha") {
918 s = s&~VColor.AMask|(clampToByte(alpha+((s>>VColor.AShift)&0xff)));
919 } else {
920 s = s&~VColor.AMask|a;
922 mixin(VColor.ColorBlendMixinStr!("s", "*dst"));
923 ++dst;
926 sba += vs.w;
927 dba += destw;
928 ++sy;
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); }