cosmetic fix
[amper.git] / egfx / base.d
blobd72fcb117fe427521531a0d3f86e949a3f7df7b9
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module egfx.base;
18 private:
20 import arsd.simpledisplay;
22 import iv.bclamp;
23 import iv.cmdcon;
26 // ////////////////////////////////////////////////////////////////////////// //
27 public uint c2img (in Color c) pure nothrow @safe @nogc {
28 pragma(inline, true);
29 return
30 ((c.asUint&0xff)<<16)|
31 (c.asUint&0x00ff00)|
32 ((c.asUint>>16)&0xff);
35 public uint c2img (uint c) pure nothrow @safe @nogc {
36 pragma(inline, true);
37 return
38 ((c&0xff)<<16)|
39 (c&0x00ff00)|
40 ((c>>16)&0xff);
43 public Color img2c (uint clr) pure nothrow @safe @nogc {
44 pragma(inline, true);
45 return Color((clr>>16)&0xff, (clr>>8)&0xff, clr&0xff);
49 // ////////////////////////////////////////////////////////////////////////// //
50 public struct GxSize {
51 public:
52 int width, height; ///
54 pure nothrow @safe @nogc:
55 this() (in auto ref GxSize p) { pragma(inline, true); width = p.width; height = p.height; } ///
56 this (int ax, int ay) { pragma(inline, true); width = ax; height = ay; } ///
57 void opAssign() (in auto ref GxSize p) { pragma(inline, true); width = p.width; height = p.height; } ///
58 bool opEquals() (in auto ref GxSize p) const { pragma(inline, true); return (p.width == width && p.height == height); } ///
59 ///
60 int opCmp() (in auto ref GxSize p) const {
61 pragma(inline, true);
62 if (auto d0 = height-p.height) return (d0 < 0 ? -1 : 1);
63 else if (auto d1 = width-p.width) return (d1 < 0 ? -1 : 1);
64 else return 0;
69 public struct GxPoint {
70 public:
71 int x, y; ///
73 pure nothrow @safe @nogc:
74 this() (in auto ref GxPoint p) { pragma(inline, true); x = p.x; y = p.y; } ///
75 this (int ax, int ay) { pragma(inline, true); x = ax; y = ay; } ///
76 void opAssign() (in auto ref GxPoint p) { pragma(inline, true); x = p.x; y = p.y; } ///
77 bool opEquals() (in auto ref GxPoint p) const { pragma(inline, true); return (p.x == x && p.y == y); } ///
78 ///
79 int opCmp() (in auto ref GxPoint p) const {
80 pragma(inline, true);
81 if (auto d0 = y-p.y) return (d0 < 0 ? -1 : 1);
82 else if (auto d1 = x-p.x) return (d1 < 0 ? -1 : 1);
83 else return 0;
88 public struct GxRect {
89 public:
90 int x0, y0; ///
91 int width = -1; // <0: invalid rect
92 int height = -1; // <0: invalid rect
94 alias left = x0; ///
95 alias top = y0; ///
96 alias right = x1; ///
97 alias bottom = y1; ///
99 ///
100 string toString () const @trusted nothrow {
101 if (valid) {
102 import core.stdc.stdio : snprintf;
103 char[128] buf = void;
104 return buf[0..snprintf(buf.ptr, buf.length, "(%d,%d)-(%d,%d)", x0, y0, x0+width-1, y0+height-1)].idup;
105 } else {
106 return "(invalid-rect)";
110 pure nothrow @safe @nogc:
112 this() (in auto ref GxRect rc) { pragma(inline, true); x0 = rc.x0; y0 = rc.y0; width = rc.width; height = rc.height; } ///
115 this (int ax0, int ay0, int awidth, int aheight) {
116 //pragma(inline, true);
117 x0 = ax0;
118 y0 = ay0;
119 width = awidth;
120 height = aheight;
124 this() (in auto ref GxPoint xy0, int awidth, int aheight) {
125 pragma(inline, true);
126 x0 = xy0.x;
127 y0 = xy0.y;
128 width = awidth;
129 height = aheight;
133 this() (in auto ref GxPoint xy0, in auto ref GxPoint xy1) {
134 pragma(inline, true);
135 x0 = xy0.x;
136 y0 = xy0.y;
137 width = xy1.x-xy0.x+1;
138 height = xy1.y-xy0.y+1;
141 void opAssign() (in auto ref GxRect rc) { pragma(inline, true); x0 = rc.x0; y0 = rc.y0; width = rc.width; height = rc.height; } ///
142 bool opEquals() (in auto ref GxRect rc) const { pragma(inline, true); return (rc.x0 == x0 && rc.y0 == y0 && rc.width == width && rc.height == height); } ///
144 int opCmp() (in auto ref GxRect p) const {
145 if (auto d0 = y0-rc.y0) return (d0 < 0 ? -1 : 1);
146 if (auto d1 = x0-rc.x0) return (d1 < 0 ? -1 : 1);
147 if (auto d2 = width*height-rc.width*rc.height) return (d2 < 0 ? -1 : 1);
148 return 0;
151 @property bool valid () const { pragma(inline, true); return (width >= 0 && height >= 0); } ///
152 @property bool invalid () const { pragma(inline, true); return (width < 0 || height < 0); } ///
153 @property bool empty () const { pragma(inline, true); return (width <= 0 || height <= 0); } /// invalid rects are empty
155 void invalidate () { pragma(inline, true); width = height = -1; } ///
157 @property GxPoint lefttop () const { pragma(inline, true); return GxPoint(x0, y0); } ///
158 @property GxPoint righttop () const { pragma(inline, true); return GxPoint(x0+width-1, y0); } ///
159 @property GxPoint leftbottom () const { pragma(inline, true); return GxPoint(x0, y0+height-1); } ///
160 @property GxPoint rightbottom () const { pragma(inline, true); return GxPoint(x0+width-1, y0+height-1); } ///
162 alias topleft = lefttop; ///
163 alias topright = righttop; ///
164 alias bottomleft = leftbottom; ///
165 alias bottomright = rightbottom; ///
167 @property int x1 () const { pragma(inline, true); return (width > 0 ? x0+width-1 : x0-1); } ///
168 @property int y1 () const { pragma(inline, true); return (height > 0 ? y0+height-1 : y0-1); } ///
170 @property void x1 (in int val) { pragma(inline, true); width = val-x0+1; } ///
171 @property void y1 (in int val) { pragma(inline, true); height = val-y0+1; } ///
173 GxPoint translateToGlobal() (in auto ref GxPoint lpt) const {
174 pragma(inline, true);
175 return GxPoint(lpt.x+x0, lpt.y+y0);
178 GxRect translateToGlobal() (in auto ref GxRect lrc) const {
179 pragma(inline, true);
180 return GxRect(lrc.x0+x0, lrc.y0+y0, lrc.width, lrc.height);
184 bool inside() (in auto ref GxPoint p) const {
185 pragma(inline, true);
186 return (width > 0 && height > 0 ? (p.x >= x0 && p.y >= y0 && p.x < x0+width && p.y < y0+height) : false);
189 /// ditto
190 bool inside (in int ax, in int ay) const {
191 pragma(inline, true);
192 return (width > 0 && height > 0 ? (ax >= x0 && ay >= y0 && ax < x0+width && ay < y0+height) : false);
195 /// is `r` inside `this`?
196 bool contains() (in auto ref GxRect r) const {
197 pragma(inline, true);
198 return
199 width > 0 && height > 0 &&
200 r.width > 0 && r.height > 0 &&
201 r.x0 >= x0 && r.y0 >= y0 &&
202 r.x0+r.width <= x0+width && r.y0+r.height <= y0+height;
205 /// is `r` and `this` overlaps?
206 bool overlaps() (in auto ref GxRect r) const {
207 pragma(inline, true);
208 return
209 width > 0 && height > 0 &&
210 r.width > 0 && r.height > 0 &&
211 x0 < r.x0+r.width && r.x0 < x0+width &&
212 y0 < r.y0+r.height && r.y0 < y0+height;
215 /// extend `this` so it will include `r`
216 void include() (in auto ref GxRect r) {
217 pragma(inline, true);
218 if (!r.empty) {
219 if (empty) {
220 x0 = r.x;
221 y0 = r.y;
222 width = r.width;
223 height = r.height;
224 } else {
225 if (r.x < x0) x0 = r.x0;
226 if (r.y < y0) y0 = r.y0;
227 if (r.x1 > x1) x1 = r.x1;
228 if (r.y1 > y1) y1 = r.y1;
233 /// clip `this` so it will not be larger than `r`
234 bool intersect() (in auto ref GxRect r) {
235 if (r.invalid || invalid) { width = height = -1; return false; }
236 if (r.empty || empty) { width = height = 0; return false; }
237 if (r.y1 < y0 || r.x1 < x0 || r.x0 > x1 || r.y0 > y1) { width = height = 0; return false; }
238 // rc is at least partially inside this rect
239 if (x0 < r.x0) x0 = r.x0;
240 if (y0 < r.y0) y0 = r.y0;
241 if (x1 > r.x1) x1 = r.x1;
242 if (y1 > r.y1) y1 = r.y1;
243 assert(!empty); // yeah, always
244 return true;
248 void shrinkBy (int dx, int dy) {
249 pragma(inline, true);
250 if ((dx || dy) && valid) {
251 x0 += dx;
252 y0 += dy;
253 width -= dx*2;
254 height -= dy*2;
259 void growBy (int dx, int dy) {
260 pragma(inline, true);
261 if ((dx || dy) && valid) {
262 x0 -= dx;
263 y0 -= dy;
264 width += dx*2;
265 height += dy*2;
270 void set (int ax0, int ay0, int awidth, int aheight) {
271 pragma(inline, true);
272 x0 = ax0;
273 y0 = ay0;
274 width = awidth;
275 height = aheight;
279 void moveLeftTopBy (int dx, int dy) {
280 pragma(inline, true);
281 x0 += dx;
282 y0 += dy;
283 width -= dx;
284 height -= dy;
287 alias moveTopLeftBy = moveLeftTopBy; /// ditto
290 void moveRightBottomBy (int dx, int dy) {
291 pragma(inline, true);
292 width += dx;
293 height += dy;
296 alias moveBottomRightBy = moveRightBottomBy; /// ditto
299 void moveBy (int dx, int dy) {
300 pragma(inline, true);
301 x0 += dx;
302 y0 += dy;
306 void moveTo (int nx, int ny) {
307 pragma(inline, true);
308 x0 = nx;
309 y0 = ny;
313 * clip (x,y,len) stripe to this rect
315 * Params:
316 * x = stripe start (not relative to rect)
317 * y = stripe start (not relative to rect)
318 * len = stripe length
320 * Returns:
321 * x = fixed x (invalid if result is false)
322 * len = fixed length (invalid if result is false)
323 * leftSkip = how much cells skipped at the left side (invalid if result is false)
324 * result = false if stripe is completely clipped out
326 * TODO:
327 * overflows
329 bool clipHStripe (ref int x, int y, ref int len, int* leftSkip=null) const @trusted {
330 if (empty) return false;
331 if (len <= 0 || y < y0 || y >= y0+height || x >= x0+width) return false;
332 if (x < x0) {
333 // left clip
334 if (x+len <= x0) return false;
335 immutable int dx = x0-x;
336 if (leftSkip !is null) *leftSkip = dx;
337 len -= dx;
338 x = x0;
339 assert(len > 0); // yeah, always
341 if (x+len > x0+width) {
342 // right clip
343 len = x0+width-x;
344 assert(len > 0); // yeah, always
346 return true;
350 * clip (x,y,hgt) stripe to this rect
352 * Params:
353 * x = stripe start (not relative to rect)
354 * y = stripe start (not relative to rect)
355 * hgt = stripe length
357 * Returns:
358 * y = fixed y (invalid if result is false)
359 * hgt = fixed length (invalid if result is false)
360 * topSkip = how much cells skipped at the top side (invalid if result is false)
361 * result = false if stripe is completely clipped out
363 * TODO:
364 * overflows
366 bool clipVStripe (int x, ref int y, ref int hgt, int* topSkip=null) const @trusted {
367 if (empty) return false;
368 if (hgt <= 0 || x < x0 || x >= x0+width || y >= y0+height) return false;
369 if (y < y0) {
370 // top clip
371 if (y+hgt <= y0) return false;
372 immutable int dy = y0-y;
373 if (topSkip !is null) *topSkip = dy;
374 hgt -= dy;
375 y = y0;
376 assert(hgt > 0); // yeah, always
378 if (y+hgt > y0+height) {
379 // bottom clip
380 hgt = y0+height-y;
381 assert(hgt > 0); // yeah, always
383 return true;
387 bool clipHVStripes (ref int x, ref int y, ref int wdt, ref int hgt, int* leftSkip=null, int* topSkip=null) const @trusted {
388 if (empty || wdt <= 0 || hgt <= 0) return false;
389 if (y >= y0+height || x >= x0+width) return false;
390 if (x < x0) {
391 // left clip
392 if (x+wdt <= x0) return false;
393 immutable int dx = x0-x;
394 if (leftSkip !is null) *leftSkip = dx;
395 wdt -= dx;
396 x = x0;
397 assert(wdt > 0); // yeah, always
399 if (x+wdt > x0+width) {
400 // right clip
401 wdt = x0+width-x;
402 assert(wdt > 0); // yeah, always
405 if (y < y0) {
406 // top clip
407 if (y+hgt <= y0) return false;
408 immutable int dy = y0-y;
409 if (topSkip !is null) *topSkip = dy;
410 hgt -= dy;
411 y = y0;
412 assert(hgt > 0); // yeah, always
414 if (y+hgt > y0+height) {
415 // bottom clip
416 hgt = y0+height-y;
417 assert(hgt > 0); // yeah, always
420 return true;
425 // ////////////////////////////////////////////////////////////////////////// //
426 public bool gxIsTransparent (uint clr) pure nothrow @safe @nogc { pragma(inline, true); return ((clr&0xff000000) == 0xff000000); }
427 public bool gxIsSolid (uint clr) pure nothrow @safe @nogc { pragma(inline, true); return ((clr&0xff000000) == 0x00_000000); }
429 public enum gxTransparent = 0xff000000;
432 // ////////////////////////////////////////////////////////////////////////// //
433 private template isGoodRGBInt(T) {
434 import std.traits : Unqual;
435 alias TT = Unqual!T;
436 enum isGoodRGBInt =
437 is(TT == ubyte) ||
438 is(TT == short) || is(TT == ushort) ||
439 is(TT == int) || is(TT == uint) ||
440 is(TT == long) || is(TT == ulong);
444 // ////////////////////////////////////////////////////////////////////////// //
445 public uint gxrgb(T0, T1, T2) (T0 r, T1 g, T2 b) pure nothrow @trusted @nogc if (isGoodRGBInt!T0 && isGoodRGBInt!T1 && isGoodRGBInt!T2) {
446 pragma(inline, true);
447 return (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
451 public template gxRGB(int r, int g, int b) {
452 enum gxRGB = (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
455 public template gxRGBA(int r, int g, int b, int a) {
456 enum gxRGBA = (clampToByte(a)<<24)|(clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);