some updates
[iv.d.git] / color.d
blobc9fd91cefbf30eda1c300fb06af455a553148855
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, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv.color;
19 // color in sRGB space
20 struct RGB8 {
21 public:
22 string toString () const nothrow @trusted {
23 import core.stdc.stdio : snprintf;
24 char[256] buf = void;
25 auto len = snprintf(buf.ptr, buf.length, "RGB8(%u,%u,%u)", cast(uint)r, cast(uint)g, cast(uint)b);
26 return buf[0..len].idup;
29 public pure nothrow @safe @nogc:
30 ubyte r=0, g=0, b=0;
32 this (int ar, int ag, int ab) pure nothrow @safe @nogc { pragma(inline, true); r = clampToByte(ar); g = clampToByte(ag); b = clampToByte(ab); }
33 this() (in auto ref RGB8 c) pure nothrow @safe @nogc { pragma(inline, true); r = c.r; g = c.g; b = c.b; }
34 this() (in auto ref SRGB c) pure nothrow @safe @nogc { pragma(inline, true); r = clampToByte(cast(int)(c.r*255.0)); g = clampToByte(cast(int)(c.g*255.0)); b = clampToByte(cast(int)(c.b*255.0)); }
35 this() (in auto ref CLAB c) pure nothrow @safe @nogc { pragma(inline, true); this = cast(RGB8)cast(CXYZD65)c; }
36 this(C) (in auto ref C c) pure nothrow @safe @nogc if (is(C : CXYZImpl!M, string M) || is(C == CHSL)) { pragma(inline, true); this = cast(RGB8)c; }
38 ref auto opAssign() (in auto ref RGB8 c) { pragma(inline, true); r = c.r; g = c.g; b = c.b; return this; }
39 ref auto opAssign() (in auto ref SRGB c) { pragma(inline, true); this = cast(RGB8)c; return this; }
40 ref auto opAssign() (in auto ref CLAB c) { pragma(inline, true); this = cast(RGB8)cast(CXYZD65)c; return this; }
41 ref auto opAssign(C) (in auto ref C c) if (is(C : CXYZImpl!M, string M) || is(C == CHSL)) { pragma(inline, true); this = cast(RGB8)c; return this; }
43 SRGB opCast(T:RGB8) () const nothrow @safe @nogc { pragma(inline, true); return RGB8(r, g, b); }
44 SRGB opCast(T:SRGB) () const nothrow @safe @nogc { pragma(inline, true); return SRGB(this); }
45 CLAB opCast(T:CLAB) () const nothrow @safe @nogc { pragma(inline, true); return CLAB(CXYZD65(this)); }
46 C opCast(C) () const nothrow @safe @nogc if (is(C : CXYZImpl!M, string M) || is(C == CHSL)) { pragma(inline, true); return C(this); }
48 // CIE76
49 float distance(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB) || is(C == CHSL)) { pragma(inline, true); return (cast(CLAB)this).distance(c); }
50 float distanceSquared(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB) || is(C == CHSL)) { pragma(inline, true); return (cast(CLAB)this).distanceSquared(c); }
52 static:
53 // this is actually branch-less for ints on x86, and even for longs on x86_64
54 static ubyte clampToByte(T) (T n) pure nothrow @safe @nogc if (__traits(isIntegral, T)) {
55 static if (__VERSION__ > 2067) pragma(inline, true);
56 static if (T.sizeof == 2 || T.sizeof == 4) {
57 static if (__traits(isUnsigned, T)) {
58 return cast(ubyte)(n&0xff|(255-((-cast(int)(n < 256))>>24)));
59 } else {
60 n &= -cast(int)(n >= 0);
61 return cast(ubyte)(n|((255-cast(int)n)>>31));
63 } else static if (T.sizeof == 1) {
64 static assert(__traits(isUnsigned, T), "clampToByte: signed byte? no, really?");
65 return cast(ubyte)n;
66 } else static if (T.sizeof == 8) {
67 static if (__traits(isUnsigned, T)) {
68 return cast(ubyte)(n&0xff|(255-((-cast(long)(n < 256))>>56)));
69 } else {
70 n &= -cast(long)(n >= 0);
71 return cast(ubyte)(n|((255-cast(long)n)>>63));
73 } else {
74 static assert(false, "clampToByte: integer too big");
80 // color in sRGB space
81 struct SRGB {
82 public:
83 string toString () const nothrow @trusted {
84 import core.stdc.stdio : snprintf;
85 char[256] buf = void;
86 auto len = snprintf(buf.ptr, buf.length, "SRGB(%g,%g,%g)", cast(double)r, cast(double)g, cast(double)b);
87 return buf[0..len].idup;
90 public pure nothrow @safe @nogc:
91 float r=0, g=0, b=0; // [0..1]
93 this (in float ar, in float ag, in float ab) pure nothrow @safe @nogc { pragma(inline, true); r = ar; g = ag; b = ab; }
94 this() (in auto ref RGB8 c) pure nothrow @safe @nogc { pragma(inline, true); r = c.r/255.0; g = c.g/255.0; b = c.b/255.0; }
95 this() (in auto ref SRGB c) pure nothrow @safe @nogc { pragma(inline, true); r = c.r; g = c.g; b = c.b; }
96 this() (in auto ref CLAB c) pure nothrow @safe @nogc { pragma(inline, true); this = cast(SRGB)cast(CXYZD65)c; }
97 this(C) (in auto ref C c) pure nothrow @safe @nogc if (is(C : CXYZImpl!M, string M) || is(C == CHSL)) { pragma(inline, true); this = cast(SRGB)c; }
99 ref auto opAssign() (in auto ref RGB8 c) { pragma(inline, true); r = c.r/255.0; g = c.g/255.0; b = c.b/255.0; return this; }
100 ref auto opAssign() (in auto ref SRGB c) { pragma(inline, true); r = c.r; g = c.g; b = c.b; return this; }
101 ref auto opAssign() (in auto ref CLAB c) { pragma(inline, true); this = cast(SRGB)cast(CXYZD65)c; return this; }
102 ref auto opAssign(C) (in auto ref C c) if (is(C : CXYZImpl!M, string M) || is(C == CHSL)) { pragma(inline, true); this = cast(SRGB)c; return this; }
104 RGB8 opCast(T:RGB8) () const nothrow @safe @nogc { pragma(inline, true); return RGB8(cast(int)(r*255.0+0.5), cast(int)(g*255.0+0.5), cast(int)(b*255.0+0.5)); }
105 SRGB opCast(T:SRGB) () const nothrow @safe @nogc { pragma(inline, true); return SRGB(r, g, b); }
106 CLAB opCast(T:CLAB) () const nothrow @safe @nogc { pragma(inline, true); return CLAB(CXYZD65(this)); }
107 C opCast(C) () const nothrow @safe @nogc if (is(C : CXYZImpl!M, string M) || is(C == CHSL)) { pragma(inline, true); return C(this); }
109 // CIE76
110 float distance(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB) || is(C == CHSL)) { pragma(inline, true); return (cast(CLAB)this).distance(c); }
111 float distanceSquared(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB) || is(C == CHSL)) { pragma(inline, true); return (cast(CLAB)this).distanceSquared(c); }
115 alias CXYZD65 = CXYZImpl!"d65"; // color in CIE Standard Illuminant D65
116 alias CXYZLinear = CXYZImpl!"linear"; // color in linear space
118 // color in linear space
119 struct CXYZImpl(string mode) {
120 public:
121 static assert(mode == "d65" || mode == "linear", "invalid CXYZ mode: '"~mode~"'");
122 alias MyType = CXYZImpl!mode;
124 public:
125 string toString () const nothrow @trusted {
126 import core.stdc.stdio : snprintf;
127 char[256] buf = void;
128 auto len = snprintf(buf.ptr, buf.length, "CXYZ[%s](%g,%g,%g)", mode.ptr, cast(double)x, cast(double)y, cast(double)z);
129 return buf[0..len].idup;
132 public pure nothrow @safe @nogc:
133 float x=0, y=0, z=0; // [0..1]
135 this (in float ax, in float ay, in float az) { pragma(inline, true); x = ax; y = ay; z = az; }
136 this() (in auto ref MyType c) { pragma(inline, true); x = c.x; y = c.y; z = c.z; }
137 this() (in auto CLAB c) { pragma(inline, true); this = cast(MyType)c; }
138 this() (in auto CHSL c) { pragma(inline, true); this = cast(MyType)c; }
139 this() (in auto ref RGB8 c) {
140 static if (mode == "d65") {
141 immutable double rl = valueFromGamma(c.r/255.0);
142 immutable double gl = valueFromGamma(c.g/255.0);
143 immutable double bl = valueFromGamma(c.b/255.0);
144 // observer = 2degs, illuminant = D65
145 x = rl*0.4124+gl*0.3576+bl*0.1805;
146 y = rl*0.2126+gl*0.7152+bl*0.0722;
147 z = rl*0.0193+gl*0.1192+bl*0.9505;
148 } else {
149 x = valueFromGamma(c.r/255.0);
150 y = valueFromGamma(c.g/255.0);
151 z = valueFromGamma(c.b/255.0);
154 this() (in auto ref SRGB c) {
155 static if (mode == "d65") {
156 immutable double rl = valueFromGamma(c.r);
157 immutable double gl = valueFromGamma(c.g);
158 immutable double bl = valueFromGamma(c.b);
159 // observer = 2degs, illuminant = D65
160 x = rl*0.4124+gl*0.3576+bl*0.1805;
161 y = rl*0.2126+gl*0.7152+bl*0.0722;
162 z = rl*0.0193+gl*0.1192+bl*0.9505;
163 } else {
164 x = valueFromGamma(c.r);
165 y = valueFromGamma(c.g);
166 z = valueFromGamma(c.b);
170 ref auto opAssign() (in auto ref MyType c) { pragma(inline, true); x = c.x; y = c.y; z = c.z; return this; }
171 ref auto opAssign(C) (in auto ref C c) if (is(C == RGB8) || is(C == SRGB) || is(C == CHSL)) { pragma(inline, true); this = cast(MyType)c; return this; }
172 static if (mode == "d65") ref auto opAssign() (in auto CLAB c) { pragma(inline, true); this = cast(MyType)c; return this; }
173 static if (mode == "linear") ref auto opAssign() (in auto CLAB c) { pragma(inline, true); this = cast(MyType)cast(SRGB)c; return this; }
175 MyType opCast(T:MyType) () const { pragma(inline, true); return MyType(x, y, z); }
176 static if (mode == "d65") CLAB opCast(T:CLAB) () { pragma(inline, true); return CLAB(this); }
178 RGB8 opCast(T:RGB8) () const {
179 static if (mode == "d65") {
180 immutable double xs = x* 3.2406+y*-1.5372+z*-0.4986;
181 immutable double ys = x*-0.9689+y* 1.8758+z* 0.0415;
182 immutable double zs = x* 0.0557+y*-0.2040+z* 1.0570;
183 return RGB8(cast(int)(valueFromLinear(xs)*255.0+0.5), cast(int)(valueFromLinear(ys)*255.0+0.5), cast(int)(valueFromLinear(zs)*255.0+0.5));
184 } else {
185 return SRGB(cast(int)(valueFromLinear(x)*255.0+0.5), cast(int)(valueFromLinear(y)*255.0+0.5), cast(int)(valueFromLinear(z)*255.0+0.5));
189 SRGB opCast(T:SRGB) () const {
190 static if (mode == "d65") {
191 immutable double xs = x* 3.2406+y*-1.5372+z*-0.4986;
192 immutable double ys = x*-0.9689+y* 1.8758+z* 0.0415;
193 immutable double zs = x* 0.0557+y*-0.2040+z* 1.0570;
194 return SRGB(valueFromLinear(xs), valueFromLinear(ys), valueFromLinear(zs));
195 } else {
196 return SRGB(valueFromLinear(x), valueFromLinear(y), valueFromLinear(z));
200 MyType lighten (in float n) const { pragma(inline, true); return MyType(clamp(x+n), clamp(y+n), clamp(z+n)); }
201 MyType darken (in float n) const { pragma(inline, true); return MyType(clamp(x-n), clamp(y-n), clamp(z-n)); }
203 // CIE76
204 float distance(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB) || is(C == CHSL)) { pragma(inline, true); return (cast(CLAB)this).distance(c); }
205 float distanceSquared(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB) || is(C == CHSL)) { pragma(inline, true); return (cast(CLAB)this).distanceSquared(c); }
207 public:
208 static T clamp(T) (in T a) { pragma(inline, true); return (a < 0 ? 0 : a > 1 ? 1 : a); }
210 // gamma to linear conversion
211 // value should be in [0..1] range
212 static T valueFromGamma(T : real) (T v) {
213 import std.math : pow;
214 return (v > 0.04045 ? pow((v+0.055)/1.055, 2.4) : v/12.92);
217 // linear to gamma conversion
218 // value should be in [0..1] range
219 static T valueFromLinear(T : real) (T v) {
220 import std.math : pow;
221 return (v > 0.0031308 ? 1.055*pow(v, (1.0/2.4))-0.055 : 12.92*v);
226 // color in CIE Lab space
227 struct CLAB {
228 public:
229 string toString () const nothrow @trusted {
230 import core.stdc.stdio : snprintf;
231 char[256] buf = void;
232 auto len = snprintf(buf.ptr, buf.length, "CLAB(%g,%g,%g)", cast(double)l, cast(double)a, cast(double)b);
233 return buf[0..len].idup;
236 public pure nothrow @safe @nogc:
237 float l=0, a=0, b=0; // *NOT* [0..1]
239 this (in float al, in float aa, in float ab) { pragma(inline, true); l = al; a = aa; b = ab; }
240 this(C) (in auto ref C c) if (is(C == SRGB) || is(C == RGB8)) { pragma(inline, true); this = cast(CLAB)c; }
242 this() (in auto ref CXYZD65 c) {
243 import std.math : pow;
245 double xs = c.x/95.047;
246 double ys = c.y/100.0;
247 double zs = c.z/108.883;
249 xs = (xs > 0.008856 ? pow(xs, 1.0/3.0) : (7.787*xs)+16.0/116.0);
250 ys = (ys > 0.008856 ? pow(ys, 1.0/3.0) : (7.787*ys)+16.0/116.0);
251 zs = (zs > 0.008856 ? pow(zs, 1.0/3.0) : (7.787*zs)+16.0/116.0);
253 l = cast(float)((116.0*ys)-16.0);
254 a = cast(float)(500.0*(xs-ys));
255 b = cast(float)(200.0*(ys-zs));
258 ref auto opAssign() (in auto ref CLAB c) { pragma(inline, true); l = c.l; a = c.a; b = c.b; return this; }
259 ref auto opAssign(C) (in auto ref C c) if (is(C == RGB8) || is(C == SRGB) || is(C == CXYZD65) || is(C == CHSL)) { pragma(inline, true); this = cast(CLAB)c; return this; }
260 ref auto opAssign() (in auto ref CXYZLinear c) { pragma(inline, true); this = cast(CLAB)cast(SRGB)c; return this; }
262 CLAB opCast(T:CLAB) () const { pragma(inline, true); return CLAB(l, a, b); }
263 SRGB opCast(T:SRGB) () const { pragma(inline, true); return SRGB(this); }
264 RGB8 opCast(T:RGB8) () const { pragma(inline, true); return RGB8(this); }
265 RGB8 opCast(T:CHSL) () const { pragma(inline, true); return CHSL(this); }
266 CXYZLinear opCast(T:CXYZLinear) () const { pragma(inline, true); return CXYZLinear(cast(SRGB)this); }
268 CXYZD65 opCast(T:CXYZD65) () const {
269 immutable double ys = (l+16.0)/116.0;
270 immutable double xs = (a/500.0)+ys;
271 immutable double zs = ys-(b/200.0);
273 immutable double x3 = xs*xs*xs;
274 immutable double y3 = ys*ys*ys;
275 immutable double z3 = zs*zs*zs;
277 return CXYZD65(
278 (x3 > 0.008856 ? x3 : (xs-16.0/116.0)/7.787)*95.047,
279 (y3 > 0.008856 ? y3 : (ys-16.0/116.0)/7.787)*100.000,
280 (z3 > 0.008856 ? z3 : (zs-16.0/116.0)/7.787)*108.883,
284 // CIE76
285 float distance() (in auto ref CLAB c) const { pragma(inline, true); import std.math : sqrt; return sqrt((l-c.l)*(l-c.l)+(a-c.a)*(a-c.a)+(b-c.b)*(b-c.b)); }
286 float distance(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CHSL)) { pragma(inline, true); return distance(cast(CLAB)c); }
287 float distanceSquared() (in auto ref CLAB c) const { pragma(inline, true); return (l-c.l)*(l-c.l)+(a-c.a)*(a-c.a)+(b-c.b)*(b-c.b); }
288 float distanceSquared(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CHSL)) { pragma(inline, true); return distanceSquared(cast(CLAB)c); }
292 // Hue/Saturation/Lighting color
293 struct CHSL {
294 public:
295 string toString () const nothrow @trusted {
296 import core.stdc.stdio : snprintf;
297 char[256] buf = void;
298 auto len = snprintf(buf.ptr, buf.length, "CHSL(%g,%g,%g)", cast(double)h, cast(double)s, cast(double)l);
299 return buf[0..len].idup;
302 private nothrow @safe @nogc:
303 void fromColor(C) (in auto ref C c) pure {
304 static if (is(C == SRGB)) {
305 enum Weighted = true;
306 immutable double r1 = clamp(c.r);
307 immutable double g1 = clamp(c.g);
308 immutable double b1 = clamp(c.b);
309 } else static if (is(C == RGB8)) {
310 enum Weighted = true;
311 immutable double r1 = c.r/255.0;
312 immutable double g1 = c.g/255.0;
313 immutable double b1 = c.b/255.0;
314 } else static if (is(C : CXYZImpl!M, string M)) {
315 enum Weighted = false;
316 immutable double r1 = clamp(c.r);
317 immutable double g1 = clamp(c.g);
318 immutable double b1 = clamp(c.b);
319 } else {
320 immutable cc = cast(CXYZD65)c;
321 enum Weighted = false;
322 immutable double r1 = clamp(cc.r);
323 immutable double g1 = clamp(cc.g);
324 immutable double b1 = clamp(cc.b);
327 double maxColor = r1;
328 if (g1 > maxColor) maxColor = g1;
329 if (b1 > maxColor) maxColor = b1;
331 double minColor = r1;
332 if (g1 < minColor) minColor = g1;
333 if (b1 < minColor) minColor = b1;
335 static if (Weighted && false) {
336 // the colors don't affect the eye equally
337 // this is a little more accurate than plain HSL numbers
338 l = 0.2126*r1+0.7152*g1+0.0722*b1;
339 } else {
340 l = (maxColor+minColor)/2.0;
342 if (maxColor != minColor) {
343 if (l < 0.5) {
344 s = (maxColor-minColor)/(maxColor+minColor);
345 } else {
346 s = (maxColor-minColor)/(2.0-maxColor-minColor);
348 if (r1 == maxColor) {
349 h = (g1-b1)/(maxColor-minColor);
350 } else if(g1 == maxColor) {
351 h = 2.0+(b1-r1)/(maxColor-minColor);
352 } else {
353 h = 4.0+(r1-g1)/(maxColor-minColor);
357 h = h*60;
358 if (h < 0) h += 360;
359 h /= 360;
362 C toColor(C) () const {
363 static double hue (double h, double m1, double m2) pure nothrow @safe @nogc {
364 if (h < 0) h += 1;
365 if (h > 1) h -= 1;
366 if (h < 1.0/6.0) return m1+(m2-m1)*h*6.0;
367 if (h < 3.0/6.0) return m2;
368 if (h < 4.0/6.0) return m1+(m2-m1)*(2.0/3.0-h)*6.0;
369 return m1;
371 import std.math : modf;
372 real tmpi = void;
373 double sh = modf(h, tmpi);
374 if (sh < 0.0f) sh += 1.0f;
375 double ss = clamp(s);
376 double sl = clamp(l);
377 immutable double m2 = (sl <= 0.5 ? sl*(1+ss) : sl+ss-sl*ss);
378 immutable double m1 = 2*sl-m2;
380 static if (is(C == SRGB)) {
381 return SRGB(
382 clamp(hue(sh+1.0/3.0, m1, m2)),
383 clamp(hue(sh, m1, m2)),
384 clamp(hue(sh-1.0/3.0, m1, m2)),
386 } else static if (is(C == RGB8)) {
387 return RGB8(
388 cast(int)(hue(sh+1.0/3.0, m1, m2)*255.0),
389 cast(int)(hue(sh, m1, m2)*255.0),
390 cast(int)(hue(sh-1.0/3.0, m1, m2)*255.0),
392 } else static if (is(C : CXYZImpl!M, string M)) {
393 return C(
394 clamp(hue(sh+1.0/3.0, m1, m2)),
395 clamp(hue(sh, m1, m2)),
396 clamp(hue(sh-1.0/3.0, m1, m2)),
398 } else {
399 return cast(C)CXYZD65(
400 clamp(hue(sh+1.0/3.0, m1, m2)),
401 clamp(hue(sh, m1, m2)),
402 clamp(hue(sh-1.0/3.0, m1, m2)),
407 public nothrow @safe @nogc:
408 float h = 0, s = 0, l = 0; // [0..1]
410 this (in float ah, in float as, in float al) { pragma(inline, true); h = ah; s = as; l = al; }
411 this() (in auto ref CHSL c) { pragma(inline, true); h = c.h; s = c.s; l = c.l; }
412 this(C) (in auto ref C c) if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB)) { pragma(inline, true); fromColor(c); }
414 ref opAssign(C:CHSL) (in auto ref C c) { pragma(inline, true); h = c.h; s = c.s; l = c.l; return this; }
415 ref opAssign(C) (in auto ref C c) if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB)) { pragma(inline, true); fromColor(c); return this; }
417 CHSL opCast(C:CHSL) () const { pragma(inline, true); return CHSL(h, s, l); }
418 C opCast(C) () const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB)) { pragma(inline, true); return toColor!C; }
420 //CHSL darken (in float n) const { pragma(inline, true); return CHSL(clamp(h*n), s, l); }
422 // CIE76
423 float distance(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB)) { pragma(inline, true); return (cast(CLAB)this).distance(c); }
424 float distanceSquared(C) (in auto ref C c) const if (is(C : CXYZImpl!M, string M) || is(C == SRGB) || is(C == RGB8) || is(C == CLAB)) { pragma(inline, true); return (cast(CLAB)this).distanceSquared(c); }
426 public:
427 static T clamp(T) (in T a) { pragma(inline, true); return (a < 0 ? 0 : a > 1 ? 1 : a); }
431 version(iv_color_unittest) unittest {
432 import std.stdio;
434 auto s0 = SRGB(1, 128/255.0, 0);
435 auto l0 = cast(CXYZD65)s0;
436 auto l1 = cast(CXYZLinear)s0;
437 auto s1 = cast(SRGB)l0;
438 auto s2 = cast(SRGB)l1;
439 writeln("s0=", s0, " : ", cast(RGB8)s0);
440 writeln("l0=", l0);
441 writeln("l1=", l1);
442 writeln("s1=", s1);
443 writeln("s2=", s2);
445 writeln("black XYZ=", cast(CXYZD65)SRGB(0, 0, 0));
446 writeln("white XYZ=", cast(CXYZD65)SRGB(1, 1, 1));
447 writeln("MUST BE =CXYZ(0.9642,1,0.8249)");
448 //writeln("white XYZ=", cast(linear)sRGB(1));
450 auto lab = cast(CLAB)s0;
451 writeln("srgb->lab->srgb: ", cast(SRGB)lab, " : ", cast(RGB8)lab);
452 writeln("rgb: ", s0, " : ", cast(RGB8)s0);
453 writeln("lab: ", lab);
454 writeln("rgb: ", cast(SRGB)lab);
455 lab.l -= 1;
456 auto z1 = cast(SRGB)lab; //cast(SRGB)CLAB(lab.l-0.01, lab.a, lab.b);
457 writeln("rgbX: ", z1, " : ", cast(RGB8)z1);
458 writeln("xxx: ", cast(CLAB)cast(CXYZD65)RGB8(255-16, 128-16, 0));
462 writeln("============");
463 auto s0 = RGB8(255, 127, 0);
464 writeln("*s0: ", s0, " : ", cast(RGB8)s0);
465 auto h0 = cast(CHSL)s0;
466 writeln("*h0: ", h0);
467 h0.h *= 0.9;
468 writeln("*s1: ", h0, " : ", cast(RGB8)h0);
469 writeln(RGB8(255-25, 127-25, 0-25));
473 writeln("============");
474 auto s0 = cast(CXYZD65)RGB8(255, 127, 0);
475 writeln("*s0: ", s0, " : ", cast(RGB8)s0);
476 auto s1 = s0.darken(0.1);
477 writeln("*s1: ", s0, " : ", cast(RGB8)s0);
478 writeln(RGB8(255-25, 127-25, 0-25));