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 }