Various gfx stuffs
[SmugglerRL.git] / src / util.d
blob752f9dc6d5ada9a2b336d85d58f9aae81d8d56cd
1 public import std.random: uniform;
2 import game;
3 import constants;
4 import std.exception: Exception;
5 import std.functional: memoize;
6 import std.string: format;
8 public alias rnd = uniform;
10 class DException: Exception {
11 pragma(inline, true) pure this(T...)(T args) {
12 super(format(args));
16 class InternalError: DException {
17 pragma(inline, true) pure this(T...)(T args) {
18 super(args);
22 // Many thanks, FIQ
23 bool isvalidcolour(string hexcolour) {
24 bool bypass = false;
25 if (hexcolour[0] == '#') {
26 bypass = true;
29 int count = 0;
30 foreach (char nam; hexcolour) {
31 if (bypass) {
32 bypass = false;
33 continue;
35 if (!nam) {
36 break;
39 if (!((('0' <= nam) && (nam <= '9')) || (('A' <= nam) && (nam <= 'F')))) {
40 return false;
43 count++;
44 if (count > 6) {
45 return false;
48 return (count == 6);
51 static if(0) {
52 bool isvalidcolour(string hexcolour) {
53 import std.regex: ctRegex, matchFirst;
54 auto hexcolourmatch = ctRegex!("#?[0-9a-fA-F]{3,6}");
56 if (matchFirst(hexcolour, hexcolourmatch).empty) {
57 return false;
58 } else {
59 return true;
64 struct RGBColour {
65 ubyte r=0xFF, g=0xFF, b=0xFF;
66 pragma(inline, true) pure this(ubyte in_r, ubyte in_g, ubyte in_b) {
67 r = in_r;
68 g = in_g;
69 b = in_b;
73 // No constructor for colours of the form 0xFFF, because there's no way to tell the difference between 0xFFF and 0x000FFF
74 pragma(inline, true) pure this(uint clr) {
75 b = clr % 256;
77 /* An interesting optimization. Ordinarily, you do b = clr % 256,
78 * g = (clr / 256) % 256, r = (clr / 256 / 256) % 256. left/right
79 * shifts are cheaper than multiplication, and by reassigning clr,
80 * there's an extra multiplication step that doesn't occur.
82 g = (clr >>= 8) % 256;
83 r = (clr >> 8) % 256;
86 pure this(string hexcolour, bool hasvalidated=false) {
87 import std.conv: to;
89 // strip leading #, if present
90 if ((hexcolour.length == 7) || (hexcolour.length == 4)) {
91 hexcolour = hexcolour[1..$];
94 if (hasvalidated) {
95 if (hexcolour.length == 6) {
96 r = hexcolour[0..2].to!ubyte(16);
97 g = hexcolour[2..4].to!ubyte(16);
98 b = hexcolour[4..6].to!ubyte(16);
99 } else {
100 // The ranges here are to get a string just one character long so to!ubyte works
101 r = cast(ubyte)(hexcolour[0..1].to!ubyte(16)*16);
102 g = cast(ubyte)(hexcolour[1..2].to!ubyte(16)*16);
103 b = cast(ubyte)(hexcolour[2..3].to!ubyte(16)*16);
105 } else {
106 throw new InternalError("RGBColour constructor called but rgbcolour not validated!");
110 pragma(inline, true) this(string hexcolour) {
111 if (!isvalidcolour(hexcolour)) {
112 throw new InternalError("RGBColour constructor called with faulty colour.");
114 this(hexcolour, true);
117 pragma(inline, true) pure int toint() {
118 return (255 << 24) | (r << 16) | (g << 8) | b;
121 RGBColour darken(ubyte level) {
122 RGBColour tmp;
123 tmp.r = cast(ubyte) ((r < level) ? 0 : r-level);
124 tmp.g = cast(ubyte) ((g < level) ? 0 : g-level);
125 tmp.b = cast(ubyte) ((b < level) ? 0 : b-level);
127 return tmp;
130 RGBColour lighten(ubyte level) {
131 RGBColour tmp;
133 tmp.r = cast(ubyte) (((255-r) < level) ? 255 : r+level);
134 tmp.g = cast(ubyte) (((255-g) < level) ? 255 : g+level);
135 tmp.b = cast(ubyte) (((255-b) < level) ? 255 : b+level);
137 return tmp;
141 void exit(ubyte code) {
142 import core.runtime: Runtime;
143 static import core.stdc.stdlib;
144 Runtime.terminate();
145 core.stdc.stdlib.exit(code);
148 pure string fillstr(int length, char ch=' ') {
149 string buf;
150 while (length --> 0) {
151 buf ~= ch;
153 return buf;
157 // Taken directly from http://stackoverflow.com/a/41978310
158 /* It's supposed to be incredibly performant...but honestly I used it because
159 * I could copy-paste it easily.
161 pure ubyte _get256colour(RGBColour colour) {
162 enum ubyte[6] i2cv = [0, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
163 pragma(inline, true) {
164 pure ubyte function(ubyte) v2ci = cast(ubyte function(ubyte) pure) (ubyte v) => v < 48 ? 0 : v < 115 ? 1 : (v - 35) / 40;
165 auto colour_index = (int r, int g, int b) => 36*r + 6*g + b;
166 auto distance = (int r1, int g1, int b1, int r2, int g2, int b2) => (r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2);
169 ubyte ir = v2ci(colour.r), ig = v2ci(colour.g), ib = v2ci(colour.b);
170 ubyte average = (colour.r + colour.g + colour.b) / 3;
171 ubyte igray = average > 238 ? 23 : (average-3) / 10;
172 ubyte cr = i2cv[ir], cg = i2cv[ig], cb = i2cv[ib];
173 ubyte gv = cast(ubyte)(8 + 10 * igray);
175 int colour_err = distance(cr, cg, cb, colour.r, colour.g, colour.b);
176 int gray_err = distance(gv, gv, gv, colour.r, colour.g, colour.b);
178 return cast(ubyte)(colour_err <= gray_err ? 16 + colour_index(ir, ig, ib) : 232 + igray);
181 alias get256colour = memoize!_get256colour;
183 // Overloads...
184 pragma(inline, true) ubyte get256colour(ubyte r, ubyte g, ubyte b) { return get256colour(RGBColour(r, g, b)); }
185 pragma(inline, true) ubyte get256colour(string colour) { return get256colour(RGBColour(colour)); }