round the hsv to rgb results a little more nicely for integer RGB
[imager.git] / color.c
1 #include "image.h"
2 #include <math.h>
3
4 /*
5 =head1 NAME
6
7 color.c - color manipulation functions
8
9 =head1 SYNOPSIS
10
11   i_fcolor color;
12   i_rgb_to_hsvf(&color);
13   i_hsv_to_rgbf(&color);
14
15 =head1 DESCRIPTION
16
17 A collection of utility functions for converting between color spaces.
18
19 */
20
21 #define EPSILON (1e-8)
22
23 #define my_max(a, b) ((a) < (b) ? (b) : (a))
24 #define my_min(a, b) ((a) > (b) ? (b) : (a))
25
26 /*
27 =item i_rgb2hsvf(&color)
28
29 Converts the first 3 channels of color into hue, saturation and value.
30
31 Each value is scaled into the range 0 to 1.0.
32
33 =cut
34 */
35 void i_rgb_to_hsvf(i_fcolor *color) {
36   double h, s, v;
37   double temp;
38   double Cr, Cg, Cb;
39
40   v = my_max(my_max(color->rgb.r, color->rgb.g), color->rgb.b);
41   temp = my_min(my_min(color->rgb.r, color->rgb.g), color->rgb.b);
42   if (v < EPSILON)
43     s = 0;
44   else
45     s = (v-temp)/v;
46   if (s == 0)
47     h = 0;
48   else {
49     Cr = (v - color->rgb.r)/(v-temp);
50     Cg = (v - color->rgb.g)/(v-temp);
51     Cb = (v - color->rgb.b)/(v-temp);
52     if (color->rgb.r == v)
53       h = Cb - Cg;
54     else if (color->rgb.g == v)
55       h = 2 + Cr - Cb;
56     else if (color->rgb.b == v)
57       h = 4 + Cg - Cr;
58     h = 60 * h;
59     if (h < 0)
60       h += 360;
61   }
62   color->channel[0] = h / 360.0;
63   color->channel[1] = s;
64   color->channel[2] = v;
65 }
66
67 /*
68 =item i_rgb2hsv(&color)
69
70 Converts the first 3 channels of color into hue, saturation and value.
71
72 Each value is scaled into the range 0 to 255.
73
74 =cut
75 */
76 void i_rgb_to_hsv(i_color *color) {
77   double h, s, v;
78   double temp;
79   double Cr, Cg, Cb;
80
81   v = my_max(my_max(color->rgb.r, color->rgb.g), color->rgb.b);
82   temp = my_min(my_min(color->rgb.r, color->rgb.g), color->rgb.b);
83   if (v == 0)
84     s = 0;
85   else
86     s = (v-temp)*255/v;
87   if (s == 0)
88     h = 0;
89   else {
90     Cr = (v - color->rgb.r)/(v-temp);
91     Cg = (v - color->rgb.g)/(v-temp);
92     Cb = (v - color->rgb.b)/(v-temp);
93     if (color->rgb.r == v)
94       h = Cb - Cg;
95     else if (color->rgb.g == v)
96       h = 2 + Cr - Cb;
97     else if (color->rgb.b == v)
98       h = 4 + Cg - Cr;
99     h = h * 60.0;
100     if (h < 0)
101       h += 360;
102   }
103   color->channel[0] = h * 255 / 360.0;
104   color->channel[1] = s;
105   color->channel[2] = v;
106 }
107
108 /*
109 =item i_hsv_to_rgbf(&color)
110
111 Convert a HSV value to an RGB value, each value ranges from 0 to 1.
112
113 =cut
114 */
115
116 void i_hsv_to_rgbf(i_fcolor *color) {
117   double h = color->channel[0];
118   double s = color->channel[1];
119   double v = color->channel[2];
120
121   if (color->channel[1] < EPSILON) {
122     /* ignore h in this case */
123     color->rgb.r = color->rgb.g = color->rgb.b = v;
124   }
125   else {
126     int i;
127     double f, m, n, k;
128     h = fmod(h, 1.0) * 6;
129     i = floor(h);
130     f = h - i;
131     m = v * (1 - s);
132     n = v * (1 - s * f);
133     k = v * (1 - s * (1 - f));
134     switch (i) {
135     case 0:
136       color->rgb.r = v; color->rgb.g = k; color->rgb.b = m;
137       break;
138     case 1:
139       color->rgb.r = n; color->rgb.g = v; color->rgb.b = m;
140       break;
141     case 2:
142       color->rgb.r = m; color->rgb.g = v; color->rgb.b = k;
143       break;
144     case 3:
145       color->rgb.r = m; color->rgb.g = n; color->rgb.b = v;
146       break;
147     case 4:
148       color->rgb.r = k; color->rgb.g = m; color->rgb.b = v;
149       break;
150     case 5:
151       color->rgb.r = v; color->rgb.g = m; color->rgb.b = n;
152       break;
153     }
154   }
155 }
156
157 /*
158 =item i_hsv_to_rgb(&color)
159
160 Convert a HSV value to an RGB value, each value ranges from 0 to 1.
161
162 =cut
163 */
164
165 void i_hsv_to_rgb(i_color *color) {
166   double h = color->channel[0];
167   double s = color->channel[1];
168   double v = color->channel[2];
169
170   if (color->channel[1] == 0) {
171     /* ignore h in this case */
172     color->rgb.r = color->rgb.g = color->rgb.b = v;
173   }
174   else {
175     int i;
176     double f;
177     int m, n, k;
178     h = h / 255.0 * 6;
179     i = h;
180     f = h - i;
181     m = 0.5 + v * (255 - s) / 255;
182     n = 0.5 + v * (255 - s * f) / 255;
183     k = 0.5 + v * (255 - s * (1 - f)) / 255;
184     switch (i) {
185     case 0:
186       color->rgb.r = v; color->rgb.g = k; color->rgb.b = m;
187       break;
188     case 1:
189       color->rgb.r = n; color->rgb.g = v; color->rgb.b = m;
190       break;
191     case 2:
192       color->rgb.r = m; color->rgb.g = v; color->rgb.b = k;
193       break;
194     case 3:
195       color->rgb.r = m; color->rgb.g = n; color->rgb.b = v;
196       break;
197     case 4:
198       color->rgb.r = k; color->rgb.g = m; color->rgb.b = v;
199       break;
200     case 5:
201       color->rgb.r = v; color->rgb.g = m; color->rgb.b = n;
202       break;
203     }
204   }
205 }
206
207 /*
208 =back
209
210 =head1 AUTHOR
211
212 Tony Cook <tony@develop-help.com>
213
214 =head1 SEE ALSO
215
216 Imager
217
218 =cut
219 */