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/>.
19 // color in sRGB space
22 string
toString () const nothrow @trusted {
23 import core
.stdc
.stdio
: snprintf
;
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:
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); }
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
); }
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)));
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?");
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)));
70 n
&= -cast(long)(n
>= 0);
71 return cast(ubyte)(n|
((255-cast(long)n
)>>63));
74 static assert(false, "clampToByte: integer too big");
80 // color in sRGB space
83 string
toString () const nothrow @trusted {
84 import core
.stdc
.stdio
: snprintf
;
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); }
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
) {
121 static assert(mode
== "d65" || mode
== "linear", "invalid CXYZ mode: '"~mode
~"'");
122 alias MyType
= CXYZImpl
!mode
;
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;
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;
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));
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
));
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
)); }
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
); }
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
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
;
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,
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
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
);
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
;
340 l
= (maxColor
+minColor
)/2.0;
342 if (maxColor
!= minColor
) {
344 s
= (maxColor
-minColor
)/(maxColor
+minColor
);
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
);
353 h
= 4.0+(r1
-g1
)/(maxColor
-minColor
);
362 C
toColor(C
) () const {
363 static double hue (double h
, double m1
, double m2
) pure nothrow @safe @nogc {
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;
371 import std
.math
: modf
;
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
)) {
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
)) {
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
)) {
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
)),
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); }
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
); }
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 {
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
);
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
);
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
);
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));