egra: checkbox fixes
[iv.d.git] / vl2 / vlo.d
blob9f50e691f15e26d9a8567d5c24c310537dbd6659
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module iv.vl2.vlo /*is aliced*/;
19 import iv.vl2.core;
20 import iv.vl2.font6;
23 // ////////////////////////////////////////////////////////////////////////// //
24 version(GNU) {
25 static import gcc.attribute;
26 private enum gcc_inline = gcc.attribute.attribute("forceinline");
27 private enum gcc_noinline = gcc.attribute.attribute("noinline");
28 private enum gcc_flatten = gcc.attribute.attribute("flatten");
29 } else {
30 // hackery for non-gcc compilers
31 private enum gcc_inline;
32 private enum gcc_noinline;
33 private enum gcc_flatten;
37 // ////////////////////////////////////////////////////////////////////////// //
38 __gshared VLOverlay vlsOvl; /// vscreen overlay
41 // ////////////////////////////////////////////////////////////////////////// //
42 private void vloInitVSO () {
43 vlsOvl.setupWithVScr(vlVScr, vlWidth, vlHeight);
47 private void vloDeinitVSO () {
48 vlsOvl.free(); // this will not free VScr, as vlsOvl isn't own it
52 shared static this () {
53 vlInitHook = &vloInitVSO;
54 vlDeinitHook = &vloDeinitVSO;
58 // ////////////////////////////////////////////////////////////////////////// //
59 // BEWARE! ANY COPIES WILL RESET `vscrOwn` FLAG! NO REFCOUNTING HERE!
60 struct VLOverlay {
61 //private:
62 public:
63 int mWidth, mHeight;
64 int mClipX0, mClipY0;
65 int mClipX1, mClipY1;
66 int mXOfs, mYOfs;
67 uint* mVScr;
68 bool vscrOwn; // true: `free()` mVScr in dtor
70 //private:
71 public:
72 this (void* avscr, int wdt, int hgt) @trusted nothrow @nogc { setupWithVScr(avscr, wdt, hgt); }
74 public:
75 this(TW, TH) (TW wdt, TH hgt) if (__traits(isIntegral, TW) && __traits(isIntegral, TH)) {
76 resize(wdt, hgt);
79 ~this () @trusted nothrow @nogc { free(); }
81 // any copy resets "own" flag
82 this (this) @safe nothrow @nogc { vscrOwn = false; }
84 //final:
85 void setupWithVScr (void* avscr, int wdt, int hgt) @trusted nothrow @nogc {
86 if (wdt < 1 || hgt < 1 || avscr is null) {
87 free();
88 } else {
89 if (avscr !is mVScr) free();
90 mVScr = cast(uint*)avscr;
91 vscrOwn = false;
92 mWidth = wdt;
93 mHeight = hgt;
94 resetClipOfs();
98 void resize(TW, TH) (TW wdt, TH hgt) if (__traits(isIntegral, TW) && __traits(isIntegral, TH)) {
99 import core.exception : onOutOfMemoryError;
100 import core.stdc.stdlib : malloc, realloc, free;
101 if (wdt < 1 || wdt > 16384 || hgt < 1 || hgt > 16384) throw new VideoLibError("VLOverlay: invalid size");
102 if (!vscrOwn) throw new VideoLibError("VLOverlay: can't resize predefined overlay");
103 if (mVScr is null) {
104 mWidth = cast(int)wdt;
105 mHeight = cast(int)hgt;
106 mVScr = cast(uint*)malloc(mWidth*mHeight*mVScr[0].sizeof);
107 if (mVScr is null) onOutOfMemoryError();
108 } else if (mWidth != cast(int)wdt || mHeight != cast(int)hgt) {
109 mWidth = cast(int)wdt;
110 mHeight = cast(int)hgt;
111 auto scr = cast(uint*)realloc(mVScr, mWidth*mHeight*mVScr[0].sizeof);
112 if (scr is null) { this.free(); onOutOfMemoryError(); }
113 mVScr = scr;
115 resetClipOfs();
118 /// WARNING! this will trash virtual screen!
119 @property void width(T) (T w) if (__traits(isIntegral, T)) { resize(w, mHeight); }
120 @property void height(T) (T h) if (__traits(isIntegral, T)) { resize(mWidth, h); }
122 @nogc:
123 nothrow:
124 @property bool valid () const pure { return (mVScr !is null); }
126 void free () @trusted {
127 if (vscrOwn && mVScr !is null) {
128 import core.stdc.stdlib : free;
129 free(mVScr);
130 mVScr = null;
132 mWidth = 1;
133 mHeight = 1;
134 resetClipOfs();
138 * Draw (possibly semi-transparent) pixel onto virtual screen; mix colors.
140 * Params:
141 * x = x coordinate
142 * y = y coordinate
143 * col = rgba color
145 * Returns:
146 * nothing
148 @gcc_inline void putPixel(TX, TY) (TX x, TY y, Color col) @trusted
149 if (__traits(isIntegral, TX) && __traits(isIntegral, TY))
151 static if (__VERSION__ > 2067) pragma(inline, true);
152 immutable long xx = cast(long)x+mXOfs;
153 immutable long yy = cast(long)y+mYOfs;
154 if ((col&vlAMask) != vlAMask && xx >= mClipX0 && yy >= mClipY0 && xx <= mClipX1 && yy <= mClipY1) {
155 uint* da = mVScr+yy*mWidth+xx;
156 if (col&vlAMask) {
157 immutable uint a = 256-(col>>24); // to not loose bits
158 immutable uint dc = (*da)&0xffffff;
159 immutable uint srb = (col&0xff00ff);
160 immutable uint sg = (col&0x00ff00);
161 immutable uint drb = (dc&0xff00ff);
162 immutable uint dg = (dc&0x00ff00);
163 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
164 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
165 *da = orb|og;
166 } else {
167 *da = col;
173 * Draw (possibly semi-transparent) pixel onto virtual screen; don't mix colors.
175 * Params:
176 * x = x coordinate
177 * y = y coordinate
178 * col = rgba color
180 * Returns:
181 * nothing
183 @gcc_inline void setPixel(TX, TY) (TX x, TY y, Color col) @trusted
184 if (__traits(isIntegral, TX) && __traits(isIntegral, TY))
186 static if (__VERSION__ > 2067) pragma(inline, true);
187 immutable long xx = cast(long)x+mXOfs;
188 immutable long yy = cast(long)y+mYOfs;
189 if (xx >= mClipX0 && yy >= mClipY0 && xx <= mClipX1 && yy <= mClipY1) {
190 uint* da = mVScr+yy*mWidth+xx;
191 *da = col;
195 void resetClipOfs () @safe {
196 if (mVScr !is null) {
197 mClipX0 = mClipY0 = mXOfs = mYOfs = 0;
198 mClipX1 = mWidth-1;
199 mClipY1 = mHeight-1;
200 } else {
201 // all functions checks clipping, and this is not valid region
202 // so we can omit VScr checks
203 mClipX0 = mClipY0 = -42;
204 mClipX1 = mClipY1 = -666;
208 @property int width () const @safe pure { return mWidth; }
209 @property int height () const @safe pure { return mHeight; }
211 @property int xOfs () const @safe pure { return mXOfs; }
212 @property void xOfs (int v) @safe { mXOfs = v; }
214 @property int yOfs () const @safe pure { return mYOfs; }
215 @property void yOfs (int v) @safe { mYOfs = v; }
217 void getOfs (ref int x, ref int y) const @safe pure { x = mXOfs; y = mYOfs; }
218 void setOfs (in int x, in int y) @safe { mXOfs = x; mYOfs = y; }
221 struct Ofs {
222 int x, y;
225 void getOfs (ref Ofs ofs) const @safe pure { ofs.x = mXOfs; ofs.y = mYOfs; }
226 void setOfs (in ref Ofs ofs) @safe { mXOfs = ofs.x; mYOfs = ofs.y; }
227 void resetOfs () @safe { mXOfs = mYOfs = 0; }
230 struct Clip {
231 int x, y, w, h;
234 void getClip (ref int x0, ref int y0, ref int wdt, ref int hgt) const @safe pure {
235 if (mVScr !is null) {
236 x0 = mClipX0;
237 y0 = mClipY0;
238 wdt = mClipX1-mClipX0+1;
239 hgt = mClipY1-mClipY0+1;
240 } else {
241 x0 = y0 = wdt = hgt = 0;
245 void setClip (in int x0, in int y0, in int wdt, in int hgt) @safe {
246 if (mVScr !is null) {
247 mClipX0 = x0;
248 mClipY0 = y0;
249 mClipX1 = (wdt > 0 ? x0+wdt-1 : x0-1);
250 mClipY1 = (hgt > 0 ? y0+hgt-1 : y0-1);
251 if (mClipX0 < 0) mClipX0 = 0;
252 if (mClipY0 < 0) mClipY0 = 0;
253 if (mClipX0 >= mWidth) mClipX0 = mWidth-1;
254 if (mClipY0 >= mHeight) mClipY0 = mHeight-1;
255 if (mClipX1 < 0) mClipX1 = 0;
256 if (mClipY1 < 0) mClipY1 = 0;
257 if (mClipX1 >= mWidth) mClipX1 = mWidth-1;
258 if (mClipY1 >= mHeight) mClipY1 = mHeight-1;
262 void resetClip () @safe {
263 if (mVScr !is null) {
264 mClipX0 = mClipY0 = 0;
265 mClipX1 = mWidth-1;
266 mClipY1 = mHeight-1;
267 } else {
268 // all functions checks clipping, and this is not valid region
269 // so we can omit VScr checks
270 mClipX0 = mClipY0 = -42;
271 mClipX1 = mClipY1 = -666;
275 void getClip (ref Clip clip) const @safe pure { getClip(clip.x, clip.y, clip.w, clip.h); }
276 void setClip (in ref Clip clip) @safe { setClip(clip.x, clip.y, clip.w, clip.h); }
278 void clipIntrude (int dx, int dy) @safe {
279 if (mVScr !is null) {
280 mClipX0 += dx;
281 mClipY0 += dx;
282 mClipX1 -= dx;
283 mClipY1 -= dx;
284 if (mClipX1 >= mClipX0 && mClipY1 >= mClipY0) {
285 setClip(mClipX0, mClipY0, mClipX1-mClipX0+1, mClipY1-mClipY0+1);
290 void clipExtrude (int dx, int dy) @safe { clipIntrude(-dx, -dy); }
292 // //////////////////////////////////////////////////////////////////////// //
294 * Draw character onto virtual screen in KOI8 encoding.
296 * Params:
297 * x = x coordinate
298 * y = y coordinate
299 * wdt = char width
300 * shift = shl count
301 * ch = character
302 * col = foreground color
303 * bkcol = background color
305 * Returns:
306 * nothing
308 void drawCharWdt (int x, int y, int wdt, int shift, char ch, Color col, Color bkcol=vlcTransparent) @trusted {
309 usize pos = ch*8;
310 if (wdt < 1 || shift >= 8) return;
311 if (col == vlcTransparent && bkcol == vlcTransparent) return;
312 if (wdt > 8) wdt = 8;
313 if (shift < 0) shift = 0;
314 foreach (immutable int dy; 0..8) {
315 ubyte b = cast(ubyte)(vlFont6[pos++]<<shift);
316 foreach (immutable int dx; 0..wdt) {
317 Color c = (b&0x80 ? col : bkcol);
318 if (!vlcIsTransparent(c)) putPixel(x+dx, y+dy, c);
319 b = (b<<1)&0xff;
324 // outline types
325 enum : ubyte {
326 OutLeft = 0x01,
327 OutRight = 0x02,
328 OutUp = 0x04,
329 OutDown = 0x08,
330 OutLU = 0x10, // left-up
331 OutRU = 0x20, // right-up
332 OutLD = 0x40, // left-down
333 OutRD = 0x80, // right-down
334 OutAll = 0xff,
338 * Draw outlined character onto virtual screen in KOI8 encoding.
340 * Params:
341 * x = x coordinate
342 * y = y coordinate
343 * wdt = char width
344 * shift = shl count
345 * ch = character
346 * col = foreground color
347 * outcol = outline color
348 * ot = outline type, OutXXX, ored
350 * Returns:
351 * nothing
353 void drawCharWdtOut (int x, int y, int wdt, int shift, char ch, Color col, Color outcol=vlcTransparent, ubyte ot=0) @trusted {
354 if (col == vlcTransparent && outcol == vlcTransparent) return;
355 if (ot == 0 || outcol == vlcTransparent) {
356 // no outline? simple draw
357 drawCharWdt(x, y, wdt, shift, ch, col, vlcTransparent);
358 return;
360 usize pos = ch*8;
361 if (wdt < 1 || shift >= 8) return;
362 if (wdt > 8) wdt = 8;
363 if (shift < 0) shift = 0;
364 ubyte[8+2][8+2] bmp = 0; // char bitmap; 0: empty; 1: char; 2: outline
365 foreach (immutable dy; 1..9) {
366 ubyte b = cast(ubyte)(vlFont6[pos++]<<shift);
367 foreach (immutable dx; 1..wdt+1) {
368 if (b&0x80) {
369 // put pixel
370 bmp[dy][dx] = 1;
371 // put outlines
372 if ((ot&OutUp) && bmp[dy-1][dx] == 0) bmp[dy-1][dx] = 2;
373 if ((ot&OutDown) && bmp[dy+1][dx] == 0) bmp[dy+1][dx] = 2;
374 if ((ot&OutLeft) && bmp[dy][dx-1] == 0) bmp[dy][dx-1] = 2;
375 if ((ot&OutRight) && bmp[dy][dx+1] == 0) bmp[dy][dx+1] = 2;
376 if ((ot&OutLU) && bmp[dy-1][dx-1] == 0) bmp[dy-1][dx-1] = 2;
377 if ((ot&OutRU) && bmp[dy-1][dx+1] == 0) bmp[dy-1][dx+1] = 2;
378 if ((ot&OutLD) && bmp[dy+1][dx-1] == 0) bmp[dy+1][dx-1] = 2;
379 if ((ot&OutRD) && bmp[dy+1][dx+1] == 0) bmp[dy+1][dx+1] = 2;
381 b = (b<<1)&0xff;
384 // now draw it
385 --x;
386 --y;
387 foreach (immutable int dy; 0..10) {
388 foreach (immutable int dx; 0..10) {
389 if (auto t = bmp[dy][dx]) putPixel(x+dx, y+dy, (t == 1 ? col : outcol));
395 * Draw 6x8 character onto virtual screen in KOI8 encoding.
397 * Params:
398 * x = x coordinate
399 * y = y coordinate
400 * ch = character
401 * col = foreground color
402 * bkcol = background color
404 * Returns:
405 * nothing
407 void drawChar (int x, int y, char ch, Color col, Color bkcol=vlcTransparent) @trusted {
408 drawCharWdt(x, y, 6, 0, ch, col, bkcol);
411 void drawCharOut (int x, int y, char ch, Color col, Color outcol=vlcTransparent, ubyte ot=OutAll) @trusted {
412 drawCharWdtOut(x, y, 6, 0, ch, col, outcol, ot);
415 void drawStr (int x, int y, string str, Color col, Color bkcol=vlcTransparent) @trusted {
416 foreach (immutable char ch; str) {
417 drawChar(x, y, ch, col, bkcol);
418 x += 6;
422 void drawStrOut (int x, int y, string str, Color col, Color outcol=vlcTransparent, ubyte ot=OutAll) @trusted {
423 foreach (immutable char ch; str) {
424 drawCharOut(x, y, ch, col, outcol, ot);
425 x += 6;
429 static int charWidthProp (char ch) @trusted pure { return (vlFontPropWidth[ch]&0x0f); }
431 int strWidthProp (string str) @trusted pure {
432 int wdt = 0;
433 foreach (immutable char ch; str) wdt += (vlFontPropWidth[ch]&0x0f)+1;
434 if (wdt > 0) --wdt; // don't count last empty pixel
435 return wdt;
438 int drawCharProp (int x, int y, char ch, Color col, Color bkcol=vlcTransparent) @trusted {
439 immutable int wdt = (vlFontPropWidth[ch]&0x0f);
440 drawCharWdt(x, y, wdt, vlFontPropWidth[ch]>>4, ch, col, bkcol);
441 return wdt;
444 int drawCharPropOut (int x, int y, char ch, Color col, Color outcol=vlcTransparent, ubyte ot=OutAll) @trusted {
445 immutable int wdt = (vlFontPropWidth[ch]&0x0f);
446 drawCharWdtOut(x, y, wdt, vlFontPropWidth[ch]>>4, ch, col, outcol, ot);
447 return wdt;
450 int drawStrProp (int x, int y, string str, Color col, Color bkcol=vlcTransparent) @trusted {
451 bool vline = false;
452 int sx = x;
453 foreach (immutable char ch; str) {
454 if (vline) {
455 if (!vlcIsTransparent(bkcol)) foreach (int dy; 0..8) putPixel(x, y+dy, bkcol);
456 ++x;
458 vline = true;
459 x += drawCharProp(x, y, ch, col, bkcol);
461 return x-sx;
464 int drawStrPropOut (int x, int y, string str, Color col, Color outcol=vlcTransparent, ubyte ot=OutAll) @trusted {
465 int sx = x;
466 foreach (immutable char ch; str) {
467 x += drawCharPropOut(x, y, ch, col, outcol, ot)+1;
469 if (x > sx) --x; // don't count last empty pixel
470 return x-sx;
473 // ////////////////////////////////////////////////////////////////////////// //
474 void clear (Color col) @trusted {
475 if (mVScr !is null) {
476 if (!vscrOwn) col &= 0xffffff;
477 mVScr[0..mWidth*mHeight] = col;
481 void hline (int x0, int y0, int len, Color col) @trusted {
482 if (isOpaque(col) && len > 0 && mVScr !is null) {
483 x0 += mXOfs;
484 y0 += mYOfs;
485 if (y0 >= mClipY0 && x0 <= mClipX1 && y0 <= mClipY1 && x0+len > mClipX0) {
486 if (x0 < mClipX0) { if ((len += (x0-mClipX0)) <= 0) return; x0 = mClipX0; }
487 if (x0+len-1 > mClipX1) len = mClipX1-x0+1;
488 immutable usize ofs = y0*mWidth+x0;
489 mVScr[ofs..ofs+len] = col;
491 } else {
492 while (len-- > 0) putPixel(x0++, y0, col);
496 void vline (int x0, int y0, int len, Color col) @trusted {
497 while (len-- > 0) putPixel(x0, y0++, col);
502 void drawLine(bool lastPoint) (int x0, int y0, int x1, int y1, Color col) @trusted {
503 import std.math : abs;
504 int dx = abs(x1-x0), sx = (x0 < x1 ? 1 : -1);
505 int dy = -abs(y1-y0), sy = (y0 < y1 ? 1 : -1);
506 int err = dx+dy, e2; // error value e_xy
507 for (;;) {
508 static if (lastPoint) putPixel(x0, y0, col);
509 if (x0 == x1 && y0 == y1) break;
510 static if (!lastPoint) putPixel(x0, y0, col);
511 e2 = 2*err;
512 if (e2 >= dy) { err += dy; x0 += sx; } // e_xy+e_x > 0
513 if (e2 <= dx) { err += dx; y0 += sy; } // e_xy+e_y < 0
518 // as the paper on which this code is based in not available to public,
519 // i say "fuck you!"
520 // knowledge must be publicly available; the ones who hides the knowledge
521 // are not deserving any credits.
522 void drawLine(bool lastPoint) (int x0, int y0, int x1, int y1, immutable Color col) {
523 enum swap(string a, string b) = "{int tmp_="~a~";"~a~"="~b~";"~b~"=tmp_;}";
525 if ((col&vlAMask) == vlAMask || mClipX0 > mClipX1 || mClipY0 > mClipY1 || mVScr is null) return;
527 if (x0 == x1 && y0 == y1) {
528 static if (lastPoint) putPixel(x0, y0, col);
529 return;
532 x0 += mXOfs; x1 += mXOfs;
533 y0 += mYOfs; y1 += mYOfs;
535 // clip rectange
536 int wx0 = mClipX0, wy0 = mClipY0, wx1 = mClipX1, wy1 = mClipY1;
537 // other vars
538 int stx, sty; // "steps" for x and y axes
539 int dsx, dsy; // "lengthes" for x and y axes
540 int dx2, dy2; // "double lengthes" for x and y axes
541 int xd, yd; // current coord
542 int e; // "error" (as in bresenham algo)
543 int rem;
544 int term;
545 int *d0, d1;
546 // horizontal setup
547 if (x0 < x1) {
548 // from left to right
549 if (x0 > wx1 || x1 < wx0) return; // out of screen
550 stx = 1; // going right
551 } else {
552 // from right to left
553 if (x1 > wx1 || x0 < wx0) return; // out of screen
554 stx = -1; // going left
555 x0 = -x0;
556 x1 = -x1;
557 wx0 = -wx0;
558 wx1 = -wx1;
559 mixin(swap!("wx0", "wx1"));
561 // vertical setup
562 if (y0 < y1) {
563 // from top to bottom
564 if (y0 > wy1 || y1 < wy0) return; // out of screen
565 sty = 1; // going down
566 } else {
567 // from bottom to top
568 if (y1 > wy1 || y0 < wy0) return; // out of screen
569 sty = -1; // going up
570 y0 = -y0;
571 y1 = -y1;
572 wy0 = -wy0;
573 wy1 = -wy1;
574 mixin(swap!("wy0", "wy1"));
576 dsx = x1-x0;
577 dsy = y1-y0;
578 if (dsx < dsy) {
579 d0 = &yd;
580 d1 = &xd;
581 mixin(swap!("x0", "y0"));
582 mixin(swap!("x1", "y1"));
583 mixin(swap!("dsx", "dsy"));
584 mixin(swap!("wx0", "wy0"));
585 mixin(swap!("wx1", "wy1"));
586 mixin(swap!("stx", "sty"));
587 } else {
588 d0 = &xd;
589 d1 = &yd;
591 dx2 = 2*dsx;
592 dy2 = 2*dsy;
593 xd = x0;
594 yd = y0;
595 e = 2*dsy-dsx;
596 term = x1;
597 bool xfixed = false;
598 if (y0 < wy0) {
599 // clip at top
600 int temp = dx2*(wy0-y0)-dsx;
601 xd += temp/dy2;
602 rem = temp%dy2;
603 if (xd > wx1) return; // x is moved out of clipping rect, nothing to do
604 if (xd+1 >= wx0) {
605 yd = wy0;
606 e -= rem+dsx;
607 if (rem > 0) { ++xd; e += dy2; }
608 xfixed = true;
611 if (!xfixed && x0 < wx0) {
612 // clip at left
613 int temp = dy2*(wx0-x0);
614 yd += temp/dx2;
615 rem = temp%dx2;
616 if (yd > wy1 || yd == wy1 && rem >= dsx) return;
617 xd = wx0;
618 e += rem;
619 if (rem >= dsx) { ++yd; e -= dx2; }
621 if (y1 > wy1) {
622 // clip at bottom
623 int temp = dx2*(wy1-y0)+dsx;
624 term = x0+temp/dy2;
625 rem = temp%dy2;
626 if (rem == 0) --term;
628 if (term > wx1) term = wx1; // clip at right
629 static if (lastPoint) {
630 // draw last point
631 ++term;
632 } else {
633 if (term == xd) return; // this is the only point, get out of here
635 if (sty == -1) yd = -yd;
636 if (stx == -1) { xd = -xd; term = -term; }
637 dx2 -= dy2;
638 // draw it; `putPixel()` can omit checks
639 while (xd != term) {
640 // inlined `putPixel(*d0, *d1, col)`
641 // this can be made even faster by precalculating `da` and making
642 // separate code branches for mixing and non-mixing drawing, but...
643 // ah, screw it!
644 uint* da = mVScr+(*d1)*mWidth+(*d0);
645 if (col&vlAMask) {
646 immutable uint a = 256-(col>>24); // to not loose bits
647 immutable uint dc = (*da)&0xffffff;
648 immutable uint srb = (col&0xff00ff);
649 immutable uint sg = (col&0x00ff00);
650 immutable uint drb = (dc&0xff00ff);
651 immutable uint dg = (dc&0x00ff00);
652 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
653 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
654 *da = orb|og;
655 } else {
656 *da = col;
658 // done drawing, move coords
659 if (e >= 0) {
660 yd += sty;
661 e -= dx2;
662 } else {
663 e += dy2;
665 xd += stx;
669 void line (int x0, int y0, int x1, int y1, Color col) @trusted { drawLine!true(x0, y0, x1, y1, col); }
670 void lineNoLast (int x0, int y0, int x1, int y1, Color col) @trusted { drawLine!false(x0, y0, x1, y1, col); }
672 void fillRect (int x, int y, int w, int h, Color col) @trusted {
673 x += mXOfs;
674 y += mYOfs;
675 if (w > 0 && h > 0 && x+w > mClipX0 && y+h > mClipY0 && x <= mClipX1 && y <= mClipY1) {
676 SDL_Rect r, sr, dr;
677 sr.x = mClipX0; sr.y = mClipY0; sr.w = mClipX1-mClipX0+1; sr.h = mClipY1-mClipY0+1;
678 r.x = x; r.y = y; r.w = w; r.h = h;
679 if (SDL_IntersectRect(&sr, &r, &dr)) {
680 x = dr.x-mXOfs;
681 y = dr.y-mYOfs;
682 while (dr.h-- > 0) hline(x, y++, dr.w, col);
687 void rect (int x, int y, int w, int h, Color col) @trusted {
688 if (w > 0 && h > 0) {
689 hline(x, y, w, col);
690 hline(x, y+h-1, w, col);
691 vline(x, y+1, h-2, col);
692 vline(x+w-1, y+1, h-2, col);
696 /* 4 phases */
697 void selectionRect (int phase, int x0, int y0, int wdt, int hgt, Color col0, Color col1=vlcTransparent) @trusted {
698 if (wdt > 0 && hgt > 0) {
699 // top
700 foreach (immutable f; x0..x0+wdt) { putPixel(f, y0, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
701 // right
702 foreach (immutable f; y0+1..y0+hgt) { putPixel(x0+wdt-1, f, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
703 // bottom
704 foreach_reverse (immutable f; x0..x0+wdt-1) { putPixel(f, y0+hgt-1, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
705 // left
706 foreach_reverse (immutable f; y0..y0+hgt-1) { putPixel(x0, f, ((phase %= 4) < 2 ? col0 : col1)); ++phase; }
710 private void plot4points() (int cx, int cy, int x, int y, Color clr) @trusted {
711 putPixel(cx+x, cy+y, clr);
712 if (x != 0) putPixel(cx-x, cy+y, clr);
713 if (y != 0) putPixel(cx+x, cy-y, clr);
714 putPixel(cx-x, cy-y, clr);
717 void circle (int cx, int cy, int radius, Color clr) @trusted {
718 if (radius > 0 && !vlcIsTransparent(clr)) {
719 int error = -radius, x = radius, y = 0;
720 if (radius == 1) { putPixel(cx, cy, clr); return; }
721 while (x > y) {
722 plot4points(cx, cy, x, y, clr);
723 plot4points(cx, cy, y, x, clr);
724 error += y*2+1;
725 ++y;
726 if (error >= 0) { --x; error -= x*2; }
728 plot4points(cx, cy, x, y, clr);
732 void fillCircle (int cx, int cy, int radius, Color clr) @trusted {
733 if (radius > 0 && !vlcIsTransparent(clr)) {
734 int error = -radius, x = radius, y = 0;
735 if (radius == 1) { putPixel(cx, cy, clr); return; }
736 while (x >= y) {
737 int last_y = y;
738 error += y;
739 ++y;
740 error += y;
741 hline(cx-x, cy+last_y, 2*x+1, clr);
742 if (x != 0 && last_y != 0) hline(cx-x, cy-last_y, 2*x+1, clr);
743 if (error >= 0) {
744 if (x != last_y) {
745 hline(cx-last_y, cy+x, 2*last_y+1, clr);
746 if (last_y != 0 && x != 0) hline(cx-last_y, cy-x, 2*last_y+1, clr);
748 error -= x;
749 --x;
750 error -= x;
756 void ellipse (int x0, int y0, int x1, int y1, Color clr) @trusted {
757 import std.math : abs;
758 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
759 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
760 long err = dx+dy+b1*a*a; // error of 1.step
761 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
762 if (y0 > y1) y0 = y1; // ...exchange them
763 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
764 a *= 8*a; b1 = 8*b*b;
765 do {
766 long e2;
767 putPixel(x1, y0, clr); // I. Quadrant
768 putPixel(x0, y0, clr); // II. Quadrant
769 putPixel(x0, y1, clr); // III. Quadrant
770 putPixel(x1, y1, clr); // IV. Quadrant
771 e2 = 2*err;
772 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
773 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
774 } while (x0 <= x1);
775 while (y0-y1 < b) {
776 // too early stop of flat ellipses a=1
777 putPixel(x0-1, ++y0, clr); // complete tip of ellipse
778 putPixel(x0-1, --y1, clr);
782 void fillEllipse (int x0, int y0, int x1, int y1, Color clr) @trusted {
783 import std.math : abs;
784 int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // values of diameter
785 long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; // error increment
786 long err = dx+dy+b1*a*a; // error of 1.step
787 int prev_y0 = -1, prev_y1 = -1;
788 if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points...
789 if (y0 > y1) y0 = y1; // ...exchange them
790 y0 += (b+1)/2; y1 = y0-b1; // starting pixel
791 a *= 8*a; b1 = 8*b*b;
792 do {
793 long e2;
794 if (y0 != prev_y0) { hline(x0, y0, x1-x0+1, clr); prev_y0 = y0; }
795 if (y1 != y0 && y1 != prev_y1) { hline(x0, y1, x1-x0+1, clr); prev_y1 = y1; }
796 e2 = 2*err;
797 if (e2 >= dx) { ++x0; --x1; err += dx += b1; } // x step
798 if (e2 <= dy) { ++y0; --y1; err += dy += a; } // y step
799 } while (x0 <= x1);
800 while (y0-y1 < b) {
801 // too early stop of flat ellipses a=1
802 putPixel(x0-1, ++y0, clr); // complete tip of ellipse
803 putPixel(x0-1, --y1, clr);
807 /** blit overlay to main screen */
808 void blitTpl(string btype) (ref VLOverlay destovl, int xd, int yd, ubyte alpha=0) @trusted {
809 static if (btype == "NoSrcAlpha") import core.stdc.string : memcpy;
810 if (!valid || !destovl.valid) return;
811 if (xd > -mWidth && yd > -mHeight && xd < destovl.mWidth && yd < destovl.mHeight && alpha < 255) {
812 int w = mWidth, h = mHeight;
813 immutable uint vsPitch = destovl.mWidth;
814 immutable uint myPitch = mWidth;
815 uint *my = mVScr;
816 uint *dest;
817 // vertical clipping
818 if (yd < 0) {
819 // skip invisible top part
820 if ((h += yd) < 1) return;
821 my -= yd*mWidth;
822 yd = 0;
824 if (yd+h > destovl.mHeight) {
825 // don't draw invisible bottom part
826 if ((h = destovl.mHeight-yd) < 1) return;
828 // horizontal clipping
829 if (xd < 0) {
830 // skip invisible left part
831 if ((w += xd) < 1) return;
832 my -= xd;
833 xd = 0;
835 if (xd+w > destovl.mWidth) {
836 // don't draw invisible right part
837 if ((w = destovl.mWidth-xd) < 1) return;
839 // copying?
840 dest = destovl.mVScr+yd*vsPitch+xd;
841 static if (btype == "NoSrcAlpha") {
842 if (alpha == 0) {
843 while (h-- > 0) {
844 import core.stdc.string : memcpy;
845 memcpy(dest, my, w*destovl.mVScr[0].sizeof);
846 dest += vsPitch;
847 my += myPitch;
849 return;
852 // alpha mixing
854 static if (btype == "NoSrcAlpha") immutable uint a = 256-alpha; // to not loose bits
855 while (h-- > 0) {
856 auto src = cast(immutable(uint)*)my;
857 auto dst = dest;
858 foreach_reverse (immutable dx; 0..w) {
859 immutable uint s = *src++;
860 static if (btype == "SrcAlpha") {
861 if ((s&vlAMask) == vlcTransparent) { ++dst; continue; }
862 immutable uint a = 256-clampToByte(cast(int)(alpha+(s>>vlAShift)&0xff)); // to not loose bits
864 immutable uint dc = (*dst)&0xffffff;
865 immutable uint srb = (s&0xff00ff);
866 immutable uint sg = (s&0x00ff00);
867 immutable uint drb = (dc&0xff00ff);
868 immutable uint dg = (dc&0x00ff00);
869 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
870 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
871 *dst++ = orb|og;
873 dest += vsPitch;
874 my += myPitch;
880 alias blit = blitTpl!"NoSrcAlpha";
881 alias blitSrcAlpha = blitTpl!"SrcAlpha";