1 /**
2 inmath.hsv
3
4 Authors: David Herberth, Inochi2D Project
5 License: MIT
6 */
7 module inmath.hsv;
8
9 private {
10 import std.conv : to;
11
12 import inmath.linalg : vec3, vec4;
13 import inmath.math : min, max, floor;
14
15 version(unittest) {
16 import inmath.math : almostEqual;
17 }
18 }
19
20 /// Converts a 3 dimensional color-vector from the RGB to the HSV colorspace.
21 /// The function assumes that each component is in the range [0, 1].
22 @safe pure nothrow vec3 rgb2hsv(vec3 inp) {
23 vec3 ret = vec3(0.0f, 0.0f, 0.0f);
24
25 float h_max = max(inp.r, inp.g, inp.b);
26 float h_min = min(inp.r, inp.g, inp.b);
27 float delta = h_max - h_min;
28
29
30 // h
31 if(delta == 0.0f) {
32 ret.x = 0.0f;
33 } else if(inp.r == h_max) {
34 ret.x = (inp.g - inp.b) / delta; // h
35 } else if(inp.g == h_max) {
36 ret.x = 2 + (inp.b - inp.r) / delta; // h
37 } else {
38 ret.x = 4 + (inp.r - inp.g) / delta; // h
39 }
40
41 ret.x = ret.x * 60;
42 if(ret.x < 0) {
43 ret.x = ret.x + 360;
44 }
45
46 // s
47 if(h_max == 0.0f) {
48 ret.y = 0.0f;
49 } else {
50 ret.y = delta / h_max;
51 }
52
53 // v
54 ret.z = h_max;
55
56 return ret;
57 }
58
59 /// Converts a 4 dimensional color-vector from the RGB to the HSV colorspace.
60 /// The alpha value is not touched. This function also assumes that each component is in the range [0, 1].
61 @safe pure nothrow vec4 rgb2hsv(vec4 inp) {
62 return vec4(rgb2hsv(vec3(inp.rgb)), inp.a);
63 }
64
65 unittest {
66 assert(rgb2hsv(vec3(0.0f, 0.0f, 0.0f)) == vec3(0.0f, 0.0f, 0.0f));
67 assert(rgb2hsv(vec3(1.0f, 1.0f, 1.0f)) == vec3(0.0f, 0.0f, 1.0f));
68
69 vec3 hsv = rgb2hsv(vec3(100.0f/255.0f, 100.0f/255.0f, 100.0f/255.0f));
70 assert(hsv.x == 0.0f && hsv.y == 0.0f && almostEqual(hsv.z, 0.392157, 0.000001));
71
72 assert(rgb2hsv(vec3(0.0f, 0.0f, 1.0f)) == vec3(240.0f, 1.0f, 1.0f));
73 }
74
75 /// Converts a 3 dimensional color-vector from the HSV to the RGB colorspace.
76 /// RGB colors will be in the range [0, 1].
77 /// This function is not marked es pure, since it depends on std.math.floor, which
78 /// is also not pure.
79 @safe nothrow vec3 hsv2rgb(vec3 inp) {
80 if(inp.y == 0.0f) { // s
81 return vec3(inp.zzz); // v
82 } else {
83 float var_h = inp.x * 6;
84 float var_i = to!float(floor(var_h));
85 float var_1 = inp.z * (1 - inp.y);
86 float var_2 = inp.z * (1 - inp.y * (var_h - var_i));
87 float var_3 = inp.z * (1 - inp.y * (1 - (var_h - var_i)));
88
89 if(var_i == 0.0f) return vec3(inp.z, var_3, var_1);
90 else if(var_i == 1.0f) return vec3(var_2, inp.z, var_1);
91 else if(var_i == 2.0f) return vec3(var_1, inp.z, var_3);
92 else if(var_i == 3.0f) return vec3(var_1, var_2, inp.z);
93 else if(var_i == 4.0f) return vec3(var_3, var_1, inp.z);
94 else return vec3(inp.z, var_1, var_2);
95 }
96 }
97
98 /// Converts a 4 dimensional color-vector from the HSV to the RGB colorspace.
99 /// The alpha value is not touched and the resulting RGB colors will be in the range [0, 1].
100 @safe nothrow vec4 hsv2rgb(vec4 inp) {
101 return vec4(hsv2rgb(vec3(inp.xyz)), inp.w);
102 }
103
104 unittest {
105 assert(hsv2rgb(vec3(0.0f, 0.0f, 0.0f)) == vec3(0.0f, 0.0f, 0.0f));
106 assert(hsv2rgb(vec3(0.0f, 0.0f, 1.0f)) == vec3(1.0f, 1.0f, 1.0f));
107
108 vec3 rgb = hsv2rgb(vec3(0.0f, 0.0f, 0.392157f));
109 assert(rgb == vec3(0.392157f, 0.392157f, 0.392157f));
110
111 assert(hsv2rgb(vec3(300.0f, 1.0f, 1.0f)) == vec3(1.0f, 0.0f, 1.0f));
112 }