| 1 |
module qd; |
|---|
| 2 |
import tools.base, tools.compat; |
|---|
| 3 |
alias tools.base.min min; |
|---|
| 4 |
alias tools.base.max max; |
|---|
| 5 |
|
|---|
| 6 |
template ctype(string S) { |
|---|
| 7 |
static if (S == "unsigned long") |
|---|
| 8 |
alias uint ctype; |
|---|
| 9 |
else static if (S == "long") |
|---|
| 10 |
alias int ctype; |
|---|
| 11 |
else static assert(false, "Unsupported C type "~S); |
|---|
| 12 |
} |
|---|
| 13 |
|
|---|
| 14 |
import tools.base; |
|---|
| 15 |
extern(C) { |
|---|
| 16 |
struct SDL_Rect { |
|---|
| 17 |
short x, y; |
|---|
| 18 |
ushort w, h; |
|---|
| 19 |
} |
|---|
| 20 |
struct SDL_RWops { |
|---|
| 21 |
int function(SDL_RWops *context, int offset, int whence) seek; |
|---|
| 22 |
int function(SDL_RWops *context, void *ptr, int size, int maxnum) read; |
|---|
| 23 |
int function(SDL_RWops *context, void *ptr, int size, int maxnum) write; |
|---|
| 24 |
int function(SDL_RWops *context) close; |
|---|
| 25 |
uint type; |
|---|
| 26 |
union hidden { |
|---|
| 27 |
struct stdio { int autoclose; void *fp; } |
|---|
| 28 |
struct mem { ubyte *base, here, stop; } |
|---|
| 29 |
struct unknown { void *data; } |
|---|
| 30 |
} |
|---|
| 31 |
} |
|---|
| 32 |
struct SDL_Color { |
|---|
| 33 |
ubyte r, g, b, a; |
|---|
| 34 |
} |
|---|
| 35 |
struct SDL_PixelFormat { |
|---|
| 36 |
//SDL_Palette *palette; |
|---|
| 37 |
void *palette; |
|---|
| 38 |
ubyte BitsPerPixel, BytesPerPixel, Rloss, Gloss, Bloss, Aloss, Rshift, Gshift, Bshift, Ashift; |
|---|
| 39 |
uint Rmask, Gmask, Bmask, Amask, colorkey; ubyte alpha; |
|---|
| 40 |
} |
|---|
| 41 |
struct SDL_Surface { |
|---|
| 42 |
uint flags; |
|---|
| 43 |
SDL_PixelFormat *format; |
|---|
| 44 |
int w, h; |
|---|
| 45 |
ushort pitch; |
|---|
| 46 |
void *pixels; |
|---|
| 47 |
int offset; |
|---|
| 48 |
void *hwdata; |
|---|
| 49 |
SDL_Rect clip_rect; |
|---|
| 50 |
uint unused; |
|---|
| 51 |
uint locked; |
|---|
| 52 |
void *map; |
|---|
| 53 |
uint format_version; |
|---|
| 54 |
int refcount; |
|---|
| 55 |
} |
|---|
| 56 |
uint SDL_MapRGBA(SDL_PixelFormat *format, ubyte r, ubyte g, ubyte b, ubyte a); |
|---|
| 57 |
void SDL_GetRGBA(uint pixel, SDL_PixelFormat *fmt, ubyte *r, ubyte *g, ubyte *b, ubyte *a); |
|---|
| 58 |
void SDL_GetRGB(uint pixel, SDL_PixelFormat *fmt, ubyte *r, ubyte *g, ubyte *b); |
|---|
| 59 |
int SDL_LockSurface(SDL_Surface *); |
|---|
| 60 |
void SDL_UnlockSurface(SDL_Surface *); |
|---|
| 61 |
SDL_Surface * SDL_SetVideoMode(int width, int height, int bpp, uint flags); |
|---|
| 62 |
SDL_Surface *SDL_CreateRGBSurface( |
|---|
| 63 |
uint flags, int width, int height, int depth, |
|---|
| 64 |
uint Rmask=0xff, uint Gmask=0xff00, uint Bmask=0xff0000, uint Amask=/*0xff000000*/0); |
|---|
| 65 |
void SDL_FreeSurface(SDL_Surface *); |
|---|
| 66 |
int SDL_Flip(SDL_Surface *); |
|---|
| 67 |
void SDL_UpdateRect (SDL_Surface *screen, int x=0, int y=0, uint w=0, uint h=0); |
|---|
| 68 |
int SDL_UpperBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect); |
|---|
| 69 |
alias SDL_UpperBlit SDL_BlitSurface; |
|---|
| 70 |
int SDL_SetAlpha(SDL_Surface *surface, uint flags, ubyte alpha); |
|---|
| 71 |
int SDL_SetColorKey(SDL_Surface *surface, uint flag, uint key); |
|---|
| 72 |
int SDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, uint color); |
|---|
| 73 |
SDL_RWops *SDL_RWFromFile(char *file, char *mode); |
|---|
| 74 |
SDL_RWops *SDL_RWFromMem(void *mem, int size); |
|---|
| 75 |
char *SDL_GetError(); |
|---|
| 76 |
const uint SDL_SWSURFACE=0, SDL_HWSURFACE=1, SDL_ASYNCBLIT=4; |
|---|
| 77 |
const uint SDL_ANYFORMAT=0x10000000, SDL_HWPALETTE=0x20000000, SDL_DOUBLEBUF=0x40000000, SDL_FULLSCREEN=0x80000000; |
|---|
| 78 |
const uint SDL_OPENGL=0x2, SDL_OPENGLBLIT=0xa, SDL_RESIZABLE=0x10, SDL_NOFRAME=0x20; |
|---|
| 79 |
const uint SDL_SRCALPHA=0x00010000, SDL_SRCCOLORKEY=0x00001000; |
|---|
| 80 |
void SDL_Delay(uint ms); |
|---|
| 81 |
uint SDL_GetTicks(); |
|---|
| 82 |
ubyte SDL_EventState(ubyte type, int state); |
|---|
| 83 |
|
|---|
| 84 |
enum SDLKey { |
|---|
| 85 |
Unknown = 0, First = 0, Return = 13, |
|---|
| 86 |
Escape = 27, |
|---|
| 87 |
Delete = 127, |
|---|
| 88 |
LCtrl = 306, |
|---|
| 89 |
a = 97, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z = 122, Zero = 48, Nine = 57, |
|---|
| 90 |
Up = 273, Down = 274, Right = 275, Left = 276, |
|---|
| 91 |
Home = 278, End = 279, PageUp = 280, PageDown = 281, |
|---|
| 92 |
KP_Multiply = 268, KP_Divide = 267, KP_Plus = 270, KP_Minus = 269, |
|---|
| 93 |
Minus = 45, Plus = 43, Backspace = 8, |
|---|
| 94 |
F1 = 282, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 |
|---|
| 95 |
} |
|---|
| 96 |
enum SDLMod { |
|---|
| 97 |
None = 0, LShift = 1, RShift = 2, Shift = 3, |
|---|
| 98 |
LCtrl = 0x40, RCtrl = 0x80, Ctrl = 0xc0, |
|---|
| 99 |
LAlt = 0x100, RAlt = 0x200, Alt = 0x300, |
|---|
| 100 |
LMeta = 0x400, RMeta = 0x800, |
|---|
| 101 |
Num = 0x1000, Caps = 0x2000, Mode = 0x4000, |
|---|
| 102 |
Reserved = 0x8000 |
|---|
| 103 |
}; |
|---|
| 104 |
|
|---|
| 105 |
struct SDL_keysym { ubyte scancode; SDLKey sym; SDLMod mod; ushort unicode; } |
|---|
| 106 |
void SDL_EnableUNICODE(int enable=1); |
|---|
| 107 |
enum SDL_EventType : ubyte { |
|---|
| 108 |
NoEvent=0, Active, KeyDown, KeyUp, |
|---|
| 109 |
MouseMotion, MouseButtonDown, MouseButtonUp, JoyAxisMotion, |
|---|
| 110 |
JoyBallMotion, JoyHatMotion, JoyButtonDown, JoyButtonUp, |
|---|
| 111 |
Quit, SysWMEvent, ReservedA, ReservedB, Resize, Expose |
|---|
| 112 |
} |
|---|
| 113 |
version(win32) { } |
|---|
| 114 |
else { |
|---|
| 115 |
alias uint Window; alias uint Atom; |
|---|
| 116 |
char* XGetAtomName(void* display, Atom atom); |
|---|
| 117 |
Atom XInternAtom(void* display, char* name, bool only_if_exists = true); |
|---|
| 118 |
int XConvertSelection(void* display, Atom selection, Atom target, Atom property, Window requester, int time); |
|---|
| 119 |
bool XCheckTypedWindowEvent(void* display, Window w, int type, XEvent* target); |
|---|
| 120 |
int XGetWindowProperty(void* display, Window w, Atom property, |
|---|
| 121 |
ctype!("long") offset, ctype!("long") length, bool del, Atom req_type, Atom* actual_type_return, |
|---|
| 122 |
int* actual_format_return, ctype!("unsigned long")* nitems_return, ctype!("unsigned long")* bytes_after_return, |
|---|
| 123 |
ubyte** prop_return); |
|---|
| 124 |
int XFree(void* data); |
|---|
| 125 |
void* XOpenDisplay(char* name = null); |
|---|
| 126 |
int XCloseDisplay(void* display); |
|---|
| 127 |
int XChangeProperty(void* display, Window w, Atom property, Atom type, int format, int mode, ubyte* data, int nelements); |
|---|
| 128 |
const XA_ATOM = 4, PropModeReplace = 0; |
|---|
| 129 |
static if (false) void setupDragDrop() { |
|---|
| 130 |
Atom dndVersion = 4; |
|---|
| 131 |
auto disp = XOpenDisplay; scope(exit) XCloseDisplay(disp); |
|---|
| 132 |
XChangeProperty(disp, getWindow, XInternAtom(disp, "XdndAware"), XA_ATOM, 32, PropModeReplace, cast(ubyte*) &dndVersion, 1); |
|---|
| 133 |
} |
|---|
| 134 |
const SelectionNotify = 31, AnyPropertyType = 0; |
|---|
| 135 |
struct XClientMessageEvent { |
|---|
| 136 |
ctype!("unsigned long") serial; |
|---|
| 137 |
bool send_event; |
|---|
| 138 |
void* display; |
|---|
| 139 |
Window window; |
|---|
| 140 |
Atom type; |
|---|
| 141 |
int format; |
|---|
| 142 |
union Data { byte[20] b; short[10] s; int[5] l; } |
|---|
| 143 |
Data data; |
|---|
| 144 |
} |
|---|
| 145 |
struct XSelectionEvent { |
|---|
| 146 |
ctype!("unsigned long") serial; |
|---|
| 147 |
bool send_event; |
|---|
| 148 |
void* display; |
|---|
| 149 |
Window window; |
|---|
| 150 |
Atom selection, target, property; int time; |
|---|
| 151 |
} |
|---|
| 152 |
struct XEvent { int type; union { XClientMessageEvent xclient; XSelectionEvent xselect; } } |
|---|
| 153 |
alias int Status; |
|---|
| 154 |
Status XSendEvent(void* display, Window w, bool propagate, ctype!("long") mask, XEvent* send); |
|---|
| 155 |
struct SDL_SysWMinfo { |
|---|
| 156 |
ubyte major, minor, patch; |
|---|
| 157 |
int SDL_SYSWM_TYPE; |
|---|
| 158 |
union { |
|---|
| 159 |
struct _x11 { |
|---|
| 160 |
void* display; |
|---|
| 161 |
Window window; |
|---|
| 162 |
} |
|---|
| 163 |
_x11 x11; |
|---|
| 164 |
} |
|---|
| 165 |
} |
|---|
| 166 |
int SDL_GetWMInfo(SDL_SysWMinfo* info); |
|---|
| 167 |
Window getWindow() { |
|---|
| 168 |
char[1024] buffer; |
|---|
| 169 |
auto info = cast(SDL_SysWMinfo*) buffer.ptr; |
|---|
| 170 |
info.major = 1; info.minor = 2; info.patch = 13; |
|---|
| 171 |
SDL_GetWMInfo(info); |
|---|
| 172 |
return info.x11.window; |
|---|
| 173 |
} |
|---|
| 174 |
} |
|---|
| 175 |
union SDL_Event { |
|---|
| 176 |
SDL_EventType type; |
|---|
| 177 |
struct Active { SDL_EventType type, gain, state; }; Active active; |
|---|
| 178 |
struct Key { SDL_EventType type, which, state; SDL_keysym keysym; }; Key key; |
|---|
| 179 |
struct Motion { SDL_EventType type, which, state; ushort x, y; short xrel, yrel; }; Motion motion; |
|---|
| 180 |
struct Button { SDL_EventType type, which, button, state; ushort x, y; }; Button button; |
|---|
| 181 |
struct Jaxis { SDL_EventType type, which, axis; short value; }; Jaxis jaxis; |
|---|
| 182 |
struct Jball { SDL_EventType type, which, ball; short xrel, yrel; }; Jball jball; |
|---|
| 183 |
struct Jhat { SDL_EventType type, which, hat, value; }; Jhat jhat; |
|---|
| 184 |
struct Jbutton { SDL_EventType type, which, button, state; }; Jbutton jbutton; |
|---|
| 185 |
struct Resize { SDL_EventType type; int w, h; }; Resize resize; |
|---|
| 186 |
struct Expose { SDL_EventType type; }; Expose expose; |
|---|
| 187 |
struct Quit { SDL_EventType type; }; Quit quit; |
|---|
| 188 |
struct User { SDL_EventType type; int code; void *data1, data2; }; User user; |
|---|
| 189 |
version(win32) alias void SyswmMsg; |
|---|
| 190 |
else { |
|---|
| 191 |
struct SyswmMsg { |
|---|
| 192 |
ubyte major, minor, patch; |
|---|
| 193 |
uint subsystem; |
|---|
| 194 |
XEvent xevent; |
|---|
| 195 |
} |
|---|
| 196 |
} |
|---|
| 197 |
struct Syswm { SDL_EventType type; SyswmMsg *msg; }; Syswm syswm; |
|---|
| 198 |
} |
|---|
| 199 |
|
|---|
| 200 |
int SDL_PollEvent(SDL_Event *event); |
|---|
| 201 |
int SDL_SaveBMP_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst); |
|---|
| 202 |
void SDL_QuitSubSystem(uint flags); |
|---|
| 203 |
const uint SDL_INIT_VIDEO = 0x00000020; |
|---|
| 204 |
int SDL_Init(uint flags); |
|---|
| 205 |
char* SDL_GetKeyName(SDLKey key); |
|---|
| 206 |
void SDL_WarpMouse(ushort x, ushort y); |
|---|
| 207 |
} |
|---|
| 208 |
|
|---|
| 209 |
static this() { |
|---|
| 210 |
SDL_Init(SDL_INIT_VIDEO); |
|---|
| 211 |
SDL_EnableUNICODE(); |
|---|
| 212 |
} |
|---|
| 213 |
|
|---|
| 214 |
void SDL_SaveBMP(SDL_Surface *surf, string name) { SDL_SaveBMP_RW(surf, SDL_RWFromFile(toStringz(name), "wb"), 1); } |
|---|
| 215 |
|
|---|
| 216 |
//SDL_Surface *display; |
|---|
| 217 |
Area display; |
|---|
| 218 |
|
|---|
| 219 |
void putpixel32(SDL_Surface *which, int x, int y, uint c) { |
|---|
| 220 |
uint *bufp = cast(uint *)which.pixels + y*which.pitch/4 + x; |
|---|
| 221 |
*bufp = c; |
|---|
| 222 |
} |
|---|
| 223 |
|
|---|
| 224 |
void putpixel32(int x, int y, rgb col) { |
|---|
| 225 |
putpixel32(display.surface, x, y, SDL_MapRGBA(display.surface.format, col.values[0], col.values[1], col.values[2], 0)); |
|---|
| 226 |
} |
|---|
| 227 |
|
|---|
| 228 |
void getpixel32(SDL_Surface *which, int x, int y, ubyte[4]* col) { |
|---|
| 229 |
uint* bufp = cast(uint *)which.pixels + y*which.pitch/4 + x; |
|---|
| 230 |
SDL_GetRGBA(*bufp, which.format, col.ptr, col.ptr + 1, col.ptr + 2, col.ptr + 3); |
|---|
| 231 |
// we know we're 32-bit, so we can decode directly |
|---|
| 232 |
// presume RGBA order // this might actually be WRONG !! |
|---|
| 233 |
// ptr = cast(ubyte*) bufp; |
|---|
| 234 |
} |
|---|
| 235 |
|
|---|
| 236 |
void fastblend32(int FractionBits)(rgb to) { |
|---|
| 237 |
auto ds = display.surface; |
|---|
| 238 |
uint add = SDL_MapRGBA(ds.format, to.r, to.g, to.b, 0); |
|---|
| 239 |
const FractionMask = |
|---|
| 240 |
(255 >> FractionBits) + |
|---|
| 241 |
((255 >> FractionBits) << 8) + |
|---|
| 242 |
((255 >> FractionBits) << 16) + |
|---|
| 243 |
((255 >> FractionBits) << 24); // construct shift mask |
|---|
| 244 |
add = (add >> FractionBits) & FractionMask; |
|---|
| 245 |
for (int y = 0; y < ds.h; ++y) { |
|---|
| 246 |
auto line = cast(uint*) ds.pixels + y * ds.pitch / 4; |
|---|
| 247 |
// enable autovec |
|---|
| 248 |
const Step = 16; |
|---|
| 249 |
int x; |
|---|
| 250 |
for (x = 0; x <= ds.w - Step; x += Step) { |
|---|
| 251 |
auto chunk = &line[x]; |
|---|
| 252 |
for (int x2 = 0; x2 < Step; ++x2) { |
|---|
| 253 |
// fastblend |
|---|
| 254 |
chunk[x2] = chunk[x2] - ((chunk[x2] >> FractionBits) & FractionMask) + add; |
|---|
| 255 |
} |
|---|
| 256 |
} |
|---|
| 257 |
// rest |
|---|
| 258 |
for (; x < ds.w; ++x) { |
|---|
| 259 |
line[x] = line[x] - ((line[x] >> FractionBits) & FractionMask) + add; |
|---|
| 260 |
} |
|---|
| 261 |
} |
|---|
| 262 |
} |
|---|
| 263 |
|
|---|
| 264 |
rgb getpixel32(int x, int y) { |
|---|
| 265 |
ubyte[4] temp = void; |
|---|
| 266 |
uint* bufp = cast(uint*) display.surface.pixels + y*display.surface.pitch/4 + x; |
|---|
| 267 |
SDL_GetRGBA(*bufp, display.surface.format, &temp[0], &temp[1], &temp[2], &temp[3]); |
|---|
| 268 |
return *cast(rgb*) temp.ptr; |
|---|
| 269 |
} |
|---|
| 270 |
|
|---|
| 271 |
void getpixel24(SDL_Surface *which, int x, int y, ubyte[4] *col) { |
|---|
| 272 |
ubyte *bufp = cast(ubyte *)which.pixels + y*which.pitch + x*3; |
|---|
| 273 |
uint value=bufp[2] << 16 | bufp[1] << 8 | bufp[0]; |
|---|
| 274 |
SDL_GetRGBA(value, which.format, col.ptr, col.ptr+1, col.ptr+2, col.ptr+3); |
|---|
| 275 |
} |
|---|
| 276 |
|
|---|
| 277 |
struct hsv { |
|---|
| 278 |
union { |
|---|
| 279 |
ubyte[3] values; |
|---|
| 280 |
alias values field; |
|---|
| 281 |
struct { ubyte h, s, v; } |
|---|
| 282 |
} |
|---|
| 283 |
static hsv opCall(T...)(T t) { |
|---|
| 284 |
hsv res = void; |
|---|
| 285 |
static if (T.length == 1 && is(T[0]==rgb)) { |
|---|
| 286 |
with (t[0]) with (res) { |
|---|
| 287 |
auto Min = min(r, g, b), Max = max(r, g, b), delta = Max - Min; |
|---|
| 288 |
v = Max; |
|---|
| 289 |
float res_h; |
|---|
| 290 |
if (Max) s = cast(ubyte) (255f*(1f*delta / Max)); |
|---|
| 291 |
else { |
|---|
| 292 |
s = 0; |
|---|
| 293 |
res_h = 0; |
|---|
| 294 |
return res; |
|---|
| 295 |
} |
|---|
| 296 |
if (r == Max) res_h = (g - b)*1f / delta; |
|---|
| 297 |
else if (g == Max) res_h = 2f + (b - r)*1f / delta; |
|---|
| 298 |
else res_h = 4f + (r - g)*1f / delta; |
|---|
| 299 |
res_h *= 60f; if (res_h < 0) res_h += 360f; |
|---|
| 300 |
h = cast(ubyte) (res_h*255f/360f); |
|---|
| 301 |
} |
|---|
| 302 |
} else { |
|---|
| 303 |
static assert (T.length == 3, "Invalid parameter: "~T.stringof); |
|---|
| 304 |
res.h = cast(ubyte) t[0]; res.s = cast(ubyte) t[1]; res.v = cast(ubyte) t[2]; |
|---|
| 305 |
} |
|---|
| 306 |
return res; |
|---|
| 307 |
} |
|---|
| 308 |
} |
|---|
| 309 |
|
|---|
| 310 |
struct rgb { |
|---|
| 311 |
union { |
|---|
| 312 |
ubyte[3] values; |
|---|
| 313 |
struct { ubyte r, g, b; } |
|---|
| 314 |
Repeat!(ubyte, 3) tuple; |
|---|
| 315 |
} |
|---|
| 316 |
SDL_Color toSDL() { return SDL_Color(r, g, b, 0); } |
|---|
| 317 |
uint map() { |
|---|
| 318 |
return SDL_MapRGBA(display.surface.format, r, g, b, 0); |
|---|
| 319 |
} |
|---|
| 320 |
rgb opCat(rgb other) { |
|---|
| 321 |
rgb res; |
|---|
| 322 |
foreach (id, ref v; res.values) v=cast(ubyte)((values[id]+other.values[id])/2); |
|---|
| 323 |
return res; |
|---|
| 324 |
} |
|---|
| 325 |
bool opEquals(rgb r) { |
|---|
| 326 |
return values == r.values; |
|---|
| 327 |
} |
|---|
| 328 |
rgb blend(R, S)(R other, S f) { |
|---|
| 329 |
static assert(is(typeof(R.r)) && is(typeof(R.g)) && is(typeof(R.b)), R.stringof~" cannot be blended with: needs r,g,b!"); |
|---|
| 330 |
static if (is(S == ubyte)) { |
|---|
| 331 |
return rgb((r*(256-f)+other.r*f)/256, (g*(256-f)+other.g*f)/256, (b*(256-f)+other.b*f)/256); |
|---|
| 332 |
} else { |
|---|
| 333 |
static assert(is(S: real), " Cannot blend "~R.stringof~" with factor "~S.stringof); |
|---|
| 334 |
return rgb(r+(other.r-r)*f, g+(other.g-g)*f, b+(other.b-b)*f); |
|---|
| 335 |
} |
|---|
| 336 |
} |
|---|
| 337 |
rgb opMul(rgb other) { |
|---|
| 338 |
return rgb((r*other.r)/256, (g*other.g)/256, (b*other.b)/256); |
|---|
| 339 |
} |
|---|
| 340 |
rgb opMul(float f) { |
|---|
| 341 |
return rgb(cast(ubyte) (r*f), cast(ubyte) (g*f), cast(ubyte) (b*f)); |
|---|
| 342 |
} |
|---|
| 343 |
rgb opMulAssign(rgb other) { *this = opMul(other); return *this; } |
|---|
| 344 |
rgb opAdd(rgb other) { |
|---|
| 345 |
rgb res = void; |
|---|
| 346 |
res.r = cast(ubyte) min(255, r + other.r); |
|---|
| 347 |
res.g = cast(ubyte) min(255, g + other.g); |
|---|
| 348 |
res.b = cast(ubyte) min(255, b + other.b); |
|---|
| 349 |
return res; |
|---|
| 350 |
} |
|---|
| 351 |
rgb invert() { return rgb(255-r, 255-g, 255-b); } |
|---|
| 352 |
static rgb opCall(T...)(T tuple) { |
|---|
| 353 |
rgb res = void; |
|---|
| 354 |
static if (T.length == 1) { |
|---|
| 355 |
static if (is(T[0] == hsv)) { |
|---|
| 356 |
with (tuple[0]) { |
|---|
| 357 |
if (!s) res=rgb(v, v, v); |
|---|
| 358 |
else { |
|---|
| 359 |
auto float_h = 6f * h / 256f, float_s = s / 255f, float_v = v / 255f; |
|---|
| 360 |
auto i = cast(int) float_h, f = float_h - i; |
|---|
| 361 |
auto p = float_v * (1f - float_s), q = float_v * (1f - float_s * f), t = float_v * (1f - float_s * (1f - f)); |
|---|
| 362 |
switch (i) { |
|---|
| 363 |
case 0: res = rgb(float_v*255f, t*255f, p*255f); break; |
|---|
| 364 |
case 1: res = rgb(q*255f, float_v*255f, p*255f); break; |
|---|
| 365 |
case 2: res = rgb(p*255f, float_v*255f, t*255f); break; |
|---|
| 366 |
case 3: res = rgb(p*255f, q*255f, float_v*255f); break; |
|---|
| 367 |
case 4: res = rgb(t*255f, p*255f, float_v*255f); break; |
|---|
| 368 |
case 5: res = rgb(float_v*255f, p*255f, q*255f); break; |
|---|
| 369 |
default: fail("Oh noes."); |
|---|
| 370 |
} |
|---|
| 371 |
} |
|---|
| 372 |
} |
|---|
| 373 |
} else static if (is(T[0] == ubyte[3])) { |
|---|
| 374 |
res.r = tuple[0][0]; res.g = tuple[0][1]; res.b = tuple[0][2]; |
|---|
| 375 |
} else static assert(false, "rgb: invalid parameter: "~T.stringof); |
|---|
| 376 |
} else { |
|---|
| 377 |
static assert (T.length == 3, "rgb: Invalid parameter: "~T.stringof); |
|---|
| 378 |
res.r = cast(ubyte) tuple[0]; res.g = cast(ubyte) tuple[1]; res.b = cast(ubyte) tuple[2]; |
|---|
| 379 |
} |
|---|
| 380 |
return res; |
|---|
| 381 |
} |
|---|
| 382 |
string toString() { return Format("RGB ", values); } |
|---|
| 383 |
} |
|---|
| 384 |
|
|---|
| 385 |
int alpha; |
|---|
| 386 |
|
|---|
| 387 |
void putpixel(int x, int y, uint col) { |
|---|
| 388 |
auto transformed=display.tl+pt(x, y); |
|---|
| 389 |
with (transformed) if (x<0 || y<0 || x!<display.surface.w || y!<display.surface.h) return; |
|---|
| 390 |
putpixel32(display.surface, transformed.tuple, col); |
|---|
| 391 |
} |
|---|
| 392 |
|
|---|
| 393 |
void blend(ubyte* rgba, ubyte nr, ubyte ng, ubyte nb, ubyte alpha) { |
|---|
| 394 |
rgba[0] = cast(ubyte) (rgba[0] + ((nr - rgba[0]) * alpha) / 255); |
|---|
| 395 |
rgba[1] = cast(ubyte) (rgba[1] + ((ng - rgba[1]) * alpha) / 255); |
|---|
| 396 |
rgba[2] = cast(ubyte) (rgba[2] + ((nb - rgba[2]) * alpha) / 255); |
|---|
| 397 |
} |
|---|
| 398 |
|
|---|
| 399 |
void putpixel(int x, int y, ubyte r, ubyte g, ubyte b) { |
|---|
| 400 |
putpixel(x, y, SDL_MapRGBA(display.surface.format, r, g, b, 0)); |
|---|
| 401 |
} |
|---|
| 402 |
void putpixel(int x, int y, ubyte[3] rgb, ubyte a) { |
|---|
| 403 |
ubyte[4] old_rgba; |
|---|
| 404 |
getpixel32(display.surface, display.tl.x + x, display.tl.y + y, &old_rgba); |
|---|
| 405 |
blend(old_rgba.ptr, rgb[0], rgb[1], rgb[2], a); |
|---|
| 406 |
putpixel(x, y, SDL_MapRGBA(display.surface.format, old_rgba[0], old_rgba[1], old_rgba[2], 0)); |
|---|
| 407 |
} |
|---|
| 408 |
|
|---|
| 409 |
const rgb White={[255, 255, 255]}; |
|---|
| 410 |
const rgb Black={[0, 0, 0]}; |
|---|
| 411 |
const rgb Red={[255, 0, 0]}; |
|---|
| 412 |
const rgb Green={[0, 255, 0]}; |
|---|
| 413 |
const rgb Blue={[0, 0, 255]}; |
|---|
| 414 |
const rgb Yellow={[255, 255, 0]}; |
|---|
| 415 |
const rgb Cyan={[0, 255, 255]}; |
|---|
| 416 |
const rgb Purple={[255, 0, 255]}; |
|---|
| 417 |
rgb color=White; |
|---|
| 418 |
rgb back=Black; |
|---|
| 419 |
|
|---|
| 420 |
template failfind(U, T...) { |
|---|
| 421 |
static if (T.length) |
|---|
| 422 |
static if (is(T[0] == U)) static assert(false, "Duplicate "~U.stringof~" found!"); |
|---|
| 423 |
else const bool failfind=failfind!(U, T[1..$]); |
|---|
| 424 |
else |
|---|
| 425 |
const bool failfind=true; |
|---|
| 426 |
} |
|---|
| 427 |
|
|---|
| 428 |
template Select(U, T...) { |
|---|
| 429 |
static if(T.length) |
|---|
| 430 |
static if (is(U == T[0])) { static if (failfind!(U, T[1..$])) { }; const int Select = 0; } |
|---|
| 431 |
else |
|---|
| 432 |
static if (Select!(U, T[1..$]) != -1) |
|---|
| 433 |
const int Select = 1 + Select!(U, T[1..$]); |
|---|
| 434 |
else |
|---|
| 435 |
const int Select = -1; |
|---|
| 436 |
else |
|---|
| 437 |
const int Select = -1; |
|---|
| 438 |
} |
|---|
| 439 |
|
|---|
| 440 |
typedef rgb back_rgb; |
|---|
| 441 |
back_rgb Back(rgb r) { return cast(back_rgb) r; } |
|---|
| 442 |
back_rgb Back() { return cast(back_rgb) back; } |
|---|
| 443 |
typedef rgb box_rgb; |
|---|
| 444 |
box_rgb Box(rgb r) { return cast(box_rgb) r; } |
|---|
| 445 |
box_rgb Box() { return cast(box_rgb) color; } |
|---|
| 446 |
alias Back Fill; |
|---|
| 447 |
|
|---|
| 448 |
bool doFlip=false; |
|---|
| 449 |
//void flip() { if (!screen.offscreen) SDL_Flip(display.surface); } |
|---|
| 450 |
void flip() { if (!screen.offscreen) SDL_UpdateRect(display.surface); } |
|---|
| 451 |
void flip(bool target) { doFlip=target; } |
|---|
| 452 |
scope class groupDraws { |
|---|
| 453 |
bool wasOn; |
|---|
| 454 |
this() { wasOn=doFlip; flip=false; } |
|---|
| 455 |
~this() { if (wasOn) { flip=true; flip; } } |
|---|
| 456 |
} |
|---|
| 457 |
|
|---|
| 458 |
void execParams(T...)(T params) { |
|---|
| 459 |
const int bcol=Select!(back_rgb, T); |
|---|
| 460 |
static if (bcol != -1) back=cast(rgb) params[bcol]; |
|---|
| 461 |
const int col=Select!(rgb, T); |
|---|
| 462 |
static if (col != -1) color=params[col]; |
|---|
| 463 |
//else static if (bcol != -1) color=back; |
|---|
| 464 |
const int boxcol=Select!(box_rgb, T); |
|---|
| 465 |
static if (boxcol != -1) color=cast(rgb) params[boxcol]; |
|---|
| 466 |
} |
|---|
| 467 |
|
|---|
| 468 |
void tintfill(int x1, int y1, int x2, int y2, rgb color) { |
|---|
| 469 |
SDL_LockSurface(display.surface); |
|---|
| 470 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) flip; } |
|---|
| 471 |
// ubyte* c; |
|---|
| 472 |
ubyte[4] c; |
|---|
| 473 |
x1+=display.tl.x; x2+=display.tl.x; |
|---|
| 474 |
y1+=display.tl.y; y2+=display.tl.y; |
|---|
| 475 |
for (int x=x1; x<x2; ++x) { |
|---|
| 476 |
for (int y=y1; y<y2; ++y) { |
|---|
| 477 |
getpixel32(display.surface, x, y, &c); |
|---|
| 478 |
c[0]=cast(ubyte)((c[0]*178+color.r*77)>>8); |
|---|
| 479 |
c[1]=cast(ubyte)((c[1]*178+color.g*77)>>8); |
|---|
| 480 |
c[2]=cast(ubyte)((c[2]*178+color.b*77)>>8); |
|---|
| 481 |
putpixel32(display.surface, x, y, SDL_MapRGBA(display.surface.format, c[0], c[1], c[2], c[3])); |
|---|
| 482 |
} |
|---|
| 483 |
} |
|---|
| 484 |
} |
|---|
| 485 |
|
|---|
| 486 |
int lastx, lasty; |
|---|
| 487 |
|
|---|
| 488 |
void pset(T...)(T params) { |
|---|
| 489 |
static if (T.length && is(T[0] == pt)) { |
|---|
| 490 |
auto x = params[0].x, y = params[0].y; |
|---|
| 491 |
const int RO = 1; |
|---|
| 492 |
} else { |
|---|
| 493 |
auto x = cast(int) params[0], y = cast(int) params[1]; |
|---|
| 494 |
const int RO = 2; |
|---|
| 495 |
} |
|---|
| 496 |
lastx=cast(int) x; lasty=cast(int) y; |
|---|
| 497 |
SDL_LockSurface(display.surface); |
|---|
| 498 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) flip; } |
|---|
| 499 |
execParams(params[RO..$]); |
|---|
| 500 |
if (x<0 || x!<screen.w || y<0 || y!<screen.h) return; |
|---|
| 501 |
with (color) putpixel32( |
|---|
| 502 |
display.surface, x, y, |
|---|
| 503 |
SDL_MapRGBA(display.surface.format, values[0], values[1], values[2], 0) |
|---|
| 504 |
); |
|---|
| 505 |
} |
|---|
| 506 |
|
|---|
| 507 |
rgb pget(T...)(T t) { |
|---|
| 508 |
static assert (T.length == 2); |
|---|
| 509 |
static assert (is(typeof(cast(int) t[0])) && is(typeof(cast(int) t[1]))); |
|---|
| 510 |
auto x = cast(int) t[0], y = cast(int) t[1]; |
|---|
| 511 |
SDL_LockSurface(display.surface); |
|---|
| 512 |
scope(exit) SDL_UnlockSurface(display.surface); |
|---|
| 513 |
// ubyte* c; |
|---|
| 514 |
ubyte[4] c; |
|---|
| 515 |
getpixel32(display.surface, (pt(x, y)+display.tl).tuple, &c); |
|---|
| 516 |
rgb res; res.values[]=c[0..3]; return res; |
|---|
| 517 |
} |
|---|
| 518 |
|
|---|
| 519 |
void bresenham(bool countUp=true, bool steep=false)(int x0, int y0, int x1, int y1) { |
|---|
| 520 |
auto deltax = x1 - x0, deltay = y1 - y0; |
|---|
| 521 |
static if (steep) { |
|---|
| 522 |
auto Δerror = cast(float)deltax / cast(float)deltay; |
|---|
| 523 |
auto var2 = x0; |
|---|
| 524 |
const string name="y"; |
|---|
| 525 |
} else { |
|---|
| 526 |
auto Δerror = cast(float)deltay / cast(float)deltax; |
|---|
| 527 |
auto var2 = y0; |
|---|
| 528 |
const string name="x"; |
|---|
| 529 |
} |
|---|
| 530 |
auto error = 0f; |
|---|
| 531 |
uint col=SDL_MapRGBA(display.surface.format, color.values[0], color.values[1], color.values[2], 0); |
|---|
| 532 |
for (auto var1 = mixin(name~'0'); var1 <= mixin(name~'1'); ++var1) { |
|---|
| 533 |
static if (steep) putpixel(var2, var1, col); |
|---|
| 534 |
else putpixel(var1, var2, col); |
|---|
| 535 |
error += Δerror; |
|---|
| 536 |
if (abs(error) >= 1f) { static if (countUp) { var2++; error -= 1f; } else { var2--; error += 1f; }} |
|---|
| 537 |
} |
|---|
| 538 |
} |
|---|
| 539 |
|
|---|
| 540 |
ubyte[256] square_map; // 0..255 == 0..1 |
|---|
| 541 |
static this() { |
|---|
| 542 |
for (int i = 0; i < 256; ++i) { |
|---|
| 543 |
float as_float = i / 256f; |
|---|
| 544 |
square_map[i] = cast(ubyte) (pow(as_float, 0.5f) * 256); |
|---|
| 545 |
} |
|---|
| 546 |
} |
|---|
| 547 |
|
|---|
| 548 |
void bresenham_aa(bool countUp=true, bool steep=false)(int x0, int y0, int x1, int y1) { |
|---|
| 549 |
auto deltax = x1 - x0, deltay = y1 - y0; |
|---|
| 550 |
static if (steep) { |
|---|
| 551 |
auto Δerror = cast(float)deltax / cast(float)deltay; |
|---|
| 552 |
auto var2 = x0; |
|---|
| 553 |
const string name="y"; |
|---|
| 554 |
} else { |
|---|
| 555 |
auto Δerror = cast(float)deltay / cast(float)deltax; |
|---|
| 556 |
auto var2 = y0; |
|---|
| 557 |
const string name="x"; |
|---|
| 558 |
} |
|---|
| 559 |
auto error = 0f; |
|---|
| 560 |
for (auto var1 = mixin(name~'0'); var1 <= mixin(name~'1'); ++var1) { |
|---|
| 561 |
// auto alpha = cast(ubyte) (error*255), anti_alpha = 255 - alpha; |
|---|
| 562 |
auto alpha = square_map[cast(ubyte) (error * 255)], |
|---|
| 563 |
anti_alpha = square_map[255 - cast(ubyte) (error * 255)]; |
|---|
| 564 |
static if (steep) { |
|---|
| 565 |
if (countUp) { |
|---|
| 566 |
if (var2 < 1 || var1 < 0 || var2 !< screen.w + 1 || var1 !< screen.h) continue; |
|---|
| 567 |
putpixel(var2-1, var1, color.values, anti_alpha); |
|---|
| 568 |
if (var2 < display.width) putpixel(var2, var1, color.values, alpha); |
|---|
| 569 |
} else { |
|---|
| 570 |
if (var2 < -1 || var1 < 0 || var2 !< screen.w - 1 || var1 !< screen.h) continue; |
|---|
| 571 |
putpixel(var2+1, var1, color.values, alpha); |
|---|
| 572 |
if (var2 > 0) putpixel(var2, var1, color.values, anti_alpha); |
|---|
| 573 |
} |
|---|
| 574 |
} else { |
|---|
| 575 |
if (countUp) { |
|---|
| 576 |
if (var1 < 0 || var2 < 0 || var1 !< screen.w || var2 !< screen.h) continue; |
|---|
| 577 |
putpixel(var1, var2, color.values, anti_alpha); |
|---|
| 578 |
if (var2 < display.height) putpixel(var1, var2+1, color.values, alpha); |
|---|
| 579 |
} else { |
|---|
| 580 |
if (var1 < 0 || var2 < -1 || var1 !< screen.w || var2 !< screen.h) continue; |
|---|
| 581 |
putpixel(var1, var2+1, color.values, alpha); |
|---|
| 582 |
if (var2 > 0) putpixel(var1, var2, color.values, anti_alpha); |
|---|
| 583 |
} |
|---|
| 584 |
} |
|---|
| 585 |
error += Δerror; |
|---|
| 586 |
if (abs(error) >= 1f) { static if (countUp) { var2++; error -= 1f; } else { var2--; error += 1f; }} |
|---|
| 587 |
} |
|---|
| 588 |
} |
|---|
| 589 |
|
|---|
| 590 |
template _UnPoint(int curOffs, T...) { |
|---|
| 591 |
static if (T.length) { |
|---|
| 592 |
alias _UnPoint!(curOffs + 1, T[1..$]) rest; |
|---|
| 593 |
static if (is(T[0] == pt)) { |
|---|
| 594 |
alias Tuple!(int, int, rest.tuple) tuple; |
|---|
| 595 |
const string value = "%["~ctToString(curOffs)~"].x, %["~ctToString(curOffs)~"].y" |
|---|
| 596 |
~ (rest.value.length?(", "~rest.value):rest.value); |
|---|
| 597 |
} else { |
|---|
| 598 |
alias Tuple!(T[0], rest.tuple) tuple; |
|---|
| 599 |
const string value = "%["~ctToString(curOffs)~"]" |
|---|
| 600 |
~ (rest.value.length?(", "~rest.value):rest.value); |
|---|
| 601 |
} |
|---|
| 602 |
} else { |
|---|
| 603 |
alias Tuple!() tuple; |
|---|
| 604 |
const string value = ""; |
|---|
| 605 |
} |
|---|
| 606 |
} |
|---|
| 607 |
|
|---|
| 608 |
template UnPoint(T...) { mixin _UnPoint!(0, T); } |
|---|
| 609 |
|
|---|
| 610 |
bool hasnan(T...)(T t) { |
|---|
| 611 |
foreach (value; t) |
|---|
| 612 |
static if (is(typeof(value): real)) if (isnan(cast(real) value)) return true; |
|---|
| 613 |
return false; |
|---|
| 614 |
} |
|---|
| 615 |
|
|---|
| 616 |
void line(_T...)(_T _p) { |
|---|
| 617 |
if (hasnan(_p)) return; |
|---|
| 618 |
alias UnPoint!(_T).tuple T; |
|---|
| 619 |
auto p = mixin("stuple("~ctReplace(UnPoint!(_T).value, "%", "_p")~")"); |
|---|
| 620 |
static if (T.length>=3 && (is(T[2]: long)||is(T[2]: real))) { |
|---|
| 621 |
_line(cast(int)p._0, cast(int)p._1, cast(int)p._2, cast(int)p._3, p.tupleof[4..$]); |
|---|
| 622 |
lastx=cast(int)p._2; lasty=cast(int)p._3; |
|---|
| 623 |
} else { |
|---|
| 624 |
_line(lastx, lasty, cast(int)p._0, cast(int)p._1, p.tupleof[2..$]); |
|---|
| 625 |
lastx=cast(int)p._0; lasty=cast(int)p._1; |
|---|
| 626 |
} |
|---|
| 627 |
} |
|---|
| 628 |
|
|---|
| 629 |
bool aa = true; |
|---|
| 630 |
|
|---|
| 631 |
void _line(T...)(int x0, int y0, int x1, int y1, T p) { |
|---|
| 632 |
static int max(int a, int b) { return a>b?a:b; } |
|---|
| 633 |
static int min(int a, int b) { return a<b?a:b; } |
|---|
| 634 |
execParams(p); |
|---|
| 635 |
void updateRect() { |
|---|
| 636 |
SDL_UpdateRect(display.surface, min(x0, x1), min(y0, y1), max(x0, x1)-min(x0, x1), max(y0, y1)-min(y0, y1)); |
|---|
| 637 |
} |
|---|
| 638 |
static if (Select!(back_rgb, T)!=-1) { |
|---|
| 639 |
SDL_LockSurface(display.surface); |
|---|
| 640 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) updateRect; } |
|---|
| 641 |
SDL_Rect rect; |
|---|
| 642 |
with (rect) { |
|---|
| 643 |
x=cast(short) min(x0, x1); y=cast(short) min(y0, y1); |
|---|
| 644 |
w=cast(ushort) (max(x0, x1)-min(x0, x1)); |
|---|
| 645 |
h=cast(ushort) (max(y0, y1)-min(y0, y1)); |
|---|
| 646 |
} |
|---|
| 647 |
rect.x+=display.tl.x; rect.y+=display.tl.y; |
|---|
| 648 |
with (back) SDL_FillRect(display.surface, &rect, SDL_MapRGBA(display.surface.format, values[0], values[1], values[2], 0)); |
|---|
| 649 |
} |
|---|
| 650 |
static if (Select!(box_rgb, T)!=-1) { |
|---|
| 651 |
SDL_LockSurface(display.surface); |
|---|
| 652 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) updateRect; } |
|---|
| 653 |
uint col; |
|---|
| 654 |
auto box = p[Select!(box_rgb, T)]; |
|---|
| 655 |
col = SDL_MapRGBA(display.surface.format, box.values[0], box.values[1], box.values[2], 0); |
|---|
| 656 |
if (x1 < x0) swap(x0, x1); |
|---|
| 657 |
if (y1 < y0) swap(y0, y1); |
|---|
| 658 |
hline(x0, y0, x1 - x0, col); |
|---|
| 659 |
hline(x0, y1, x1 - x0, col); |
|---|
| 660 |
vline(x0, y0, y1 - y0, col); |
|---|
| 661 |
vline(x1, y0, y1 - y0, col); |
|---|
| 662 |
} |
|---|
| 663 |
static if (Select!(box_rgb, T)+Select!(back_rgb, T)==-2) { |
|---|
| 664 |
SDL_LockSurface(display.surface); |
|---|
| 665 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) updateRect; } |
|---|
| 666 |
bool steep = abs(y1 - y0) > abs(x1 - x0); |
|---|
| 667 |
void turn() { swap(x0, x1); swap(y0, y1); } |
|---|
| 668 |
if (steep) { if (y1 < y0) turn; } |
|---|
| 669 |
else { if (x1 < x0) turn; } |
|---|
| 670 |
bool stepUp=steep ? (x0 < x1) : (y0 < y1); |
|---|
| 671 |
mixin(IfBranch!( |
|---|
| 672 |
"aa", "$1", "_aa", "", "steep", "$2", "true", "false", "stepUp", "$3", "true", "false", "bresenham$1!($3, $2)(x0, y0, x1, y1); " |
|---|
| 673 |
)); |
|---|
| 674 |
} |
|---|
| 675 |
} |
|---|
| 676 |
|
|---|
| 677 |
template circle_bresenham_pass(bool first) { |
|---|
| 678 |
const string xy=(first?"x":"y"); |
|---|
| 679 |
const string yx=(first?"y":"x"); |
|---|
| 680 |
const string str=" |
|---|
| 681 |
auto x="~(first?"radius":"0")~"; |
|---|
| 682 |
auto y="~(first?"0":"radius")~"; |
|---|
| 683 |
auto xchange=radius*radius*"~(first?"(1-2*radius)":"1")~"; |
|---|
| 684 |
auto ychange=radius*radius*"~(first?"1":"(1-2*radius)")~"; |
|---|
| 685 |
auto error=0; |
|---|
| 686 |
auto stopx="~(first?"y2square*radius":"0")~"; |
|---|
| 687 |
auto stopy="~(first?"0":"x2square*radius")~"; |
|---|
| 688 |
while (stopx"~(first?">=":"<=")~"stopy) { |
|---|
| 689 |
putpixel(cast(int) (cx+x), cast(int) (cy+y), col); |
|---|
| 690 |
putpixel(cast(int) (cx+x), cast(int) (cy-y), col); |
|---|
| 691 |
putpixel(cast(int) (cx-x), cast(int) (cy+y), col); |
|---|
| 692 |
putpixel(cast(int) (cx-x), cast(int) (cy-y), col); |
|---|
| 693 |
"~yx~"++; |
|---|
| 694 |
stop"~yx~"+="~xy~"2square; |
|---|
| 695 |
error+="~yx~"change; |
|---|
| 696 |
"~yx~"change+="~xy~"2square; |
|---|
| 697 |
if ((2*error+"~xy~"change)>0) { |
|---|
| 698 |
--"~xy~"; |
|---|
| 699 |
stop"~xy~"-="~yx~"2square; |
|---|
| 700 |
error+="~xy~"change; |
|---|
| 701 |
"~xy~"change+="~yx~"2square; |
|---|
| 702 |
} |
|---|
| 703 |
} |
|---|
| 704 |
"; |
|---|
| 705 |
} |
|---|
| 706 |
|
|---|
| 707 |
void hline(pt p, int w, rgb col) { |
|---|
| 708 |
hline(p.x, p.y, w, SDL_MapRGBA(display.surface.format, col.tuple, 0)); |
|---|
| 709 |
} |
|---|
| 710 |
|
|---|
| 711 |
void hline(int x, int y, int w, uint col) { |
|---|
| 712 |
if (alpha) { |
|---|
| 713 |
// ubyte* cur_col; |
|---|
| 714 |
ubyte[4] cur_col; |
|---|
| 715 |
auto color = (cast(ubyte*) &col)[0..4]; |
|---|
| 716 |
for (int pos=x; pos<=x+w; ++pos) { |
|---|
| 717 |
getpixel32(display.surface, pos, y, &cur_col); |
|---|
| 718 |
color[0]=cast(ubyte)((cur_col[0]*alpha+color[0]*(255-alpha))>>8); |
|---|
| 719 |
color[1]=cast(ubyte)((cur_col[1]*alpha+color[1]*(255-alpha))>>8); |
|---|
| 720 |
color[2]=cast(ubyte)((cur_col[2]*alpha+color[2]*(255-alpha))>>8); |
|---|
| 721 |
putpixel32(display.surface, pos, y, SDL_MapRGBA(display.surface.format, color[0], color[1], color[2], color[3])); |
|---|
| 722 |
} |
|---|
| 723 |
} else { |
|---|
| 724 |
for (int pos=x; pos<=x+w; ++pos) putpixel(pos, y, col); |
|---|
| 725 |
} |
|---|
| 726 |
} |
|---|
| 727 |
|
|---|
| 728 |
void vline(int x, int y, int h, uint col) { |
|---|
| 729 |
for (int pos=y; pos<=y+h; ++pos) putpixel(x, pos, col); |
|---|
| 730 |
} |
|---|
| 731 |
|
|---|
| 732 |
void circle(T...)(T t) { |
|---|
| 733 |
static assert(T.length!<3, "Circle: Needs x, y and radius"); |
|---|
| 734 |
int cx=cast(int)t[0], cy=cast(int)t[1]; float radius=cast(float)t[2]; |
|---|
| 735 |
SDL_LockSurface(display.surface); |
|---|
| 736 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) flip; } |
|---|
| 737 |
execParams(t[3..$]); |
|---|
| 738 |
if (radius!>0) return; |
|---|
| 739 |
static if (T.length>3 && is(T[3]: int)) yradius=t[3]; |
|---|
| 740 |
static if (Select!(back_rgb, T) != -1) { |
|---|
| 741 |
auto back_sdl=SDL_MapRGBA(display.surface.format, back.values[0], back.values[1], back.values[2], 0); |
|---|
| 742 |
for (int i=0; i<=radius; ++i) { |
|---|
| 743 |
ushort j=cast(ushort)(sqrt(cast(real)(radius*radius-i*i))); |
|---|
| 744 |
hline(cx-j, cy+i, 2*j, back_sdl); |
|---|
| 745 |
if (i) hline(cx-j, cy-i, 2*j, back_sdl); |
|---|
| 746 |
} |
|---|
| 747 |
} |
|---|
| 748 |
static if (Select!(back_rgb, T) == -1 || Select!(rgb, T) != -1) { |
|---|
| 749 |
auto x2square=2*radius*radius, y2square=x2square; |
|---|
| 750 |
uint col=SDL_MapRGBA(display.surface.format, color.values[0], color.values[1], color.values[2], 0); |
|---|
| 751 |
{ mixin(circle_bresenham_pass!(true).str); } |
|---|
| 752 |
{ mixin(circle_bresenham_pass!(false).str); } |
|---|
| 753 |
} |
|---|
| 754 |
} |
|---|
| 755 |
|
|---|
| 756 |
float distance(float x1, float y1, float x2, float y2) { |
|---|
| 757 |
auto x=x1-x2, y=y1-y2; |
|---|
| 758 |
return sqrt(x*x+y*y); |
|---|
| 759 |
} |
|---|
| 760 |
|
|---|
| 761 |
struct floodfill_node { |
|---|
| 762 |
int x, y; |
|---|
| 763 |
static floodfill_node opCall(int x, int y) { |
|---|
| 764 |
floodfill_node res; |
|---|
| 765 |
res.x=x; res.y=y; |
|---|
| 766 |
return res; |
|---|
| 767 |
} |
|---|
| 768 |
} |
|---|
| 769 |
|
|---|
| 770 |
void paint(T...)(int x, int y, T t) { |
|---|
| 771 |
SDL_LockSurface(display.surface); |
|---|
| 772 |
scope(exit) { SDL_UnlockSurface(display.surface); if (doFlip) flip; } |
|---|
| 773 |
execParams(t); |
|---|
| 774 |
bool border=true; |
|---|
| 775 |
if (Select!(back_rgb, T) == -1) { |
|---|
| 776 |
back=pget(x, y); |
|---|
| 777 |
border=false; |
|---|
| 778 |
} |
|---|
| 779 |
bool check(rgb r) { |
|---|
| 780 |
if (border) return (r != back) && (r != color); |
|---|
| 781 |
else return r == back; |
|---|
| 782 |
} |
|---|
| 783 |
if (back == color) throw new Exception("Having identical backgrounds and foregrounds will severely mess up floodfill."); |
|---|
| 784 |
alias floodfill_node node; |
|---|
| 785 |
node[] queue; |
|---|
| 786 |
queue ~= node(x, y); |
|---|
| 787 |
size_t count=0; |
|---|
| 788 |
uint col=SDL_MapRGBA(display.surface.format, color.values[0], color.values[1], color.values[2], 0); |
|---|
| 789 |
while (count<queue.length) { |
|---|
| 790 |
scope(exit) count++; |
|---|
| 791 |
with (queue[count]) { |
|---|
| 792 |
if (check(pget(x, y))) { |
|---|
| 793 |
int w=x, e=x; |
|---|
| 794 |
if (w<display.width) do w++; while ((w<display.width) && check(pget(w, y))); |
|---|
| 795 |
if (e>=0) do e--; while (e>=0 && check(pget(e, y))); |
|---|
| 796 |
for (int i=e+1; i<w; ++i) { |
|---|
| 797 |
putpixel(i, y, col); |
|---|
| 798 |
if (y && check(pget(i, y-1)) && ((i==w-1)||!check(pget(i+1, y-1)))) queue ~= node(i, y-1); |
|---|
| 799 |
if ((y < display.height-1) && check(pget(i, y+1)) && ((i==w-1)||!check(pget(i+1, y+1)))) queue ~= node(i, y+1); |
|---|
| 800 |
} |
|---|
| 801 |
} |
|---|
| 802 |
} |
|---|
| 803 |
} |
|---|
| 804 |
} |
|---|
| 805 |
|
|---|
| 806 |
bool opengl_mode; |
|---|
| 807 |
|
|---|
| 808 |
struct screen { |
|---|
| 809 |
static { |
|---|
| 810 |
bool offscreen=false; |
|---|
| 811 |
ParameterTypeTuple!(typeof(&SDL_SetVideoMode))[3] sdl_flags; |
|---|
| 812 |
void opCall(int w, int h, typeof(sdl_flags) flags=0, bool osc=false) { |
|---|
| 813 |
sdl_flags=flags; |
|---|
| 814 |
screen.resize(w, h, osc); |
|---|
| 815 |
} |
|---|
| 816 |
void resize(int w, int h, bool osc = false) { |
|---|
| 817 |
if (display && display.surface && !offscreen && !osc) { |
|---|
| 818 |
SDL_FreeSurface(display.surface); // we're just changing size |
|---|
| 819 |
} |
|---|
| 820 |
if (opengl_mode && !offscreen) { display.width = w; display.height = h; return; } |
|---|
| 821 |
offscreen = osc; |
|---|
| 822 |
if (offscreen) { |
|---|
| 823 |
auto surf = SDL_CreateRGBSurface(sdl_flags, w, h, 32); |
|---|
| 824 |
display = Area(RefSurf(surf)); |
|---|
| 825 |
} else { |
|---|
| 826 |
display=Area(RefSurf(SDL_SetVideoMode(w, h, 32, sdl_flags))); |
|---|
| 827 |
static if (is(typeof(&setupDragDrop))) setupDragDrop(); |
|---|
| 828 |
} |
|---|
| 829 |
} |
|---|
| 830 |
void close() { SDL_QuitSubSystem(SDL_INIT_VIDEO); } |
|---|
| 831 |
int width() { return display.width; } |
|---|
| 832 |
int height() { return display.height; } |
|---|
| 833 |
alias width w; alias height h; |
|---|
| 834 |
struct WithAreaHolder(T...) { |
|---|
| 835 |
T t; |
|---|
| 836 |
Area opAssign(C)(lazy C callable) { |
|---|
| 837 |
void call() { |
|---|
| 838 |
static if (is(C==void)) callable(); |
|---|
| 839 |
else static if (is(C==void delegate())) callable()(); |
|---|
| 840 |
else static assert(false, "Cannot call: "~T.stringof); |
|---|
| 841 |
} |
|---|
| 842 |
static if (is(T == Tuple!(Area))) { |
|---|
| 843 |
auto backup=display; display=t[0]; scope(exit) display=backup; |
|---|
| 844 |
auto backup_flags=sdl_flags; scope(exit) sdl_flags=backup_flags; |
|---|
| 845 |
auto offs=offscreen; scope(exit) offscreen=offs; |
|---|
| 846 |
call(); return display; |
|---|
| 847 |
} else static if (is(T == Tuple!(int, int))) { |
|---|
| 848 |
auto backup=display, backup_flags=sdl_flags, old_osc=offscreen; |
|---|
| 849 |
scope(exit) { display=backup; sdl_flags=backup_flags; offscreen=old_osc; } |
|---|
| 850 |
screen(t[0], t[1], 0, true); |
|---|
| 851 |
call(); return display; |
|---|
| 852 |
} else static assert(false, "opAssign with "~T.stringof~" .. ?"); |
|---|
| 853 |
} |
|---|
| 854 |
} |
|---|
| 855 |
WithAreaHolder!(T) With(T...)(T t) { |
|---|
| 856 |
WithAreaHolder!(T) res = void; |
|---|
| 857 |
foreach (id, value; t) res.t[id] = value; |
|---|
| 858 |
return res; |
|---|
| 859 |
} |
|---|
| 860 |
} |
|---|
| 861 |
} |
|---|
| 862 |
|
|---|
| 863 |
void cls(rgb fill=Black) { line(0, 0, display.width, display.height, Fill=fill); } |
|---|
| 864 |
|
|---|
| 865 |
import tools.log; |
|---|
| 866 |
void updateMouse(int x, int y, ubyte button, int pushed) { |
|---|
| 867 |
auto b = cast(Button) button; |
|---|
| 868 |
if (mouse.pos != pt(x, y)) mouse._moved = true; |
|---|
| 869 |
mouse.pos = pt(x, y); |
|---|
| 870 |
if (pushed == 1) { |
|---|
| 871 |
mouse.down(b); |
|---|
| 872 |
} |
|---|
| 873 |
if (pushed == -1) { |
|---|
| 874 |
mouse.up(b); |
|---|
| 875 |
} |
|---|
| 876 |
} |
|---|
| 877 |
|
|---|
| 878 |
enum Button : ubyte { Left=1, Middle, Right, Extra1, Extra2 } |
|---|
| 879 |
|
|---|
| 880 |
struct _mouse { |
|---|
| 881 |
pt pos; |
|---|
| 882 |
int x() { return pos.x; } |
|---|
| 883 |
int y() { return pos.y; } |
|---|
| 884 |
bool pressed() { return pressed(Button.Left); } |
|---|
| 885 |
bool pressed(Button b) { return _pressed[b]; } |
|---|
| 886 |
bool pressed(Button b, Area a) { return pressed(b) && (pos in a); } |
|---|
| 887 |
bool pressed(Area a) { return pressed(Button.Left, a); } |
|---|
| 888 |
bool[256] _pressed, was_pressed, was_released; |
|---|
| 889 |
bool clicked() { return clicked(Button.Left); } |
|---|
| 890 |
bool clicked(Button which) { return was_pressed[which]; } |
|---|
| 891 |
bool clicked(Button which, Area a) { return (pos in a) && clicked(which); } |
|---|
| 892 |
bool clicked(Area a) { return clicked(Button.Left, a); } |
|---|
| 893 |
bool released() { return released(Button.Left); } |
|---|
| 894 |
bool released(Button which) { return was_released[which]; } |
|---|
| 895 |
bool released(Button which, Area a) { return (pos in a) && released(which); } |
|---|
| 896 |
bool released(Area a) { return released(Button.Left, a); } |
|---|
| 897 |
void down(Button b) { _pressed[b] = true; was_pressed[b] = true; } |
|---|
| 898 |
void up(Button b) { _pressed[b] = false; was_released[b] = true; } |
|---|
| 899 |
void events_reset() { foreach (ref b; was_pressed) b = false; foreach (ref b; was_released) b = false; } |
|---|
| 900 |
bool _moved; |
|---|
| 901 |
bool moved() { auto res = _moved; _moved = false; return res; } |
|---|
| 902 |
} |
|---|
| 903 |
|
|---|
| 904 |
_mouse mouse; |
|---|
| 905 |
|
|---|
| 906 |
bool[wchar] keystate; |
|---|
| 907 |
|
|---|
| 908 |
struct _key { |
|---|
| 909 |
bool[ushort] key_pressed; |
|---|
| 910 |
bool _pressed(bool PUSHED)(ushort which) { |
|---|
| 911 |
if (auto kp = which in key_pressed) { |
|---|
| 912 |
auto res = *kp; |
|---|
| 913 |
static if (!PUSHED) *kp = false; |
|---|
| 914 |
return res; |
|---|
| 915 |
} else return false; |
|---|
| 916 |
} |
|---|
| 917 |
alias _pressed!(false) pressed; |
|---|
| 918 |
alias _pressed!(true) pushed; |
|---|
| 919 |
} |
|---|
| 920 |
|
|---|
| 921 |
_key key; |
|---|
| 922 |
|
|---|
| 923 |
void delegate() onResize; |
|---|
| 924 |
|
|---|
| 925 |
int drop_type, drop_action; |
|---|
| 926 |
|
|---|
| 927 |
string _text_dropped; |
|---|
| 928 |
string textDropped() { |
|---|
| 929 |
auto res = _text_dropped; |
|---|
| 930 |
_text_dropped = null; |
|---|
| 931 |
return res; |
|---|
| 932 |
} |
|---|
| 933 |
|
|---|
| 934 |
bool accept_drop; |
|---|
| 935 |
|
|---|
| 936 |
import tools.time; |
|---|
| 937 |
static this() { SDL_EventState(SDL_EventType.SysWMEvent, true); } |
|---|
| 938 |
/// parameters to key: which key, and if it's pressed or released |
|---|
| 939 |
/// parameters to mouse: x, y, button [Left: 1, Mid: 2, Right: 3], change [1: pressed, -1: released] |
|---|
| 940 |
void events(void delegate(ushort, bool) key=null, void delegate(int, int, ubyte, int) mouse=null, void delegate() quit=null) { |
|---|
| 941 |
.mouse.events_reset; |
|---|
| 942 |
SDL_Event evt; |
|---|
| 943 |
while (SDL_PollEvent(&evt)) { |
|---|
| 944 |
switch (evt.type) { |
|---|
| 945 |
case SDL_EventType.MouseMotion: |
|---|
| 946 |
with (evt.motion) { |
|---|
| 947 |
updateMouse(x, y, 0, 0); |
|---|
| 948 |
if (mouse) mouse(x, y, 0, 0); |
|---|
| 949 |
} |
|---|
| 950 |
break; |
|---|
| 951 |
case SDL_EventType.MouseButtonDown: |
|---|
| 952 |
with (evt.button) { |
|---|
| 953 |
updateMouse(x, y, button, 1); |
|---|
| 954 |
if (mouse) mouse(x, y, button, 1); |
|---|
| 955 |
} |
|---|
| 956 |
break; |
|---|
| 957 |
case SDL_EventType.MouseButtonUp: |
|---|
| 958 |
with (evt.button) { |
|---|
| 959 |
updateMouse(x, y, button, -1); |
|---|
| 960 |
if (mouse) mouse(x, y, button, -1); |
|---|
| 961 |
} |
|---|
| 962 |
break; |
|---|
| 963 |
case SDL_EventType.KeyDown: |
|---|
| 964 |
auto sym = evt.key.keysym.sym; |
|---|
| 965 |
logln("KeyDown: ", sym); |
|---|
| 966 |
.key.key_pressed[cast(ushort) sym] = true; |
|---|
| 967 |
if (key) key(cast(ushort) sym, true); |
|---|
| 968 |
break; |
|---|
| 969 |
case SDL_EventType.KeyUp: |
|---|
| 970 |
auto sym = evt.key.keysym.sym; |
|---|
| 971 |
.key.key_pressed[cast(ushort) sym] = false; |
|---|
| 972 |
if (key) key(cast(ushort) sym, false); |
|---|
| 973 |
break; |
|---|
| 974 |
case SDL_EventType.Resize: |
|---|
| 975 |
screen.resize(evt.resize.w, evt.resize.h); |
|---|
| 976 |
if (onResize) onResize(); |
|---|
| 977 |
break; |
|---|
| 978 |
case SDL_EventType.Quit: |
|---|
| 979 |
if (quit) quit(); |
|---|
| 980 |
else throw new Exception("Quit"); |
|---|
| 981 |
break; |
|---|
| 982 |
case SDL_EventType.SysWMEvent: |
|---|
| 983 |
version(win32) break; // assert(false, "No system events support for win32 yet"); |
|---|
| 984 |
else { // presume X11 |
|---|
| 985 |
with (evt.syswm.msg.xevent) { |
|---|
| 986 |
Atom atom(string name) { return XInternAtom(xclient.display, toStringz(name)); } |
|---|
| 987 |
string atomName(Atom atom) { return Format(XGetAtomName(xclient.display, atom)); } |
|---|
| 988 |
if (type == 33) { |
|---|
| 989 |
// writefln("Type: ", xclient.type); |
|---|
| 990 |
auto name = atomName(xclient.type); |
|---|
| 991 |
// writefln("Name: ", name); |
|---|
| 992 |
if (name == "XdndEnter") { |
|---|
| 993 |
auto source = xclient.data.l[0]; |
|---|
| 994 |
if (atomName(xclient.data.l[2]).startsWith("text/plain")) { |
|---|
| 995 |
accept_drop = true; |
|---|
| 996 |
// writefln("Drop accepted"); |
|---|
| 997 |
} |
|---|
| 998 |
drop_type = xclient.data.l[2]; |
|---|
| 999 |
} |
|---|
| 1000 |
if (!accept_drop) break; |
|---|
| 1001 |
if (name == "XdndPosition") { |
|---|
| 1002 |
XEvent drop_response; |
|---|
| 1003 |
drop_response.type = 33; |
|---|
| 1004 |
with (drop_response.xclient) { |
|---|
| 1005 |
display = xclient.display; |
|---|
| 1006 |
window = xclient.data.l[0]; // wtf? see wine source code, but why? |
|---|
| 1007 |
type = "XdndStatus".atom(); |
|---|
| 1008 |
format = 32; |
|---|
| 1009 |
data.l[0] = window; |
|---|
| 1010 |
data.l[1] = true; |
|---|
| 1011 |
data.l[2] = 0; |
|---|
| 1012 |
data.l[3] = 0; |
|---|
| 1013 |
drop_action = data.l[4] = xclient.data.l[4]; |
|---|
| 1014 |
} |
|---|
| 1015 |
auto res = XSendEvent(xclient.display, xclient.data.l[0], false, 0, &drop_response); |
|---|
| 1016 |
} |
|---|
| 1017 |
if (name == "XdndDrop") { |
|---|
| 1018 |
auto drop_timekey = xclient.data.l[2]; |
|---|
| 1019 |
auto res = XConvertSelection(xclient.display, "XdndSelection".atom(), |
|---|
| 1020 |
drop_type, "XdndTarget".atom(), getWindow, drop_timekey); |
|---|
| 1021 |
auto start = sec(); |
|---|
| 1022 |
while (sec() - start < 1.0) { |
|---|
| 1023 |
XEvent event; |
|---|
| 1024 |
auto check_res = XCheckTypedWindowEvent(xclient.display, getWindow, SelectionNotify, &event); |
|---|
| 1025 |
if (check_res && event.xselect.selection == "XdndSelection".atom()) goto done; |
|---|
| 1026 |
} |
|---|
| 1027 |
return; // timeout |
|---|
| 1028 |
done: |
|---|
| 1029 |
Atom act_type; uint bytes_left, items; int act_format; ubyte* data; |
|---|
| 1030 |
XGetWindowProperty(xclient.display, getWindow, "XdndTarget".atom(), 0, -1, true, |
|---|
| 1031 |
AnyPropertyType, &act_type, &act_format, &items, &bytes_left, &data); |
|---|
| 1032 |
items *= (act_format / 8); |
|---|
| 1033 |
auto my_data = Format(cast(char*) data).dup; |
|---|
| 1034 |
XFree(data); |
|---|
| 1035 |
XEvent finished; finished.type = 33; |
|---|
| 1036 |
with (finished.xclient) { |
|---|
| 1037 |
display = xclient.display; |
|---|
| 1038 |
window = getWindow(); |
|---|
| 1039 |
type = "XdndFinished".atom(); |
|---|
| 1040 |
format = 32; |
|---|
| 1041 |
data.l[0] = getWindow; |
|---|
| 1042 |
data.l[1] = true; |
|---|
| 1043 |
data.l[2] = drop_action; |
|---|
| 1044 |
} |
|---|
| 1045 |
XSendEvent(xclient.display, xclient.data.l[0], false, 0, &finished); |
|---|
| 1046 |
// writefln("Sent XdndFinished"); |
|---|
| 1047 |
_text_dropped = my_data; |
|---|
| 1048 |
accept_drop = false; |
|---|
| 1049 |
} |
|---|
| 1050 |
} |
|---|
| 1051 |
} |
|---|
| 1052 |
} |
|---|
| 1053 |
break; |
|---|
| 1054 |
case SDL_EventType.Active: break; |
|---|
| 1055 |
default: |
|---|
| 1056 |
break; |
|---|
| 1057 |
} |
|---|
| 1058 |
} |
|---|
| 1059 |
} |
|---|
| 1060 |
|
|---|
| 1061 |
void events(void delegate(ushort) key, void delegate(int, int, ubyte, int) mouse=null, void delegate() quit=null) { |
|---|
| 1062 |
events((ushort a, bool b) { |
|---|
| 1063 |
if (b) key(a); |
|---|
| 1064 |
}, mouse, quit); |
|---|
| 1065 |
} |
|---|
| 1066 |
|
|---|
| 1067 |
void events(void delegate(ushort) key, void delegate(int, int) mouse, void delegate() quit=null) { |
|---|
| 1068 |
events(key, (int x, int y, ubyte b, int p) { mouse(x, y); }, quit); |
|---|
| 1069 |
} |
|---|
| 1070 |
|
|---|
| 1071 |
void events(void delegate(ushort, bool) key, void delegate(int, int) mouse, void delegate() quit=null) { |
|---|
| 1072 |
events(key, (int x, int y, ubyte b, int p) { mouse(x, y); }, quit); |
|---|
| 1073 |
} |
|---|
| 1074 |
|
|---|
| 1075 |
struct pt { |
|---|
| 1076 |
int x, y; |
|---|
| 1077 |
static pt opCall(int i, int k) { pt res; res.x = i; res.y = k; return res; } |
|---|
| 1078 |
static pt opCall(int i) { pt res; res.x = res.y = i; return res; } |
|---|
| 1079 |
pt opAdd(pt other) { return pt(x+other.x, y+other.y); } |
|---|
| 1080 |
pt opAdd(int i) { return pt(x + i, y + i); } |
|---|
| 1081 |
void opAddAssign(pt other) { x+=other.x; y+=other.y; } |
|---|
| 1082 |
pt opSub(pt other) { return pt(x-other.x, y-other.y); } |
|---|
| 1083 |
pt opSub(int i) { return pt(x - i, y - i); } |
|---|
| 1084 |
void opSubAssign(pt other) { x-=other.x; y-=other.y; } |
|---|
| 1085 |
pt opDiv(pt other) { return pt(x/other.x, y/other.y); } |
|---|
| 1086 |
pt opDiv(int v) { return pt(x/v, y/v); } |
|---|
| 1087 |
pt opMod(pt other) { return pt(x%other.x, y%other.y); } |
|---|
| 1088 |
pt opMul(pt other) { return pt(x*other.x, y*other.y); } |
|---|
| 1089 |
pt opMul(float other) { return pt(cast(int) (x*other), cast(int) (y*other)); } |
|---|
| 1090 |
alias opMul opMul_r; |
|---|
| 1091 |
pt rotated(float angle) { |
|---|
| 1092 |
auto c = cos(angle), s = sin(angle); |
|---|
| 1093 |
return pt(cast(int) (x * c - y * s), cast(int) (x * s + y * c)); |
|---|
| 1094 |
} |
|---|
| 1095 |
string toString() { return Format("pt(", x, ", ", y, ")"); } |
|---|
| 1096 |
float length() { |
|---|
| 1097 |
return sqrt(0f + x*x + y*y); |
|---|
| 1098 |
} |
|---|
| 1099 |
float length(float f) { |
|---|
| 1100 |
auto factor = f / length; |
|---|
| 1101 |
x = cast(int) (x * factor); |
|---|
| 1102 |
y = cast(int) (y * factor); |
|---|
| 1103 |
return f; |
|---|
| 1104 |
} |
|---|
| 1105 |
bool inside(pt tl, pt size) { |
|---|
| 1106 |
return x > tl.x && y > tl.y && x < tl.x + size.x && y < tl.y + size.y; |
|---|
| 1107 |
} |
|---|
| 1108 |
} |
|---|
| 1109 |
|
|---|
| 1110 |
typedef int Align; |
|---|
| 1111 |
const Align Center=0, Left=1, Right=2, Top=4, Bottom=8; |
|---|
| 1112 |
string AlignToString(Align a) { |
|---|
| 1113 |
return "Alignment ["~["Center", "Left", "Right", "Left+Right"][a&3]~ |
|---|
| 1114 |
", "~["Center", "Top", "Bottom", "Top+Bottom"][(a>>2)&3]~"]"; |
|---|
| 1115 |
} |
|---|
| 1116 |
|
|---|
| 1117 |
Align alignto(pt from, pt to) { |
|---|
| 1118 |
Align res; |
|---|
| 1119 |
auto angle=atan2((to-from).y, (to-from).x); |
|---|
| 1120 |
int index=cast(int)((angle/(2*PI))*9); |
|---|
| 1121 |
while (index<0) index+=8; |
|---|
| 1122 |
return [Left, Left|Bottom, Bottom, Bottom|Right, Right, Right|Top, Top, Top|Left, Left][index]; |
|---|
| 1123 |
} |
|---|
| 1124 |
|
|---|
| 1125 |
/*import tools.tests; |
|---|
| 1126 |
unittest { |
|---|
| 1127 |
Assert("AlignmentFormattingTest", AlignToString(Left|Right|Top|Bottom)=="Alignment [Left+Right, Top+Bottom]"); |
|---|
| 1128 |
mustFail("InvalidAlignmentTest", validate(Left|Right|Top|Bottom)); |
|---|
| 1129 |
}*/ |
|---|
| 1130 |
|
|---|
| 1131 |
void validate(Align a) { |
|---|
| 1132 |
if (((a&Left)&&(a&Right)) || ((a&Top)&&(a&Bottom))) |
|---|
| 1133 |
throw new Exception("Invalid alignment: "~AlignToString(a)); |
|---|
| 1134 |
} |
|---|
| 1135 |
|
|---|
| 1136 |
struct RefSurf { |
|---|
| 1137 |
SDL_Surface* surface; |
|---|
| 1138 |
int refs; |
|---|
| 1139 |
static RefSurf* opCall(SDL_Surface* surf) { |
|---|
| 1140 |
auto res = new RefSurf; |
|---|
| 1141 |
res.surface = surf; |
|---|
| 1142 |
return res; |
|---|
| 1143 |
} |
|---|
| 1144 |
void lock() { |
|---|
| 1145 |
SDL_LockSurface(surface); |
|---|
| 1146 |
} |
|---|
| 1147 |
void unlock() { |
|---|
| 1148 |
SDL_UnlockSurface(surface); |
|---|
| 1149 |
} |
|---|
| 1150 |
} |
|---|
| 1151 |
|
|---|
| 1152 |
class Area { |
|---|
| 1153 |
RefSurf* surf; |
|---|
| 1154 |
SDL_Surface* surface() { return surf.surface; } |
|---|
| 1155 |
~this() { |
|---|
| 1156 |
if (!surf.surface) return; |
|---|
| 1157 |
if (--surf.refs) return; |
|---|
| 1158 |
SDL_FreeSurface(surf.surface); |
|---|
| 1159 |
} |
|---|
| 1160 |
pt tl, dimensions; |
|---|
| 1161 |
Area dup() { return Area(tl, dimensions, surf); } |
|---|
| 1162 |
pt br() { return tl+size; } |
|---|
| 1163 |
pt bl() { return pt(tl.x, tl.y + size.y); } |
|---|
| 1164 |
pt tr() { return pt(tl.x + size.x, tl.y); } |
|---|
| 1165 |
static Area opCall(pt _tl, pt _size, RefSurf* _rs=null) { |
|---|
| 1166 |
auto res=new Area; |
|---|
| 1167 |
res.tl=_tl; res.dimensions=_size; |
|---|
| 1168 |
if (!_rs) _rs = display.surf; |
|---|
| 1169 |
_rs.refs ++; |
|---|
| 1170 |
res.surf = _rs; |
|---|
| 1171 |
return res; |
|---|
| 1172 |
} |
|---|
| 1173 |
static Area opCall(RefSurf* _rs) { |
|---|
| 1174 |
auto res=new Area; |
|---|
| 1175 |
res.tl=pt(0, 0); |
|---|
| 1176 |
if (!_rs || !_rs.surface) throw new Exception("Invalid surface"); |
|---|
| 1177 |
res.dimensions=pt(_rs.surface.w, _rs.surface.h); |
|---|
| 1178 |
res.surf = _rs; |
|---|
| 1179 |
res.surf.refs ++; |
|---|
| 1180 |
return res; |
|---|
| 1181 |
} |
|---|
| 1182 |
static Area opCall(SDL_Surface* surf) { |
|---|
| 1183 |
return opCall(RefSurf(surf)); |
|---|
| 1184 |
} |
|---|
| 1185 |
bool opIn_r(T)(T vt) { return (vt.x>=tl.x && vt.x<=(tl.x+size.x)) && (vt.y>=tl.y && vt.y<=(tl.y+size.y)); } |
|---|
| 1186 |
Area select(int x, int y, int w=int.max, int h=int.max) { |
|---|
| 1187 |
if (w==int.max) w=size.x - x; |
|---|
| 1188 |
if (h==int.max) h=size.y - y; |
|---|
| 1189 |
return Area(tl+pt(x, y), pt(w, h), surf); |
|---|
| 1190 |
} |
|---|
| 1191 |
Area size(int w, int h) { return Area(tl, pt(w, h), surf); } |
|---|
| 1192 |
pt size() { return dimensions; } |
|---|
| 1193 |
// move does not check if end coordinates are actually valid |
|---|
| 1194 |
Area move(int x, int y) { return Area(tl+pt(x, y), size, surf); } |
|---|
| 1195 |
Area move(pt p) { return Area(tl+p, size, surf); } |
|---|
| 1196 |
creal locate(pt which, creal tl, creal br) { |
|---|
| 1197 |
creal local=(1f*(which.x-this.tl.x))/width + ((1f*(which.y-this.tl.y))/height)*1i; |
|---|
| 1198 |
return tl.re+(br.re-tl.re)*local.re + (tl.im+(br.im-tl.im)*local.im)*1i; |
|---|
| 1199 |
} |
|---|
| 1200 |
Area width(int w) { |
|---|
| 1201 |
if (w<0) { |
|---|
| 1202 |
auto new_size = pt(-w, size.y); |
|---|
| 1203 |
tl = br - pt(-w, height); |
|---|
| 1204 |
dimensions = new_size; |
|---|
| 1205 |
return this; |
|---|
| 1206 |
} else { |
|---|
| 1207 |
dimensions = pt(w, size.y); |
|---|
| 1208 |
return this; |
|---|
| 1209 |
} |
|---|
| 1210 |
} |
|---|
| 1211 |
//Area width(float wp) { return width(cast(int)(width*wp)); } |
|---|
| 1212 |
int width() { return dimensions.x; } |
|---|
| 1213 |
Area height(int h) { |
|---|
| 1214 |
if (h<0) { |
|---|
| 1215 |
auto new_size = pt(size.x, -h); |
|---|
| 1216 |
tl = br - pt(width, -h); |
|---|
| 1217 |
dimensions = new_size; |
|---|
| 1218 |
return this; |
|---|
| 1219 |
} else { |
|---|
| 1220 |
dimensions = pt(size.x, h); |
|---|
| 1221 |
return this; |
|---|
| 1222 |
} |
|---|
| 1223 |
} |
|---|
| 1224 |
//Area height(float hp) { return height(cast(int)(height*hp)); } |
|---|
| 1225 |
int height() { return dimensions.y; } |
|---|
| 1226 |
Area shrink(int s) { return select(s, s, width-2*s, height-2*s); } |
|---|
| 1227 |
Area grow(int s) { return shrink(-s); } |
|---|
| 1228 |
/// Select a sub-area in this area of size _size_ |
|---|
| 1229 |
Area select(pt size, Align where) { |
|---|
| 1230 |
validate(where); |
|---|
| 1231 |
pt tl2=([tl+pt((this.size.x-size.x)/2, 0), tl, tl+pt(this.size.x-size.x, 0)])[where&3]; |
|---|
| 1232 |
pt tl3=([tl2+pt(0, (this.size.y-size.y)/2), tl2, tl2+pt(0, this.size.y-size.y)])[where>>2]; |
|---|
| 1233 |
return Area(tl3, size, surf); |
|---|
| 1234 |
} |
|---|
| 1235 |
Area select(pt where, pt size, Align how) { |
|---|
| 1236 |
return Area(tl+where+pt([-size.x/2, -size.x, 0][how&3], [-size.y/2, -size.y, 0][how>>2]), size, surf); |
|---|
| 1237 |
} |
|---|
| 1238 |
void blit(Area what) { |
|---|
| 1239 |
auto srcrect=new SDL_Rect; |
|---|
| 1240 |
with (what) (*srcrect)=SDL_Rect(cast(short) tl.x, cast(short) tl.y, cast(ushort) size.x, cast(ushort) size.y); |
|---|
| 1241 |
auto dstrect=new SDL_Rect; |
|---|
| 1242 |
(*dstrect)=SDL_Rect(cast(short) tl.x, cast(short) tl.y, cast(ushort) size.x, cast(ushort) size.y); |
|---|
| 1243 |
SDL_BlitSurface(what.surface, srcrect, surface, dstrect); |
|---|
| 1244 |
} |
|---|
| 1245 |
void blit(Area what, Align how) { |
|---|
| 1246 |
select(what.size, how).blit(what); |
|---|
| 1247 |
} |
|---|
| 1248 |
void pset(pt pos, ubyte r, ubyte g, ubyte b) { |
|---|
| 1249 |
auto c = SDL_MapRGBA(surface.format, r, g, b, 0); |
|---|
| 1250 |
.putpixel32(surface, (tl+pos).tuple, c); |
|---|
| 1251 |
} |
|---|
| 1252 |
string toString() { |
|---|
| 1253 |
return Format("Area(", tl, ", size ", dimensions, ")"); |
|---|
| 1254 |
} |
|---|
| 1255 |
bool opEquals(Area a) { |
|---|
| 1256 |
if (!a) return false; |
|---|
| 1257 |
if (!this) return false; |
|---|
| 1258 |
return (surface==a.surface)&&(tl==a.tl)&&(dimensions==a.dimensions); |
|---|
| 1259 |
} |
|---|
| 1260 |
} |
|---|
| 1261 |
|
|---|
| 1262 |
union meep { rgb col; ubyte[4] ub; uint ui; } |
|---|
| 1263 |
|
|---|
| 1264 |
void blitblend(Area dest, Area src, rgb tint, ubyte blend = 255) { |
|---|
| 1265 |
if (dest.size != src.size) throw new Exception("can't blitblend different-sized areas"); |
|---|
| 1266 |
// clip dest to dest window |
|---|
| 1267 |
if (dest.br.x < 0 || dest.br.y < 0) return; // out of surface, top left |
|---|
| 1268 |
if (dest.tl.x !< dest.surface.w || dest.tl.y !< dest.surface.h) return; // out of surface, bottom right |
|---|
| 1269 |
src = src.dup; dest = dest.dup; |
|---|
| 1270 |
if (dest.tl.x < 0) { |
|---|
| 1271 |
src.tl.x += -dest.tl.x; src.dimensions.x -= -dest.tl.x; |
|---|
| 1272 |
dest.tl.x = 0; |
|---|
| 1273 |
} |
|---|
| 1274 |
if (dest.tl.y < 0) { |
|---|
| 1275 |
src.tl.y += -dest.tl.y; src.dimensions.y -= -dest.tl.y; |
|---|
| 1276 |
dest.tl.y = 0; |
|---|
| 1277 |
} |
|---|
| 1278 |
auto overhang_w = max(0, dest.br.x - dest.surface.w + 1); |
|---|
| 1279 |
if (overhang_w) { |
|---|
| 1280 |
src.dimensions.x -= overhang_w; |
|---|
| 1281 |
dest.dimensions.x -= overhang_w; |
|---|
| 1282 |
} |
|---|
| 1283 |
auto overhang_h = max(0, dest.br.y - dest.surface.h + 1); |
|---|
| 1284 |
if (overhang_h) { |
|---|
| 1285 |
src.dimensions.y -= overhang_h; |
|---|
| 1286 |
dest.dimensions.y -= overhang_h; |
|---|
| 1287 |
} |
|---|
| 1288 |
if (src.br.x < 0 || src.br.y < 0) throw new Exception("source surface: out of range (top/left)"); |
|---|
| 1289 |
if (src.tl.x !< src.surface.w || src.tl.y !< src.surface.h) |
|---|
| 1290 |
throw new Exception("source surface: out of range (bottom/right)"); |
|---|
| 1291 |
auto w = src.dimensions.x, h = src.dimensions.y; |
|---|
| 1292 |
auto srcxoffs = src.tl.x, srcyoffs = src.tl.y; |
|---|
| 1293 |
auto destxoffs = dest.tl.x, destyoffs = dest.tl.y; |
|---|
| 1294 |
for (int y = 0; y < h; ++y) { |
|---|
| 1295 |
for (int x = 0; x < w; ++x) { |
|---|
| 1296 |
meep src_rgba, dest_rgba; ubyte[4] a, b; // ubyte* a, b; |
|---|
| 1297 |
getpixel32(src.surface, x + srcxoffs, y + srcyoffs, &a); src_rgba.ui = *cast(uint*) a.ptr; |
|---|
| 1298 |
getpixel32(dest.surface, x + destxoffs, y + destyoffs, &b); dest_rgba.ui = *cast(uint*) b.ptr; |
|---|
| 1299 |
src_rgba.col *= tint; |
|---|
| 1300 |
src_rgba.ub[3] = cast(ubyte) ((src_rgba.ub[3] * blend) / 256); |
|---|
| 1301 |
meep res; |
|---|
| 1302 |
res.col = dest_rgba.col.blend(src_rgba.col, src_rgba.ub[3]); |
|---|
| 1303 |
res.ub[3] = max(src_rgba.ub[3], dest_rgba.ub[3]); |
|---|
| 1304 |
putpixel32(dest.surface, x + destxoffs, y + destyoffs, res.ui); |
|---|
| 1305 |
} |
|---|
| 1306 |
} |
|---|
| 1307 |
} |
|---|