1 /* 2 Copyright (c) 2011-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * RGBA color space 31 * 32 * Copyright: Timur Gafarov 2011-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov 35 */ 36 module dlib.image.color; 37 38 import dlib.math.vector; 39 import dlib.math.utils; 40 41 /// RGBA color channel 42 enum Channel 43 { 44 R = 0, 45 G = 1, 46 B = 2, 47 A = 3 48 } 49 50 /// RGBA 16-bit integer color representation (a vector of ushorts) 51 alias Color4 = Vector!(ushort, 4); 52 53 /// ditto 54 alias ColorRGBA = Color4; 55 56 Color4 invert(Color4 c) 57 { 58 return Color4( 59 cast(ushort)(255 - c.r), 60 cast(ushort)(255 - c.g), 61 cast(ushort)(255 - c.b), 62 c.a); 63 } 64 65 /** 66 * RGBA floating-point color representation, 67 * encapsulates Vector4f 68 */ 69 struct Color4f 70 { 71 Vector4f vec; 72 alias vec this; 73 74 this(Color4 c, uint bitDepth = 8) 75 { 76 float maxv = (2 ^^ bitDepth) - 1; 77 vec.r = c.r / maxv; 78 vec.g = c.g / maxv; 79 vec.b = c.b / maxv; 80 vec.a = c.a / maxv; 81 } 82 83 this(Color4f c) 84 { 85 vec = c.vec; 86 } 87 88 this(Vector4f v) 89 { 90 vec = v; 91 } 92 93 this(Vector3f v) 94 { 95 vec = Vector4f(v.x, v.y, v.z, 1.0f); 96 } 97 98 this(float cr, float cg, float cb, float ca = 1.0f) 99 { 100 vec = Vector4f(cr, cg, cb, ca); 101 } 102 103 static Color4f zero() 104 { 105 return Color4f(0.0f, 0.0f, 0.0f, 0.0f); 106 } 107 108 Color4f opAssign(Color4f c) 109 { 110 vec = c.vec; 111 return this; 112 } 113 114 Color4f opBinary(string op)(float x) if (op == "+") 115 { 116 return Color4f(this.vec + x); 117 } 118 119 Color4f opBinary(string op)(float x) if (op == "-") 120 { 121 return Color4f(this.vec - x); 122 } 123 124 Color4f opBinary(string op)(float x) if (op == "*") 125 { 126 return Color4f(this.vec * x); 127 } 128 129 Color4f opBinary(string op)(float x) if (op == "/") 130 { 131 return Color4f(this.vec / x); 132 } 133 134 Color4f opBinary(string op)(Vector4f v) if (op == "+") 135 { 136 return Color4f(this.vec + v); 137 } 138 139 Color4f opBinary(string op)(Vector4f v) if (op == "-") 140 { 141 return Color4f(this.vec - v); 142 } 143 144 Color4f opBinary(string op)(Vector4f v) if (op == "*") 145 { 146 return Color4f(this.vec * v); 147 } 148 149 Color4f opBinary(string op)(Vector4f v) if (op == "/") 150 { 151 return Color4f(this.vec / v); 152 } 153 154 Color4 convert(int bitDepth) 155 { 156 float maxv = (2 ^^ bitDepth) - 1; 157 return Color4( 158 cast(ushort)(r.clamp(0.0f, 1.0f) * maxv), 159 cast(ushort)(g.clamp(0.0f, 1.0f) * maxv), 160 cast(ushort)(b.clamp(0.0f, 1.0f) * maxv), 161 cast(ushort)(a.clamp(0.0f, 1.0f) * maxv) 162 ); 163 } 164 165 int opCmp(ref const(Color4f) c) const 166 { 167 return cast(int)((luminance() - c.luminance()) * 100); 168 } 169 170 alias luminance = luminance709; 171 172 // ITU-R Rec. BT.709 173 float luminance709() const 174 { 175 return ( 176 vec.arrayof[0] * 0.2126f + 177 vec.arrayof[1] * 0.7152f + 178 vec.arrayof[2] * 0.0722f 179 ); 180 } 181 182 // ITU-R Rec. BT.601 183 float luminance601() const 184 { 185 return ( 186 vec.arrayof[0] * 0.3f + 187 vec.arrayof[1] * 0.59f + 188 vec.arrayof[2] * 0.11f 189 ); 190 } 191 192 @property Color4f inverse() 193 { 194 return Color4f( 195 1.0f - vec.r, 196 1.0f - vec.g, 197 1.0f - vec.b, 198 vec.a); 199 } 200 201 @property Color4f clamped(float minv, float maxv) 202 { 203 return Color4f( 204 vec.r.clamp(minv, maxv), 205 vec.g.clamp(minv, maxv), 206 vec.b.clamp(minv, maxv), 207 vec.a.clamp(minv, maxv) 208 ); 209 } 210 211 /// Converts color from gamma space to linear space 212 Color4f toLinear(float gamma = 2.2f) 213 { 214 float lr = r ^^ gamma; 215 float lg = g ^^ gamma; 216 float lb = b ^^ gamma; 217 return Color4f(lr, lg, lb, a); 218 } 219 220 /// Converts color from linear space to gamma space 221 Color4f toGamma(float gamma = 2.2f) 222 { 223 float invGamma = 1.0f / gamma; 224 float lr = r ^^ invGamma; 225 float lg = g ^^ invGamma; 226 float lb = b ^^ invGamma; 227 return Color4f(lr, lg, lb, a); 228 } 229 } 230 231 /// ditto 232 alias ColorRGBAf = Color4f; 233 234 /// Encode a normal vector to color 235 Color4f packNormal(Vector3f n) 236 { 237 return Color4f((n + 1.0f) * 0.5f); 238 } 239 240 /// 24-bit integer color unpacking 241 Color4f color3(int hex) 242 { 243 ubyte r = (hex >> 16) & 255; 244 ubyte g = (hex >> 8) & 255; 245 ubyte b = hex & 255; 246 return Color4f( 247 cast(float)r / 255.0f, 248 cast(float)g / 255.0f, 249 cast(float)b / 255.0f); 250 } 251 252 /// 32-bit integer color unpacking 253 Color4f color4(int hex) 254 { 255 ubyte r = (hex >> 24) & 255; 256 ubyte g = (hex >> 16) & 255; 257 ubyte b = (hex >> 8) & 255; 258 ubyte a = hex & 255; 259 return Color4f( 260 cast(float)r / 255.0f, 261 cast(float)g / 255.0f, 262 cast(float)b / 255.0f, 263 cast(float)a / 255.0f); 264 } 265 266 /// Blend two colors taking transparency into account 267 Color4f alphaOver(Color4f c1, Color4f c2) 268 { 269 Color4f c; 270 float a = c2.a + c1.a * (1.0f - c2.a); 271 272 if (a == 0.0f) 273 c = Color4f(0, 0, 0, 0); 274 else 275 { 276 c = (c2 * c2.a + c1 * c1.a * (1.0f - c2.a)) / a; 277 c.a = a; 278 } 279 280 return c; 281 }