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 }