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/>.
20 // color in sRGB space
23 string
toString () const nothrow @trusted {
24 import core
.stdc
.stdio
: snprintf
;
26 auto len
= snprintf(buf
.ptr
, buf
.length
, "RGB8(%u,%u,%u)", cast(uint)r
, cast(uint)g
, cast(uint)b
);
27 return buf
[0..len
].idup
;
30 public pure nothrow @safe @nogc:
33 this (int ar
, int ag
, int ab
) pure nothrow @safe @nogc { pragma(inline
, true); r
= clampToByte(ar
); g
= clampToByte(ag
); b
= clampToByte(ab
); }
34 this() (in auto ref RGB8 c
) pure nothrow @safe @nogc { pragma(inline
, true); r
= c
.r
; g
= c
.g
; b
= c
.b
; }
35 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)); }
36 this() (in auto ref CLAB c
) pure nothrow @safe @nogc { pragma(inline
, true); this = cast(RGB8
)cast(CXYZD65
)c
; }
37 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
; }
39 ref auto opAssign() (in auto ref RGB8 c
) { pragma(inline
, true); r
= c
.r
; g
= c
.g
; b
= c
.b
; return this; }
40 ref auto opAssign() (in auto ref SRGB c
) { pragma(inline
, true); this = cast(RGB8
)c
; return this; }
41 ref auto opAssign() (in auto ref CLAB c
) { pragma(inline
, true); this = cast(RGB8
)cast(CXYZD65
)c
; return this; }
42 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; }
44 SRGB
opCast(T
:RGB8
) () const nothrow @safe @nogc { pragma(inline
, true); return RGB8(r
, g
, b
); }
45 SRGB
opCast(T
:SRGB
) () const nothrow @safe @nogc { pragma(inline
, true); return SRGB(this); }
46 CLAB
opCast(T
:CLAB
) () const nothrow @safe @nogc { pragma(inline
, true); return CLAB(CXYZD65(this)); }
47 C
opCast(C
) () const nothrow @safe @nogc if (is(C
: CXYZImpl
!M
, string M
) ||
is(C
== CHSL
)) { pragma(inline
, true); return C(this); }
50 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
); }
51 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
); }
54 // this is actually branch-less for ints on x86, and even for longs on x86_64
55 static ubyte clampToByte(T
) (T n
) pure nothrow @safe @nogc if (__traits(isIntegral
, T
)) {
56 static if (__VERSION__
> 2067) pragma(inline
, true);
57 static if (T
.sizeof
== 2 || T
.sizeof
== 4) {
58 static if (__traits(isUnsigned
, T
)) {
59 return cast(ubyte)(n
&0xff|
(255-((-cast(int)(n
< 256))>>24)));
61 n
&= -cast(int)(n
>= 0);
62 return cast(ubyte)(n|
((255-cast(int)n
)>>31));
64 } else static if (T
.sizeof
== 1) {
65 static assert(__traits(isUnsigned
, T
), "clampToByte: signed byte? no, really?");
67 } else static if (T
.sizeof
== 8) {
68 static if (__traits(isUnsigned
, T
)) {
69 return cast(ubyte)(n
&0xff|
(255-((-cast(long)(n
< 256))>>56)));
71 n
&= -cast(long)(n
>= 0);
72 return cast(ubyte)(n|
((255-cast(long)n
)>>63));
75 static assert(false, "clampToByte: integer too big");
81 // color in sRGB space
84 string
toString () const nothrow @trusted {
85 import core
.stdc
.stdio
: snprintf
;
87 auto len
= snprintf(buf
.ptr
, buf
.length
, "SRGB(%g,%g,%g)", cast(double)r
, cast(double)g
, cast(double)b
);
88 return buf
[0..len
].idup
;
91 public pure nothrow @safe @nogc:
92 float r
=0, g
=0, b
=0; // [0..1]
94 this (in float ar
, in float ag
, in float ab
) pure nothrow @safe @nogc { pragma(inline
, true); r
= ar
; g
= ag
; b
= ab
; }
95 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; }
96 this() (in auto ref SRGB c
) pure nothrow @safe @nogc { pragma(inline
, true); r
= c
.r
; g
= c
.g
; b
= c
.b
; }
97 this() (in auto ref CLAB c
) pure nothrow @safe @nogc { pragma(inline
, true); this = cast(SRGB
)cast(CXYZD65
)c
; }
98 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
; }
100 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; }
101 ref auto opAssign() (in auto ref SRGB c
) { pragma(inline
, true); r
= c
.r
; g
= c
.g
; b
= c
.b
; return this; }
102 ref auto opAssign() (in auto ref CLAB c
) { pragma(inline
, true); this = cast(SRGB
)cast(CXYZD65
)c
; return this; }
103 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; }
105 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)); }
106 SRGB
opCast(T
:SRGB
) () const nothrow @safe @nogc { pragma(inline
, true); return SRGB(r
, g
, b
); }
107 CLAB
opCast(T
:CLAB
) () const nothrow @safe @nogc { pragma(inline
, true); return CLAB(CXYZD65(this)); }
108 C
opCast(C
) () const nothrow @safe @nogc if (is(C
: CXYZImpl
!M
, string M
) ||
is(C
== CHSL
)) { pragma(inline
, true); return C(this); }
111 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
); }
112 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
); }
116 alias CXYZD65
= CXYZImpl
!"d65"; // color in CIE Standard Illuminant D65
117 alias CXYZLinear
= CXYZImpl
!"linear"; // color in linear space
119 // color in linear space
120 struct CXYZImpl(string mode
) {
122 static assert(mode
== "d65" || mode
== "linear", "invalid CXYZ mode: '"~mode
~"'");
123 alias MyType
= CXYZImpl
!mode
;
126 string
toString () const nothrow @trusted {
127 import core
.stdc
.stdio
: snprintf
;
128 char[256] buf
= void;
129 auto len
= snprintf(buf
.ptr
, buf
.length
, "CXYZ[%s](%g,%g,%g)", mode
.ptr
, cast(double)x
, cast(double)y
, cast(double)z
);
130 return buf
[0..len
].idup
;
133 public pure nothrow @safe @nogc:
134 float x
=0, y
=0, z
=0; // [0..1]
136 this (in float ax
, in float ay
, in float az
) { pragma(inline
, true); x
= ax
; y
= ay
; z
= az
; }
137 this() (in auto ref MyType c
) { pragma(inline
, true); x
= c
.x
; y
= c
.y
; z
= c
.z
; }
138 this() (in auto CLAB c
) { pragma(inline
, true); this = cast(MyType
)c
; }
139 this() (in auto CHSL c
) { pragma(inline
, true); this = cast(MyType
)c
; }
140 this() (in auto ref RGB8 c
) {
141 static if (mode
== "d65") {
142 immutable double rl
= valueFromGamma(c
.r
/255.0);
143 immutable double gl
= valueFromGamma(c
.g
/255.0);
144 immutable double bl
= valueFromGamma(c
.b
/255.0);
145 // observer = 2degs, illuminant = D65
146 x
= rl
*0.4124+gl
*0.3576+bl
*0.1805;
147 y
= rl
*0.2126+gl
*0.7152+bl
*0.0722;
148 z
= rl
*0.0193+gl
*0.1192+bl
*0.9505;
150 x
= valueFromGamma(c
.r
/255.0);
151 y
= valueFromGamma(c
.g
/255.0);
152 z
= valueFromGamma(c
.b
/255.0);
155 this() (in auto ref SRGB c
) {
156 static if (mode
== "d65") {
157 immutable double rl
= valueFromGamma(c
.r
);
158 immutable double gl
= valueFromGamma(c
.g
);
159 immutable double bl
= valueFromGamma(c
.b
);
160 // observer = 2degs, illuminant = D65
161 x
= rl
*0.4124+gl
*0.3576+bl
*0.1805;
162 y
= rl
*0.2126+gl
*0.7152+bl
*0.0722;
163 z
= rl
*0.0193+gl
*0.1192+bl
*0.9505;
165 x
= valueFromGamma(c
.r
);
166 y
= valueFromGamma(c
.g
);
167 z
= valueFromGamma(c
.b
);
171 ref auto opAssign() (in auto ref MyType c
) { pragma(inline
, true); x
= c
.x
; y
= c
.y
; z
= c
.z
; return this; }
172 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; }
173 static if (mode
== "d65") ref auto opAssign() (in auto CLAB c
) { pragma(inline
, true); this = cast(MyType
)c
; return this; }
174 static if (mode
== "linear") ref auto opAssign() (in auto CLAB c
) { pragma(inline
, true); this = cast(MyType
)cast(SRGB
)c
; return this; }
176 MyType
opCast(T
:MyType
) () const { pragma(inline
, true); return MyType(x
, y
, z
); }
177 static if (mode
== "d65") CLAB
opCast(T
:CLAB
) () { pragma(inline
, true); return CLAB(this); }
179 RGB8
opCast(T
:RGB8
) () const {
180 static if (mode
== "d65") {
181 immutable double xs
= x
* 3.2406+y
*-1.5372+z
*-0.4986;
182 immutable double ys
= x
*-0.9689+y
* 1.8758+z
* 0.0415;
183 immutable double zs
= x
* 0.0557+y
*-0.2040+z
* 1.0570;
184 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));
186 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));
190 SRGB
opCast(T
:SRGB
) () const {
191 static if (mode
== "d65") {
192 immutable double xs
= x
* 3.2406+y
*-1.5372+z
*-0.4986;
193 immutable double ys
= x
*-0.9689+y
* 1.8758+z
* 0.0415;
194 immutable double zs
= x
* 0.0557+y
*-0.2040+z
* 1.0570;
195 return SRGB(valueFromLinear(xs
), valueFromLinear(ys
), valueFromLinear(zs
));
197 return SRGB(valueFromLinear(x
), valueFromLinear(y
), valueFromLinear(z
));
201 MyType
lighten (in float n
) const { pragma(inline
, true); return MyType(clamp(x
+n
), clamp(y
+n
), clamp(z
+n
)); }
202 MyType
darken (in float n
) const { pragma(inline
, true); return MyType(clamp(x
-n
), clamp(y
-n
), clamp(z
-n
)); }
205 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
); }
206 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
); }
209 static T
clamp(T
) (in T a
) { pragma(inline
, true); return (a
< 0 ?
0 : a
> 1 ?
1 : a
); }
211 // gamma to linear conversion
212 // value should be in [0..1] range
213 static T
valueFromGamma(T
: real) (T v
) {
214 import std
.math
: pow
;
215 return (v
> 0.04045 ?
pow((v
+0.055)/1.055, 2.4) : v
/12.92);
218 // linear to gamma conversion
219 // value should be in [0..1] range
220 static T
valueFromLinear(T
: real) (T v
) {
221 import std
.math
: pow
;
222 return (v
> 0.0031308 ?
1.055*pow(v
, (1.0/2.4))-0.055 : 12.92*v
);
227 // color in CIE Lab space
230 string
toString () const nothrow @trusted {
231 import core
.stdc
.stdio
: snprintf
;
232 char[256] buf
= void;
233 auto len
= snprintf(buf
.ptr
, buf
.length
, "CLAB(%g,%g,%g)", cast(double)l
, cast(double)a
, cast(double)b
);
234 return buf
[0..len
].idup
;
237 public pure nothrow @safe @nogc:
238 float l
=0, a
=0, b
=0; // *NOT* [0..1]
240 this (in float al
, in float aa
, in float ab
) { pragma(inline
, true); l
= al
; a
= aa
; b
= ab
; }
241 this(C
) (in auto ref C c
) if (is(C
== SRGB
) ||
is(C
== RGB8
)) { pragma(inline
, true); this = cast(CLAB
)c
; }
243 this() (in auto ref CXYZD65 c
) {
244 import std
.math
: pow
;
246 double xs
= c
.x
/95.047;
247 double ys
= c
.y
/100.0;
248 double zs
= c
.z
/108.883;
250 xs
= (xs
> 0.008856 ?
pow(xs
, 1.0/3.0) : (7.787*xs
)+16.0/116.0);
251 ys
= (ys
> 0.008856 ?
pow(ys
, 1.0/3.0) : (7.787*ys
)+16.0/116.0);
252 zs
= (zs
> 0.008856 ?
pow(zs
, 1.0/3.0) : (7.787*zs
)+16.0/116.0);
254 l
= cast(float)((116.0*ys
)-16.0);
255 a
= cast(float)(500.0*(xs
-ys
));
256 b
= cast(float)(200.0*(ys
-zs
));
259 ref auto opAssign() (in auto ref CLAB c
) { pragma(inline
, true); l
= c
.l
; a
= c
.a
; b
= c
.b
; return this; }
260 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; }
261 ref auto opAssign() (in auto ref CXYZLinear c
) { pragma(inline
, true); this = cast(CLAB
)cast(SRGB
)c
; return this; }
263 CLAB
opCast(T
:CLAB
) () const { pragma(inline
, true); return CLAB(l
, a
, b
); }
264 SRGB
opCast(T
:SRGB
) () const { pragma(inline
, true); return SRGB(this); }
265 RGB8
opCast(T
:RGB8
) () const { pragma(inline
, true); return RGB8(this); }
266 RGB8
opCast(T
:CHSL
) () const { pragma(inline
, true); return CHSL(this); }
267 CXYZLinear
opCast(T
:CXYZLinear
) () const { pragma(inline
, true); return CXYZLinear(cast(SRGB
)this); }
269 CXYZD65
opCast(T
:CXYZD65
) () const {
270 immutable double ys
= (l
+16.0)/116.0;
271 immutable double xs
= (a
/500.0)+ys
;
272 immutable double zs
= ys
-(b
/200.0);
274 immutable double x3
= xs
*xs
*xs
;
275 immutable double y3
= ys
*ys
*ys
;
276 immutable double z3
= zs
*zs
*zs
;
279 (x3
> 0.008856 ? x3
: (xs
-16.0/116.0)/7.787)*95.047,
280 (y3
> 0.008856 ? y3
: (ys
-16.0/116.0)/7.787)*100.000,
281 (z3
> 0.008856 ? z3
: (zs
-16.0/116.0)/7.787)*108.883,
286 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
)); }
287 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
); }
288 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
); }
289 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
); }
293 // Hue/Saturation/Lighting color
296 string
toString () const nothrow @trusted {
297 import core
.stdc
.stdio
: snprintf
;
298 char[256] buf
= void;
299 auto len
= snprintf(buf
.ptr
, buf
.length
, "CHSL(%g,%g,%g)", cast(double)h
, cast(double)s
, cast(double)l
);
300 return buf
[0..len
].idup
;
303 private nothrow @safe @nogc:
304 void fromColor(C
) (in auto ref C c
) pure {
305 static if (is(C
== SRGB
)) {
306 enum Weighted
= true;
307 immutable double r1
= clamp(c
.r
);
308 immutable double g1
= clamp(c
.g
);
309 immutable double b1
= clamp(c
.b
);
310 } else static if (is(C
== RGB8
)) {
311 enum Weighted
= true;
312 immutable double r1
= c
.r
/255.0;
313 immutable double g1
= c
.g
/255.0;
314 immutable double b1
= c
.b
/255.0;
315 } else static if (is(C
: CXYZImpl
!M
, string M
)) {
316 enum Weighted
= false;
317 immutable double r1
= clamp(c
.r
);
318 immutable double g1
= clamp(c
.g
);
319 immutable double b1
= clamp(c
.b
);
321 immutable cc
= cast(CXYZD65
)c
;
322 enum Weighted
= false;
323 immutable double r1
= clamp(cc
.r
);
324 immutable double g1
= clamp(cc
.g
);
325 immutable double b1
= clamp(cc
.b
);
328 double maxColor
= r1
;
329 if (g1
> maxColor
) maxColor
= g1
;
330 if (b1
> maxColor
) maxColor
= b1
;
332 double minColor
= r1
;
333 if (g1
< minColor
) minColor
= g1
;
334 if (b1
< minColor
) minColor
= b1
;
336 static if (Weighted
&& false) {
337 // the colors don't affect the eye equally
338 // this is a little more accurate than plain HSL numbers
339 l
= 0.2126*r1
+0.7152*g1
+0.0722*b1
;
341 l
= (maxColor
+minColor
)/2.0;
343 if (maxColor
!= minColor
) {
345 s
= (maxColor
-minColor
)/(maxColor
+minColor
);
347 s
= (maxColor
-minColor
)/(2.0-maxColor
-minColor
);
349 if (r1
== maxColor
) {
350 h
= (g1
-b1
)/(maxColor
-minColor
);
351 } else if(g1
== maxColor
) {
352 h
= 2.0+(b1
-r1
)/(maxColor
-minColor
);
354 h
= 4.0+(r1
-g1
)/(maxColor
-minColor
);
363 C
toColor(C
) () const {
364 static double hue (double h
, double m1
, double m2
) pure nothrow @safe @nogc {
367 if (h
< 1.0/6.0) return m1
+(m2
-m1
)*h
*6.0;
368 if (h
< 3.0/6.0) return m2
;
369 if (h
< 4.0/6.0) return m1
+(m2
-m1
)*(2.0/3.0-h
)*6.0;
372 import std
.math
: modf
;
374 double sh
= modf(h
, tmpi
);
375 if (sh
< 0.0f) sh
+= 1.0f;
376 double ss
= clamp(s
);
377 double sl
= clamp(l
);
378 immutable double m2
= (sl
<= 0.5 ? sl
*(1+ss
) : sl
+ss
-sl
*ss
);
379 immutable double m1
= 2*sl
-m2
;
381 static if (is(C
== SRGB
)) {
383 clamp(hue(sh
+1.0/3.0, m1
, m2
)),
384 clamp(hue(sh
, m1
, m2
)),
385 clamp(hue(sh
-1.0/3.0, m1
, m2
)),
387 } else static if (is(C
== RGB8
)) {
389 cast(int)(hue(sh
+1.0/3.0, m1
, m2
)*255.0),
390 cast(int)(hue(sh
, m1
, m2
)*255.0),
391 cast(int)(hue(sh
-1.0/3.0, m1
, m2
)*255.0),
393 } else static if (is(C
: CXYZImpl
!M
, string M
)) {
395 clamp(hue(sh
+1.0/3.0, m1
, m2
)),
396 clamp(hue(sh
, m1
, m2
)),
397 clamp(hue(sh
-1.0/3.0, m1
, m2
)),
400 return cast(C
)CXYZD65(
401 clamp(hue(sh
+1.0/3.0, m1
, m2
)),
402 clamp(hue(sh
, m1
, m2
)),
403 clamp(hue(sh
-1.0/3.0, m1
, m2
)),
408 public nothrow @safe @nogc:
409 float h
= 0, s
= 0, l
= 0; // [0..1]
411 this (in float ah
, in float as
, in float al
) { pragma(inline
, true); h
= ah
; s
= as
; l
= al
; }
412 this() (in auto ref CHSL c
) { pragma(inline
, true); h
= c
.h
; s
= c
.s
; l
= c
.l
; }
413 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
); }
415 ref opAssign(C
:CHSL
) (in auto ref C c
) { pragma(inline
, true); h
= c
.h
; s
= c
.s
; l
= c
.l
; return this; }
416 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; }
418 CHSL
opCast(C
:CHSL
) () const { pragma(inline
, true); return CHSL(h
, s
, l
); }
419 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
; }
421 //CHSL darken (in float n) const { pragma(inline, true); return CHSL(clamp(h*n), s, l); }
424 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
); }
425 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
); }
428 static T
clamp(T
) (in T a
) { pragma(inline
, true); return (a
< 0 ?
0 : a
> 1 ?
1 : a
); }
432 version(iv_color_unittest
) unittest {
435 auto s0
= SRGB(1, 128/255.0, 0);
436 auto l0
= cast(CXYZD65
)s0
;
437 auto l1
= cast(CXYZLinear
)s0
;
438 auto s1
= cast(SRGB
)l0
;
439 auto s2
= cast(SRGB
)l1
;
440 writeln("s0=", s0
, " : ", cast(RGB8
)s0
);
446 writeln("black XYZ=", cast(CXYZD65
)SRGB(0, 0, 0));
447 writeln("white XYZ=", cast(CXYZD65
)SRGB(1, 1, 1));
448 writeln("MUST BE =CXYZ(0.9642,1,0.8249)");
449 //writeln("white XYZ=", cast(linear)sRGB(1));
451 auto lab
= cast(CLAB
)s0
;
452 writeln("srgb->lab->srgb: ", cast(SRGB
)lab
, " : ", cast(RGB8
)lab
);
453 writeln("rgb: ", s0
, " : ", cast(RGB8
)s0
);
454 writeln("lab: ", lab
);
455 writeln("rgb: ", cast(SRGB
)lab
);
457 auto z1
= cast(SRGB
)lab
; //cast(SRGB)CLAB(lab.l-0.01, lab.a, lab.b);
458 writeln("rgbX: ", z1
, " : ", cast(RGB8
)z1
);
459 writeln("xxx: ", cast(CLAB
)cast(CXYZD65
)RGB8(255-16, 128-16, 0));
463 writeln("============");
464 auto s0
= RGB8(255, 127, 0);
465 writeln("*s0: ", s0
, " : ", cast(RGB8
)s0
);
466 auto h0
= cast(CHSL
)s0
;
467 writeln("*h0: ", h0
);
469 writeln("*s1: ", h0
, " : ", cast(RGB8
)h0
);
470 writeln(RGB8(255-25, 127-25, 0-25));
474 writeln("============");
475 auto s0
= cast(CXYZD65
)RGB8(255, 127, 0);
476 writeln("*s0: ", s0
, " : ", cast(RGB8
)s0
);
477 auto s1
= s0
.darken(0.1);
478 writeln("*s1: ", s0
, " : ", cast(RGB8
)s0
);
479 writeln(RGB8(255-25, 127-25, 0-25));