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