root/trunk/qd/qd.d

Revision 779, 46.3 kB (checked in by FeepingCreature, 2 years ago)

pt silliness

Line 
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 }
Note: See TracBrowser for help on using the browser.