Commit | Line | Data |
---|---|---|
92bda632 | 1 | #include "imager.h" |
efdc2568 | 2 | #include <math.h> |
6607600c TC |
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 | ||
efdc2568 TC |
23 | #define my_max(a, b) ((a) < (b) ? (b) : (a)) |
24 | #define my_min(a, b) ((a) > (b) ? (b) : (a)) | |
25 | ||
6607600c TC |
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) { | |
e4bf9335 | 36 | double h = 0, s, v; |
6607600c TC |
37 | double temp; |
38 | double Cr, Cg, Cb; | |
39 | ||
efdc2568 TC |
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); | |
6607600c TC |
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 | ||
efdc2568 TC |
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) { | |
e4bf9335 | 77 | double h = 0, s, v; |
efdc2568 TC |
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 | ||
6607600c TC |
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; | |
efdc2568 | 129 | i = floor(h); |
6607600c TC |
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 | ||
efdc2568 TC |
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; | |
87039ee8 TC |
176 | double f; |
177 | int m, n, k; | |
efdc2568 TC |
178 | h = h / 255.0 * 6; |
179 | i = h; | |
180 | f = h - i; | |
87039ee8 TC |
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; | |
efdc2568 TC |
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 | ||
6607600c TC |
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 | */ |