1 // Copyright (c) 2005, Niels Martin Hansen, Rodrigo Braz Monteiro
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of the Aegisub Group nor the names of its contributors
13 // may be used to endorse or promote products derived from this software
14 // without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 // POSSIBILITY OF SUCH DAMAGE.
27 //
28 // Aegisub Project http://www.aegisub.org/
29
30 /// @file colorspace.cpp
31 /// @brief Functions for converting colours between different representations
32 /// @ingroup utility
33 ///
34
35 #include "colorspace.h"
36 #include "utils.h"
37
clip_colorval(int val)38 static inline unsigned int clip_colorval(int val)
39 {
40 return mid(0, val, 255);
41 }
42
43 // Algorithm from http://130.113.54.154/~monger/hsl-rgb.html
hsl_to_rgb(int H,int S,int L,unsigned char * R,unsigned char * G,unsigned char * B)44 void hsl_to_rgb(int H, int S, int L, unsigned char *R, unsigned char *G, unsigned char *B)
45 {
46 if (S == 0) {
47 *R = L;
48 *G = L;
49 *B = L;
50 return;
51 }
52
53 if (L == 128 && S == 255) {
54 switch (H) {
55 case 0:
56 case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
57 *R = 255;
58 *G = 0;
59 *B = 0;
60 return;
61 case 43:
62 *R = 255;
63 *G = 255;
64 *B = 0;
65 return;
66 case 85:
67 *R = 0;
68 *G = 255;
69 *B = 0;
70 return;
71 case 128:
72 *R = 0;
73 *G = 255;
74 *B = 255;
75 return;
76 case 171:
77 *R = 0;
78 *G = 0;
79 *B = 255;
80 return;
81 case 213:
82 *R = 255;
83 *G = 0;
84 *B = 255;
85 return;
86 }
87 }
88
89 float h, s, l, r, g, b;
90 h = H / 255.f;
91 s = S / 255.f;
92 l = L / 255.f;
93
94 float temp2;
95 if (l < .5) {
96 temp2 = l * (1. + s);
97 } else {
98 temp2 = l + s - l*s;
99 }
100
101 float temp1 = 2.f * l - temp2;
102
103 // assume h is in range [0;1]
104 float temp3[3];
105 temp3[0] = h + 1.f/3.f;
106 if (temp3[0] > 1.f) temp3[0] -= 1.f;
107 temp3[1] = h;
108 temp3[2] = h - 1.f/3.f;
109 if (temp3[2] < 0.f) temp3[2] += 1.f;
110
111 if (6.f * temp3[0] < 1.f)
112 r = temp1 + (temp2 - temp1) * 6.f * temp3[0];
113 else if (2.f * temp3[0] < 1.f)
114 r = temp2;
115 else if (3.f * temp3[0] < 2.f)
116 r = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[0]) * 6.f;
117 else
118 r = temp1;
119
120 if (6.f * temp3[1] < 1.f)
121 g = temp1 + (temp2 - temp1) * 6.f * temp3[1];
122 else if (2.f * temp3[1] < 1.f)
123 g = temp2;
124 else if (3.f * temp3[1] < 2.f)
125 g = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[1]) * 6.f;
126 else
127 g = temp1;
128
129 if (6.f * temp3[2] < 1.f)
130 b = temp1 + (temp2 - temp1) * 6.f * temp3[2];
131 else if (2.f * temp3[2] < 1.f)
132 b = temp2;
133 else if (3.f * temp3[2] < 2.f)
134 b = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[2]) * 6.f;
135 else
136 b = temp1;
137
138 *R = clip_colorval((int)(r*255));
139 *G = clip_colorval((int)(g*255));
140 *B = clip_colorval((int)(b*255));
141 }
142
143 // Formulas taken from wikipedia: http://en.wikipedia.org/wiki/HSV_color_space
144 // The range for H is 0..255 instead of 0..359, so 60 degrees has been translated to 256/6 here
hsv_to_rgb(int H,int S,int V,unsigned char * R,unsigned char * G,unsigned char * B)145 void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigned char *B)
146 {
147 *R = *G = *B = 0;
148
149 // some special cases... oh yeah baby!
150 if (S == 255) {
151 switch (H) {
152 case 0:
153 case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
154 *R = V;
155 *G = 0;
156 *B = 0;
157 return;
158 case 43:
159 *R = V;
160 *G = V;
161 *B = 0;
162 return;
163 case 85:
164 *R = 0;
165 *G = V;
166 *B = 0;
167 return;
168 case 128:
169 *R = 0;
170 *G = V;
171 *B = V;
172 return;
173 case 171:
174 *R = 0;
175 *G = 0;
176 *B = V;
177 return;
178 case 213:
179 *R = V;
180 *G = 0;
181 *B = V;
182 return;
183 }
184 }
185
186 // Cap values
187 unsigned int h = H * 360;
188 unsigned int s = clip_colorval(S)*256;
189 unsigned int v = clip_colorval(V)*256;
190
191 // Saturation is zero, make grey
192 if (S == 0) {
193 *R = V;
194 *G = V;
195 *B = V;
196 return;
197 }
198
199 unsigned int r, g, b;
200
201 // Else, calculate color
202 unsigned int Hi = h / 60 / 256;
203 unsigned int f = h / 60 - Hi * 256;
204 unsigned int p = v * (65535 - s) / 65536;
205 unsigned int q = v * (65535 - (f * s)/256) / 65536;
206 unsigned int t = v * (65535 - ((255 - f) * s)/256) / 65536;
207 switch (Hi) {
208 case 0:
209 r = v;
210 g = t;
211 b = p;
212 break;
213 case 1:
214 r = q;
215 g = v;
216 b = p;
217 break;
218 case 2:
219 r = p;
220 g = v;
221 b = t;
222 break;
223 case 3:
224 r = p;
225 g = q;
226 b = v;
227 break;
228 case 4:
229 r = t;
230 g = p;
231 b = v;
232 break;
233 case 5:
234 default:
235 r = v;
236 g = p;
237 b = q;
238 break;
239 }
240
241 *R = clip_colorval(r/256);
242 *G = clip_colorval(g/256);
243 *B = clip_colorval(b/256);
244 }
245
246 /// Algorithm from http://130.113.54.154/~monger/hsl-rgb.html
rgb_to_hsl(int R,int G,int B,unsigned char * H,unsigned char * S,unsigned char * L)247 void rgb_to_hsl(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *L)
248 {
249 float r = R/255.f, g = G/255.f, b = B/255.f;
250 float h, s, l;
251
252 float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
253
254 l = (minrgb + maxrgb) / 2;
255
256 if (minrgb == maxrgb) {
257 h = 0;
258 s = 0;
259 } else {
260 if (l < 0.5) {
261 s = (maxrgb - minrgb) / (maxrgb + minrgb);
262 } else {
263 s = (maxrgb - minrgb) / (2.f - maxrgb - minrgb);
264 }
265 if (r == maxrgb) {
266 h = (g-b) / (maxrgb-minrgb) + 0;
267 } else if (g == maxrgb) {
268 h = (b-r) / (maxrgb-minrgb) + 2;
269 } else { // if b == maxrgb
270 h = (r-g) / (maxrgb-minrgb) + 4;
271 }
272 }
273
274 if (h < 0) h += 6;
275 if (h >= 6) h -= 6;
276
277 *H = clip_colorval(int(h*256/6));
278 *S = clip_colorval(int(s*255));
279 *L = clip_colorval(int(l*255));
280 }
281
282 /// Formulas from http://en.wikipedia.org/wiki/HSV_color_space
rgb_to_hsv(int R,int G,int B,unsigned char * H,unsigned char * S,unsigned char * V)283 void rgb_to_hsv(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *V)
284 {
285 float r = R/255.f, g = G/255.f, b = B/255.f;
286 float h, s, v;
287
288 float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
289
290 v = maxrgb;
291
292 if (maxrgb < .001f) {
293 s = 1;
294 } else {
295 s = (maxrgb - minrgb) / maxrgb;
296 }
297
298 if (minrgb == maxrgb) {
299 h = 0;
300 } else if (maxrgb == r) {
301 h = (g-b) / (maxrgb-minrgb) + 0;
302 } else if (maxrgb == g) {
303 h = (b-r) / (maxrgb-minrgb) + 2;
304 } else { // if maxrgb == b
305 h = (r-g) / (maxrgb-minrgb) + 4;
306 }
307
308 if (h < 0) h += 6;
309 if (h >= 6) h -= 6;
310
311 *H = clip_colorval(int(h*256/6));
312 *S = clip_colorval(int(s*255));
313 *V = clip_colorval(int(v*255));
314 }
315
hsv_to_hsl(int iH,int iS,int iV,unsigned char * oH,unsigned char * oS,unsigned char * oL)316 void hsv_to_hsl(int iH, int iS, int iV, unsigned char *oH, unsigned char *oS, unsigned char *oL)
317 {
318 int p = iV * (255 - iS);
319 *oH = iH;
320 *oL = clip_colorval((p + iV*255)/255/2);
321 if (*oL == 0) {
322 *oS = iS; // oS is actually undefined, so any value should work ;)
323 } else if (*oL <= 128) {
324 *oS = clip_colorval((iV*255 - p) / (2 * *oL));
325 } else {
326 *oS = clip_colorval((iV*255 - p) / (511 - 2 * *oL));
327 }
328 }
329
hsl_to_hsv(int iH,int iS,int iL,unsigned char * oH,unsigned char * oS,unsigned char * oV)330 void hsl_to_hsv(int iH, int iS, int iL, unsigned char *oH, unsigned char *oS, unsigned char *oV)
331 {
332 *oH = iH;
333
334 if (iS == 0) {
335 *oS = 0;
336 *oV = iL;
337 return;
338 }
339
340 if (iL < 128) {
341 *oV = iL * (255 + iS) / 255;
342 *oS = 2 * 255 * iS / (255 + iS);
343 } else {
344 *oV = (iL*255 + iS*255 - iL*iS)/255;
345 *oS = 2 * 255 * iS * (255 - iL) / (iL*255 + iS*255 - iL*iS);
346 }
347 }
348