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