1 /* hsv2rgb.i
2  * conversions among RGB, HSV, HSL, CMYK color representations
3  *
4  * See Wikipedia article "HSL and HSV"
5  */
6 /* Copyright (c) 2013, David H. Munro.
7  * All rights reserved.
8  * This file is part of yorick (http://yorick.sourceforge.net).
9  * Read the accompanying LICENSE file for details.
10  */
11 
12 func rgb2hsv(r, g, b, cmax=)
13 /* DOCUMENT hsv = rgb2hsv([r, g, b])
14  *       or hsv = rgb2hsv(r, g, b)
15  *  Returns hue-saturation-value given red-green-blue.
16  *  Return value has same dimensions as input (in first form).
17  *  If input rgb are real, they are assumed normalized to lie in [0.,1.].
18  *  If input are integers, they are assumed to lie in [0,255].
19  *  You can specify a different maximum color value using the cmax=
20  *  keyword.
21  *  Note that the HSV and HSL systems share a common definition of
22  *  hue H, but that saturation S is a very different thing.
23  * SEE ALSO: rgb2hsl, hsv2rgb, hsl2rgb, rgb2cmyk, cmyk2rgb, rgb2hsi
24  */
25 {
26   local rgb, V, m, C, L, H;
27   _2hs;
28   S = C / max(V, 1.e-20);  /* hsv saturation */
29   return [H, S, V];
30 }
31 
32 func rgb2hsl(r, g, b, cmax=)
33 /* DOCUMENT hsl = rgb2hsl([r, g, b])
34  *       or hsl = rgb2hsl(r, g, b)
35  *  Returns hue-saturation-lightness given red-green-blue.
36  *  Return value has same dimensions as input (in first form).
37  *  If input rgb are real, they are assumed normalized to lie in [0.,1.].
38  *  If input are integers, they are assumed to lie in [0,255].
39  *  You can specify a different maximum color value using the cmax=
40  *  keyword.
41  *  Note that the HSV and HSL systems share a common definition of
42  *  hue H, but that saturation S is a very different thing.
43  * SEE ALSO: rgb2hsv, hsv2rgb, hsl2rgb, rgb2cmyk, cmyk2rgb, rgb2hsi
44  */
45 {
46   local rgb, V, m, C, L, H;
47   _2hs;
48   S = C / max(1.-abs(2.*L-1.), 1.e-20);  /* hsl saturation */
49   return [H, S, L];
50 }
51 
52 func hsv2rgb(H, S, V, cmax=)
53 /* DOCUMENT rgb = hsv2rgb([h, s, v])
54  *       or rgb = hsv2rgb(h, s, v)
55  *  Returns red-green-blue given hue-saturation-value.
56  *  Return value has same dimensions as input (in first form).
57  *  Output rgb are type char by default, with range [0,255].
58  *  You can specify a different maximum color value using the cmax=
59  *  keyword.  With cmax=1, return value will be type double, with
60  *  range [0.,1.]; otherwise, the return value will be double if cmax
61  *  is real, char if cmax is an integer <=255, otherwise long.
62  *  Note that the HSV and HSL systems share a common definition of
63  *  hue H, but that saturation S is a very different thing.
64  * SEE ALSO: hsl2rgb, rgb2hsv, rgb2hsl, rgb2cmyk, cmyk2rgb, rgb2hsi
65  */
66 {
67   if (is_void(S)) {
68     S = H(..,2);
69     V = H(..,3);
70     H = H(..,1);
71   }
72   C = V*S;
73   return _2rgb(H, C, V-C, cmax=cmax);
74 }
75 
76 func hsl2rgb(H, S, L, cmax=)
77 /* DOCUMENT rgb = hsl2rgb([h, s, l])
78  *       or rgb = hsl2rgb(h, s, l)
79  *  Returns red-green-blue given hue-saturation-lightness.
80  *  Return value has same dimensions as input (in first form).
81  *  Output rgb are type char by default, with range [0,255].
82  *  You can specify a different maximum color value using the cmax=
83  *  keyword.  With cmax=1, return value will be type double, with
84  *  range [0.,1.]; otherwise, the return value will be double if cmax
85  *  is real, char if cmax is an integer <=255, otherwise long.
86  *  Note that the HSV and HSL systems share a common definition of
87  *  hue H, but that saturation S is a very different thing.
88  * SEE ALSO: hsv2rgb, rgb2hsl, rgb2hsv, rgb2cmyk, cmyk2rgb, rgb2hsi
89  */
90 {
91   if (is_void(S)) {
92     S = H(..,2);
93     L = H(..,3);
94     H = H(..,1);
95   }
96   C = (1.-abs(2.*L-1.)) * S;
97   return _2rgb(H, C, L-0.5*C, cmax=cmax);
98 }
99 
100 func _2rgb(H, C, m, cmax=)
101 {
102   H *= 1./60.;
103   H -= 6.*floor(H/6.);  /* permit illegal hue >360 or <0 */
104   n = array(0, dimsof(H));
105   nc = numberof(H);
106   n(*) = indgen(nc);
107   rgb = array(0., dimsof(H), 3);
108   i = digitize(H, indgen(5)) % 6;
109   j = i / 2;
110   i = (j + [2,1]((i&1)+1)) % 3;
111   rgb(n+j*nc) = C;
112   rgb(n+i*nc) = C * (1. - abs(H%2. - 1.));
113   rgb += m;
114   if (is_void(cmax)) cmax = 255;
115   if (cmax == 1) cmax = 1.0;
116   rgb *= cmax;
117   if (structof(cmax+0) == long) {
118     rgb = long(rgb + 0.5);
119     if (max(cmax) < 256) rgb = char(rgb);
120   }
121   return rgb;
122 }
123 
124 func _2hs
125 {
126   extern rgb, V, m, C, L, H;
127   rgb = is_void(g)? r : [r, g, b];
128   if (is_void(cmax)) cmax = (structof(r+0)==long)? 255. : 1.0;
129   rgb *= 1./cmax;
130   r = rgb(..,1);
131   g = rgb(..,2);
132   b = rgb(..,3);
133   V = rgb(..,max); /* value */
134   m = rgb(..,min);
135   if (max(V) > 1.) error, "need explicit cmax= specification";
136   if (min(m) < 0.) error, "rgb color component less than zero";
137   C = V - m;       /* chroma */
138   L = 0.5*(V+m);   /* lightness */
139   n = array(0, dimsof(r));
140   nr = numberof(r);
141   n(*) = indgen(nr);
142   i = (V == rgb)(..,mxx) - 1;
143   H = ((rgb(n+((i+1)%3)*nr) - rgb(n+((i+2)%3)*nr))/max(C,1.e-20) + 2.*i+6.)%6.;
144   H *= 60.;        /* hue */
145 }
146 
147 func rgb2cmyk(r, g, b, cmax=)
148 /* DOCUMENT cmyk = rgb2cmyk([r, g, b])
149  *       or cmyk = rgb2cmyk(r, g, b)
150  *  Returns cyan-magenta-yellow-black given red-green-blue.  The
151  * returned [c, m, y, k] are normalized to lie in [0., 1.].
152  *  Return value has same dimensions as input (in first form).
153  *  If input rgb are real, they are assumed normalized to lie in [0.,1.].
154  *  If input are integers, they are assumed to lie in [0,255].
155  *  You can specify a different maximum color value using the cmax=
156  *  keyword.
157  *  In this simple conversion scheme, the complement of the largest
158  *  component of [r,g,b] will always be 0.0.  This is the choice with
159  *  the largest possible k component.
160  * SEE ALSO: cmyk2rgb, rgb2hsv, hsv2rgb, rgb2hsl, hsl2rgb, rgb2hsi
161  */
162 {
163   rgb = is_void(g)? r : [r, g, b];
164   if (is_void(cmax)) cmax = (structof(r+0)==long)? 255. : 1.0;
165   rgb *= 1./cmax;
166   V = rgb(..,max);    /* value */
167   zero = double(!V);
168   rgb *= (1. + zero) / (V + zero);
169   c = 1. - rgb(..,1);
170   m = 1. - rgb(..,2);
171   y = 1. - rgb(..,3);
172   k = 1. - V;
173   return [c, m, y, k];
174 }
175 
176 func cmyk2rgb(c, m, y, k, cmax=)
177 /* DOCUMENT rgb = cmyk2rgb([c, m, y, k])
178  *       or rgb = cmyk2rgb(c, m, y, k)
179  *  Returns red-green-blue given cyan-magenta-yellow-black.  The
180  *  C, M, Y, K values must be between 0.0 and 1.0 inclusive.
181  *  Return value has same dimensions as input (in first form).
182  *  Output rgb are type char by default, with range [0,255].
183  *  You can specify a different maximum color value using the cmax=
184  *  keyword.  With cmax=1, return value will be type double, with
185  *  range [0.,1.]; otherwise, the return value will be double if cmax
186  *  is real, char if cmax is an integer <=255, otherwise long.
187  * SEE ALSO: rgb2cmyk, hsv2rgb, rgb2hsv, hsl2rgb, rgb2hsl, rgb2hsi
188  */
189 {
190   rgb = 1. - (is_void(m)? c : [c, m, y, k]);
191   rgb = rgb(,1:3) * rgb(4);
192   if (is_void(cmax)) cmax = 255;
193   if (cmax == 1) cmax = 1.0;
194   rgb *= cmax;
195   if (structof(cmax+0) == long) {
196     rgb = long(rgb + 0.5);
197     if (max(cmax) < 256) rgb = char(rgb);
198   }
199   return rgb;
200 }
201 
202 /* ------------------------------------------------------------------------ */
203