root/trunk/helix/color.d

Revision 7, 32.6 kB (checked in by nail, 3 years ago)

Added isSigned, isUnsigned templates to basic.d
Removed silly assert(0) from color.d unittest

Line 
1 /*
2 Redistribution and use in source and binary forms, with or without
3 modification, are permitted provided that the following conditions
4 are met:
5
6     Redistributions of source code must retain the above copyright
7     notice, this list of conditions and the following disclaimer.
8
9     Redistributions in binary form must reproduce the above
10     copyright notice, this list of conditions and the following
11     disclaimer in the documentation and/or other materials provided
12     with the distribution.
13
14     Neither name of Victor Nakoryakov nor the names of
15     its contributors may be used to endorse or promote products
16     derived from this software without specific prior written
17     permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 Copyright (C) 2006. Victor Nakoryakov.
33 */
34 /**
35 Module with classes and functions for working with _color values.
36
37 There are structs for representation Red Green Blue _color (Color3),
38 RGB+Alpha _color (Color4) and Hue Saturation Luminance triple (HSL).
39
40 All components of those structs are float values, not integers.
41 Rationale is that under different circumstances it is necessary to
42 work with different standards of integer representation. Frequently
43 one byte-wise integer layout needed for one API and another for second.
44 One can require XRGB order, another BGRA. So it's better to operate with
45 floats and to convert them to integer just when it is necessary.
46
47 Normal range for float components' values is [0; 1]. Normal range for integer
48 values is [0; 255] for Color3 and Color4, and [0; 240] for HSL. Each struct
49 has several methods to convert native float representation to integer and
50 back.
51
52 Authors:
53     Victor Nakoryakov, nail-mail[at]mail.ru
54 */
55
56 module helix.color;
57
58 private import helix.basic,
59                helix.config;
60
61 /** Defines bytes orders for float to uint conversions. */
62 enum ByteOrder
63 {
64     XRGB,        ///
65     XBGR,        /// ditto
66     RGBX,        /// ditto
67     BGRX,        /// ditto
68     ARGB = XRGB, /// ditto
69     ABGR = XBGR, /// ditto
70     RGBA = RGBX, /// ditto
71     BGRA = BGRX  /// ditto
72 }
73
74 /**
75 Wrapper template to provide possibility to use different float types
76 in implemented structs and routines.
77 */
78 private template Color(float_t)
79 {
80     private alias helix.basic.clampBelow  clampBelow;
81     private alias helix.basic.clampAbove  clampAbove;
82     private alias helix.basic.clamp       clamp;
83     private alias helix.basic.equal       equal;
84
85     private alias .Color!(float).HSL      HSLf;
86     private alias .Color!(float).Color3   Color3f;
87     private alias .Color!(float).Color4   Color4f;
88
89     private alias .Color!(double).HSL     HSLd;
90     private alias .Color!(double).Color3  Color3d;
91     private alias .Color!(double).Color4  Color4d;
92
93     private alias .Color!(real).HSL       HSLr;
94     private alias .Color!(real).Color3    Color3r;
95     private alias .Color!(real).Color4    Color4r;
96
97     private static const float_t rgbK = 255;
98     private static const float_t hslK = 240;
99
100     /************************************************************************************
101     Hue, Saturation, Luminance triple.
102     *************************************************************************************/
103     struct HSL
104     {
105         float_t h; /// Hue.
106         float_t s; /// Saturation.
107         float_t l; /// Luminance.
108
109         /**
110         Method to construct struct in C-like syntax.
111
112         Examples:
113         ------------
114         HSL hsl = HSL(0.1, 0.2, 0.3);
115         ------------
116         */
117         static HSL opCall(float_t h, float_t s, float_t l)
118         {
119             HSL hsl;
120             hsl.set(h, s, l);
121             return hsl;
122         }
123
124         /** Sets components to values of passed arguments. */
125         void set(float_t h, float_t s, float_t l)
126         {
127             this.h = h;
128             this.s = s;
129             this.l = l;
130         }
131
132         /** Returns: Integer value of corresponding component in range [0; 240]. */
133         uint hi()
134         {
135             return cast(uint)(h * hslK);
136         }
137
138         /** ditto */
139         uint si()
140         {
141             return cast(uint)(s * hslK);
142         }
143
144         /** ditto */
145         uint li()
146         {
147             return cast(uint)(l * hslK);
148         }
149
150         /**
151         Set components to values of passed arguments. It is assumed that values of
152         arguments are in range [0; 240].
153         */
154         void hi(uint h)
155         {
156             this.h = cast(float_t)h / hslK;
157         }
158
159         /** ditto */
160         void si(uint s)
161         {
162             this.s = cast(float_t)s / hslK;
163         }
164
165         /** ditto */
166         void li(uint l)
167         {
168             this.l = cast(float_t)l / hslK;
169         }
170
171         /** Component-wise equality operator. */
172         bool opEquals(HSL hsl)
173         {
174             return h == hsl.h && s == hsl.s && l == hsl.l;
175         }
176
177         /** Returns: Color3 representing the same color as this triple. */
178         Color3 toColor3()
179         {
180             const short rgbMax = cast(short)rgbK;
181             const short hslMax = cast(short)hslK;
182             short HueToRGB(short n1, short n2, short hue)
183             {
184                 // range check: note values passed add/subtract thirds of range
185                 if (hue < 0)
186                     hue += hslMax;
187
188                 if (hue > hslMax)
189                     hue -= hslMax;
190
191                 // return r,g, or b value from this tridrant
192                 if (hue < hslMax / 6)
193                     return n1 + ((n2 - n1) * hue + hslMax / 12) / (hslMax / 6);
194                 if (hue < hslMax / 2)
195                     return n2;
196                 if (hue < hslMax * 2 / 3)
197                     return n1 + ((n2 - n1) * ((hslMax * 2/3) - hue) + (hslMax / 12)) / (hslMax / 6);
198                 else
199                     return n1;
200             }
201
202             short hue = cast(short)hi;
203             short lum = cast(short)li;
204             short sat = cast(short)si;
205             short magic1, magic2; // calculated magic numbers
206
207             Color3 ret;
208
209             if (sat == 0) // achromatic case
210             {
211                 ret.set(l, l, l);
212             }
213             else // chromatic case
214             {
215                 // set up magic numbers
216                 if (lum <= hslMax / 2)
217                     magic2 = (lum * (hslMax + sat) + hslMax / 2) / hslMax;
218                 else
219                     magic2 = lum + sat - (lum * sat + hslMax / 2) / hslMax;
220
221                 magic1 = 2 * lum - magic2;
222
223                 // get RGB, change units from hslMax to [0; 1] range
224                 ret.r = cast(float_t)(HueToRGB(magic1, magic2, hue + (hslMax / 3)) * rgbMax + hslMax / 2) / hslK / rgbK;
225                 ret.g = cast(float_t)(HueToRGB(magic1, magic2, hue) * rgbMax + hslMax / 2) / hslK / rgbK;
226                 ret.b = cast(float_t)(HueToRGB(magic1, magic2, hue - (hslMax / 3)) * rgbMax + hslMax / 2) / hslK / rgbK;
227             }
228
229             return ret;
230         }
231     }
232
233     /**
234     Approximate equality function.
235     Params:
236         relprec, absprec = Parameters passed to equal function while calculations.
237                            Have the same meaning as in equal function.
238     */
239     bool equal(HSL a, HSL b, int relprec = defrelprec, int absprec = defabsprec)
240     {
241         HSL c;
242         c.set(a.h - b.h, a.s - b.s, a.l - b.l);
243         return .equal(c.h * c.h + c.s * c.s + c.l * c.l, 0, relprec, absprec);
244     }
245
246     /************************************************************************************
247     Red, Green, Blue triple.
248     *************************************************************************************/
249     struct Color3
250     {
251         align(1)
252         {
253             float_t r; /// Red.
254             float_t g; /// Green.
255             float_t b; /// Blue.
256         }
257
258         /// Color3 with all components seted to NaN.
259         static Color3 nan = { float_t.nan, float_t.nan, float_t.nan };
260
261         /**
262         Method to construct color in C-like syntax.
263
264         Examples:
265         ------------
266         Color3 c = Color3(0.1, 0.2, 0.3);
267         ------------
268         */
269         static Color3 opCall(float_t r, float_t g, float_t b)
270         {
271             Color3 v;
272             v.set(r, g, b);
273             return v;
274         }
275
276         /**
277         Method to construct color in C-like syntax from value specified
278         in uint parameter.
279
280         Params:
281             src     = uint to extract value from.
282             order   = specifies byte-wise _order in src.
283
284         Examples:
285         ------------
286         Color3 c = Color3(0x00FFEEDD, ByteOrder.XRGB);
287         ------------
288         */
289         static Color3 opCall(uint src, ByteOrder order)
290         {
291             Color3 v;
292             v.set(src, order);
293             return v;
294         }
295
296         /** Sets components to values of passed arguments. */
297         void set(float_t r, float_t g, float_t b)
298         {
299             this.r = r;
300             this.g = g;
301             this.b = b;
302         }
303
304         /**
305         Sets components according to color packed in src uint argument.
306
307         Params:
308             src     = uint to extract value from.
309             order   = specifies byte-wise component layout in src.
310         */
311         void set(uint src, ByteOrder order = ByteOrder.XRGB)
312         {
313             switch (order)
314             {
315                 case ByteOrder.XRGB:
316                     ri = (src & 0x00FF0000) >> 16;
317                     gi = (src & 0x0000FF00) >> 8;
318                     bi = (src & 0x000000FF) >> 0;
319                     break;
320
321                 case ByteOrder.XBGR:
322                     bi = (src & 0x00FF0000) >> 16;
323                     gi = (src & 0x0000FF00) >> 8;
324                     ri = (src & 0x000000FF) >> 0;
325                     break;
326
327                 case ByteOrder.RGBX:
328                     ri = (src & 0xFF000000) >>> 24;
329                     gi = (src & 0x00FF0000) >>> 16;
330                     bi = (src & 0x0000FF00) >>> 8;
331                     break;
332
333                 case ByteOrder.BGRX:
334                     bi = (src & 0xFF000000) >>> 24;
335                     gi = (src & 0x00FF0000) >>> 16;
336                     ri = (src & 0x0000FF00) >>> 8;
337                     break;
338             }
339         }
340
341         /** Returns: Whether all components are normalized numbers. */
342         bool isnormal()
343         {
344             return std.math.isnormal(r) && std.math.isnormal(g) && std.math.isnormal(b);
345         }
346
347         /**
348         Returns: Integer value of corresponding component.
349
350         Float value 0 is mapped to integer 0. Float value 1 is mapped to
351         integer 255.
352         */
353         int ri()
354         {
355             return cast(int)(r * rgbK);
356         }
357
358         /** ditto */
359         int gi()
360         {
361             return cast(int)(g * rgbK);
362         }
363
364         /** ditto */
365         int bi()
366         {
367             return cast(int)(b * rgbK);
368         }
369
370         /**
371         Sets corresponding component value to mapped value of passed argument.
372
373         Integer value 0 is mapped to float 0. Integer value 255 is mapped to
374         float 1.
375         */
376         void ri(int r)
377         {
378             this.r = cast(float_t)r / rgbK;
379         }
380
381         /** ditto */
382         void gi(int g)
383         {
384             this.g = cast(float_t)g / rgbK;
385         }
386
387         /** ditto */
388         void bi(int b)
389         {
390             this.b = cast(float_t)b / rgbK;
391         }
392
393         /**
394         Returns:
395             This color packed to uint.
396         Params:
397             order = specifies byte-wise component layout in src.
398         Throws:
399             AssertError if any component is out of range [0; 1] and module was
400             compiled with asserts.
401         */
402         uint toUint(ByteOrder order)
403         {
404             assert(ri >= 0 && ri < 256);
405             assert(gi >= 0 && gi < 256);
406             assert(bi >= 0 && bi < 256);
407
408             switch (order)
409             {
410                 case ByteOrder.XRGB: return (ri << 16) | (gi <<  8) | (bi << 0);
411                 case ByteOrder.XBGR: return (bi << 16) | (gi <<  8) | (ri << 0);
412                 case ByteOrder.RGBX: return (ri << 24) | (gi << 16) | (bi << 8);
413                 case ByteOrder.BGRX: return (bi << 24) | (gi << 16) | (ri << 8);
414             }
415
416             return 0;
417         }
418
419         /**
420         Returns:
421             HSL triple representing same color as this.
422         */
423         HSL toHSL()
424         {
425             const short hslMax = cast(short)hslK;
426             const short rgbMax = cast(short)rgbK;
427
428             ubyte h, s, l;
429             ubyte cMax, cMin;                // max and min RGB values
430             short rDelta, gDelta, bDelta;    // intermediate value: % of spread from max
431
432             // get R, G, and B out of DWORD
433             short r = ri;
434             short g = gi;
435             short b = bi;
436
437             // calculate lightness
438             cMax = max(max(r, g), b);
439             cMin = min(min(r, g), b);
440             l = ((cMax + cMin) * hslMax + rgbMax) / (2 * rgbMax);
441
442             if (cMax == cMin)                // r = g = b --> achromatic case
443             {
444                 h = s = 0;
445             }
446             else
447             {                                // chromatic case
448                 // saturation
449                 if (l <= hslMax / 2)
450                     s = ((cMax - cMin) * hslMax + (cMax + cMin) / 2) / (cMax + cMin);
451                 else
452                     s = ((cMax - cMin) * hslMax + (2 * rgbMax - cMax - cMin) / 2) / (2 * rgbMax - cMax - cMin);
453
454                 // hue
455                 rDelta = ((cMax - r) * (hslMax / 6) + (cMax - cMin) / 2) / (cMax - cMin);
456                 gDelta = ((cMax - g) * (hslMax / 6) + (cMax - cMin) / 2) / (cMax - cMin);
457                 bDelta = ((cMax - b) * (hslMax / 6) + (cMax - cMin) / 2) / (cMax - cMin);
458
459                 if (r == cMax)
460                     h = bDelta - gDelta;
461                 else if (g == cMax)
462                     h = (hslMax / 3) + rDelta - bDelta;
463                 else // B == cMax
464                     h = (2 * hslMax) / 3 + gDelta - rDelta;
465
466                 if(h < 0)
467                     h += hslMax;
468
469                 if(h > hslMax)
470                     h -= hslMax;
471             }
472
473             HSL ret;
474             ret.hi = h;
475             ret.si = s;
476             ret.li = l;
477             return ret;
478         }
479
480         /** Returns: float_t pointer to r component of this color. It's like a _ptr method for arrays. */
481         float_t* ptr()
482         {
483             return cast(float_t*)this;
484         }
485
486         /**
487         Standard operators that have meaning exactly the same as for Vector3, i.e. do
488         component-wise operations.
489
490         Note that division operators do no cheks of value of k, so in case of division
491         by 0 result vector will have infinity components. You can check this with isnormal()
492         method.
493         */
494         bool opEquals(Color3 v)
495         {
496             return r == v.r && g == v.g && b == v.b;
497         }
498
499         /** ditto */
500         Color3 opNeg()
501         {
502             return Color3(-r, -g, -b);
503         }
504
505         /** ditto */
506         Color3 opAdd(Color3 v)
507         {
508             return Color3(r + v.r, g + v.g, b + v.b);
509         }
510
511         /** ditto */
512         void opAddAssign(Color3 v)
513         {
514             r += v.r;
515             g += v.g;
516             b += v.b;
517         }
518
519         /** ditto */
520         Color3 opSub(Color3 v)
521         {
522             return Color3(r - v.r, g - v.g, b - v.b);
523         }
524
525         /** ditto */
526         void opSubAssign(Color3 v)
527         {
528             r -= v.r;
529             g -= v.g;
530             b -= v.b;
531         }
532
533         /** ditto */
534         Color3 opMul(real k)
535         {
536             return Color3(r * k, g * k, b * k);
537         }
538
539         /** ditto */
540         void opMulAssign(real k)
541         {
542             r *= k;
543             g *= k;
544             b *= k;
545         }
546
547         /** ditto */
548         Color3 opMulr(real k)
549         {
550             return Color3(r * k, g * k, b * k);
551         }
552
553         /** ditto */
554         Color3 opDiv(real k)
555         {
556             return Color3(r / k, g / k, b / k);
557         }
558
559         /** ditto */
560         void opDivAssign(real k)
561         {
562             r /= k;
563             g /= k;
564             b /= k;
565         }
566
567         /** Sets all components less than inf to inf. */
568         void clampBelow(float_t inf = 0)
569         {
570             .clampBelow(r, inf);
571             .clampBelow(g, inf);
572             .clampBelow(b, inf);
573         }
574
575         /** Returns: Copy of this color with all components less than inf seted to inf. */
576         Color3 clampedBelow(float_t inf = 0)
577         {
578             Color3 ret = *this;
579             ret.clampBelow(inf);
580             return ret;
581         }
582
583         /** Sets all components greater than sup to sup. */
584         void clampAbove(float_t sup = 1)
585         {
586             .clampAbove(r, sup);
587             .clampAbove(g, sup);
588             .clampAbove(b, sup);
589         }
590
591         /** Returns: Copy of this color with all components greater than sup seted to sup. */
592         Color3 clampedAbove(float_t sup = 1)
593         {
594             Color3 ret = *this;
595             ret.clampBelow(sup);
596             return ret;
597         }
598
599         /**
600         Sets all components less than inf to inf and
601         all components greater than sup to sup.
602         */
603         void clamp(float_t inf = 0, float_t sup = 1)
604         {
605             clampBelow(inf);
606             clampAbove(sup);
607         }
608
609         /**
610         Returns:
611             Copy of this color with all components less than inf seted to inf
612             and all components greater than sup seted to sup.
613         */
614         Color3 clamped(float_t inf = 0, float_t sup = 1)
615         {
616             Color3 ret = *this;
617             ret.clamp(inf, sup);
618             return ret;
619         }
620
621         /** Returns: Copy of this color with float type components. */
622         Color3f toColor3f()
623         {
624             return Color3f(cast(float)r, cast(float</